diff --git a/build.bat b/build.bat index c1143669f..9758c2515 100644 --- a/build.bat +++ b/build.bat @@ -44,7 +44,7 @@ del *.ilk > NUL 2> NUL cl %compiler_settings% "src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ - && odin run code/demo.odin + && odin run code/demo.odin -opt=0 rem && odin docs core/fmt.odin del *.obj > NUL 2> NUL diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 5b96cf07a..e34497fa1 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -12,10 +12,12 @@ struct BuildContext { i64 word_size; // Size of a pointer, must be >= 4 i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) + String opt_flags; String llc_flags; String link_flags; bool is_dll; bool generate_docs; + i32 optimization_level; }; @@ -23,6 +25,8 @@ gb_global BuildContext build_context = {0}; + + // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir @@ -272,22 +276,23 @@ void init_build_context(void) { #if defined(GB_SYSTEM_WINDOWS) bc->ODIN_OS = str_lit("windows"); - #if defined(GB_ARCH_64_BIT) - bc->ODIN_ARCH = str_lit("amd64"); - #else - bc->ODIN_ARCH = str_lit("x86"); - #endif - bc->ODIN_ENDIAN = str_lit("little"); #elif defined(GB_SYSTEM_OSX) bc->ODIN_OS = str_lit("osx"); - bc->ODIN_ARCH = str_lit("amd64"); - bc->ODIN_ENDIAN = str_lit("little"); #else bc->ODIN_OS = str_lit("linux"); - bc->ODIN_ARCH = str_lit("amd64"); - bc->ODIN_ENDIAN = str_lit("little"); #endif +#if defined(GB_ARCH_64_BIT) + bc->ODIN_ARCH = str_lit("amd64"); +#else + bc->ODIN_ARCH = str_lit("x86"); +#endif + + { + u16 x = 1; + bool big = !(*cast(u8 *)&x); + bc->ODIN_ENDIAN = big ? str_lit("big") : str_lit("little"); + } // NOTE(zangent): The linker flags to set the build architecture are different @@ -317,9 +322,11 @@ void init_build_context(void) { #define LINK_FLAG_X86 "-arch x86" #endif + if (bc->ODIN_ARCH == "amd64") { bc->word_size = 8; bc->max_align = 16; + bc->llc_flags = str_lit("-march=x86-64 "); bc->link_flags = str_lit(LINK_FLAG_X64 " "); } else if (bc->ODIN_ARCH == "x86") { @@ -327,8 +334,27 @@ void init_build_context(void) { bc->max_align = 8; bc->llc_flags = str_lit("-march=x86 "); bc->link_flags = str_lit(LINK_FLAG_X86 " "); + } else { + gb_printf_err("This current architecture is not supported"); + gb_exit(1); } + + isize opt_max = 1023; + char *opt_flags_string = gb_alloc_array(heap_allocator(), char, opt_max+1); + isize opt_len = 0; + bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3); + if (bc->optimization_level != 0) { + opt_len = gb_snprintf(opt_flags_string, opt_max, "-O%d", bc->optimization_level); + } else { + opt_len = gb_snprintf(opt_flags_string, opt_max, ""); + } + if (opt_len > 0) { + opt_len--; + } + bc->opt_flags = make_string(cast(u8 *)opt_flags_string, opt_len); + + #undef LINK_FLAG_X64 #undef LINK_FLAG_X86 } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 33e323644..af2f2ad4c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4840,6 +4840,7 @@ enum CallArgumentError { CallArgumentError_ParameterNotFound, CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, + CallArgumentError_GenericProcedureNotSupported, }; enum CallArgumentErrorMode { @@ -5010,6 +5011,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } else if (o.mode != Addressing_Type) { error(o.expr, "Expected a type for the argument"); } + + score += assign_score_function(1); continue; } if (variadic) { @@ -5150,7 +5153,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } else if (o->mode != Addressing_Type) { error(o->expr, "Expected a type for the argument"); } - score += 1; + score += assign_score_function(1); } else { i64 s = 0; if (!check_is_assignable_to_with_score(c, o, e->type, &s)) { @@ -5197,6 +5200,13 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } #endif + if (pt->is_generic) { + if (show_error) { + error(call, "Generic procedures do not yet support named arguments"); + } + err = CallArgumentError_GenericProcedureNotSupported; + } + if (score_) *score_ = score; return err; @@ -5210,6 +5220,8 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod Array operands = {}; defer (array_free(&operands)); + Type *result_type = t_invalid; + if (is_call_expr_field_value(ce)) { call_checker = check_named_call_arguments; @@ -5252,10 +5264,10 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod for (isize i = 0; i < overload_count; i++) { Entity *p = procs[i]; - Type *proc_type = base_type(p->type); - if (proc_type != NULL && is_type_proc(proc_type)) { + Type *pt = base_type(p->type); + if (pt != NULL && is_type_proc(pt)) { i64 score = 0; - CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_NoErrors, &score); + CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score); if (err == CallArgumentError_None) { valids[valid_count].index = i; valids[valid_count].score = score; @@ -5279,7 +5291,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod if (valid_count == 0) { error(operand->expr, "No overloads for `%.*s` that match with the given arguments", LIT(name)); - proc_type = t_invalid; + result_type = t_invalid; } else if (valid_count > 1) { error(operand->expr, "Ambiguous procedure call `%.*s`, could be:", LIT(name)); for (isize i = 0; i < valid_count; i++) { @@ -5289,7 +5301,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod gb_printf_err("\t%.*s of type %s at %.*s(%td:%td) with score %lld\n", LIT(name), pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score); gb_string_free(pt); } - proc_type = t_invalid; + result_type = t_invalid; } else { AstNode *expr = operand->expr; while (expr->kind == AstNode_SelectorExpr) { @@ -5301,14 +5313,20 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod proc_type = e->type; i64 score = 0; CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score); + + if (proc_type != NULL && is_type_proc(proc_type)) { + result_type = base_type(proc_type)->Proc.results; + } } } else { i64 score = 0; CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score); + if (proc_type != NULL && is_type_proc(proc_type)) { + result_type = base_type(proc_type)->Proc.results; + } } - - return proc_type; + return result_type; } @@ -5443,36 +5461,39 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { } } - proc_type = check_call_arguments(c, operand, proc_type, call); + Type *result_type = check_call_arguments(c, operand, proc_type, call); gb_zero_item(operand); + operand->expr = call; - Type *pt = base_type(proc_type); - if (pt == NULL || !is_type_proc(pt)) { + if (result_type == t_invalid) { operand->mode = Addressing_Invalid; operand->type = t_invalid; - operand->expr = call; return Expr_Stmt; } + Type *pt = base_type(proc_type); bool results_are_generic = false; - if (pt->Proc.results != NULL) { + if (is_type_proc(pt) && pt->Proc.results != NULL) { results_are_generic = is_type_generic(pt->Proc.results); } if (results_are_generic) { operand->mode = Addressing_NoValue; + } else if (result_type == NULL) { + operand->mode = Addressing_NoValue; } else { - switch (pt->Proc.result_count) { + GB_ASSERT(is_type_tuple(result_type)); + switch (result_type->Tuple.variable_count) { case 0: operand->mode = Addressing_NoValue; break; case 1: operand->mode = Addressing_Value; - operand->type = pt->Proc.results->Tuple.variables[0]->type; + operand->type = result_type->Tuple.variables[0]->type; break; default: operand->mode = Addressing_Value; - operand->type = pt->Proc.results; + operand->type = result_type; break; } } diff --git a/src/common.cpp b/src/common.cpp index 8ad21a2d6..40386375d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -12,6 +12,7 @@ #include + gbAllocator heap_allocator(void) { return gb_heap_allocator(); } diff --git a/src/gb/gb.h b/src/gb/gb.h index 0daec5e67..2ef21c7da 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -164,11 +164,11 @@ extern "C" { #endif -#ifndef GB_EDIAN_ORDER -#define GB_EDIAN_ORDER +#ifndef GB_ENDIAN_ORDER +#define GB_ENDIAN_ORDER // TODO(bill): Is the a good way or is it better to test for certain compilers and macros? - #define GB_IS_BIG_EDIAN (!*(u8*)&(u16){1}) - #define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN) + #define GB_IS_BIG_ENDIAN (!*(u8*)&(u16){1}) + #define GB_IS_LITTLE_ENDIAN (!GB_IS_BIG_ENDIAN) #endif #if defined(_WIN32) || defined(_WIN64) diff --git a/src/main.cpp b/src/main.cpp index d838ce908..e8ea79593 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,7 +33,9 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) { va_start(va, fmt); cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va); va_end(va); - // gb_printf("%.*s\n", cast(int)cmd_len, cmd_line); + if (!is_silent) { + // gb_printf("%.*s\n", cast(int)cmd_len, cmd_line); + } tmp = gb_temp_arena_memory_begin(&string_buffer_arena); @@ -118,13 +120,19 @@ Array setup_args(int argc, char **argv) { wchar_t *warg = wargv[i]; isize wlen = string16_len(warg); String16 wstr = make_string16(warg, wlen); - array_add(&args, string16_to_string(a, wstr)); + String arg = string16_to_string(a, wstr); + if (arg.len > 0) { + array_add(&args, arg); + } } #else array_init(&args, a, argc); for (i = 0; i < argc; i++) { - array_add(&args, make_string_c(argv[i])); + String arg = make_string_c(argv[i]); + if (arg.len > 0) { + array_add(&args, arg); + } } #endif return args; @@ -132,6 +140,8 @@ Array setup_args(int argc, char **argv) { + + void print_usage_line(i32 indent, char *fmt, ...) { while (indent --> 0) { gb_printf_err("\t"); @@ -155,6 +165,163 @@ void usage(String argv0) { print_usage_line(1, "version print version"); } + + +enum BuildFlagKind { + BuildFlag_Invalid, + + BuildFlag_OptimizationLevel, + + BuildFlag_COUNT, +}; + +enum BuildFlagParamKind { + BuildFlagParam_None, + + BuildFlagParam_Boolean, + BuildFlagParam_Integer, + BuildFlagParam_Float, + BuildFlagParam_String, + + BuildFlagParam_COUNT, +}; + +struct BuildFlag { + BuildFlagKind kind; + String name; + BuildFlagParamKind param_kind; +}; + + +void add_flag(Array *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) { + BuildFlag flag = {kind, name, param_kind}; + array_add(build_flags, flag); +} + +bool parse_build_flags(Array args) { + Array build_flags = {}; + array_init(&build_flags, heap_allocator(), BuildFlag_COUNT); + add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); + + Array flag_args = args; + flag_args.data += 3; + flag_args.count -= 3; + + bool set_flags[BuildFlag_COUNT] = {}; + + bool bad_flags = false; + for_array(i, flag_args) { + String flag = flag_args[i]; + if (flag[0] != '-') { + gb_printf_err("Invalid flag: %.*s\n", LIT(flag)); + } else { + String name = substring(flag, 1, flag.len); + isize end = 0; + for (; end < name.len; end++) { + if (name[end] == '=') { + break; + } + } + name.len = end; + String param = substring(flag, 2+end, flag.len); + + bool found = false; + for_array(build_flag_index, build_flags) { + BuildFlag bf = build_flags[build_flag_index]; + if (bf.name == name) { + found = true; + if (set_flags[bf.kind]) { + gb_printf_err("Previous flag set: `%.*s`\n", LIT(name)); + bad_flags = true; + } else { + ExactValue value = {}; + bool ok = false; + if (bf.param_kind == BuildFlagParam_None) { + if (param.len == 0) { + ok = true; + } else { + gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param)); + bad_flags = true; + } + } else { + if (param.len == 0) { + gb_printf_err("Flag missing for `%.*s`\n", LIT(name)); + bad_flags = true; + } else { + ok = true; + switch (bf.param_kind) { + default: ok = false; break; + case BuildFlagParam_Boolean: { + if (param == "t") { + value = exact_value_bool(true); + } else if (param == "T") { + value = exact_value_bool(true); + } else if (param == "true") { + value = exact_value_bool(true); + } else if (param == "TRUE") { + value = exact_value_bool(true); + } else if (param == "1") { + value = exact_value_bool(true); + } else if (param == "f") { + value = exact_value_bool(false); + } else if (param == "F") { + value = exact_value_bool(false); + } else if (param == "false") { + value = exact_value_bool(false); + } else if (param == "FALSE") { + value = exact_value_bool(false); + } else if (param == "0") { + value = exact_value_bool(false); + } else { + gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param)); + } + } break; + case BuildFlagParam_Integer: + value = exact_value_integer_from_string(param); + break; + case BuildFlagParam_Float: + value = exact_value_float_from_string(param); + break; + case BuildFlagParam_String: + value = exact_value_string(param); + break; + } + } + + } + if (ok) { + switch (bf.kind) { + case BuildFlag_OptimizationLevel: + if (value.kind == ExactValue_Integer) { + build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer); + } else { + gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param)); + bad_flags = true; + ok = false; + } + break; + } + } + + + set_flags[bf.kind] = ok; + } + break; + } + } + if (!found) { + gb_printf_err("Unknown flag: `%.*s`\n", LIT(name)); + bad_flags = true; + } + } + } + + return !bad_flags; +} + + + + int main(int arg_count, char **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -172,39 +339,31 @@ int main(int arg_count, char **arg_ptr) { #if 1 - init_build_context(); - - if (build_context.word_size == 4) { - print_usage_line(0, "%s 32-bit is not yet supported", args[0]); - return 1; - } - - init_universal_scope(); String init_filename = {}; bool run_output = false; if (args[1] == "run") { - if (args.count != 3) { + if (args.count < 3) { usage(args[0]); return 1; } init_filename = args[2]; run_output = true; } else if (args[1] == "build_dll") { - if (args.count != 3) { + if (args.count < 3) { usage(args[0]); return 1; } init_filename = args[2]; build_context.is_dll = true; } else if (args[1] == "build") { - if (args.count != 3) { + if (args.count < 3) { usage(args[0]); return 1; } init_filename = args[2]; } else if (args[1] == "docs") { - if (args.count != 3) { + if (args.count < 3) { usage(args[0]); return 1; } @@ -223,6 +382,18 @@ int main(int arg_count, char **arg_ptr) { return 1; } + if (!parse_build_flags(args)) { + return 1; + } + + + init_build_context(); + if (build_context.word_size == 4) { + print_usage_line(0, "%s 32-bit is not yet supported", args[0]); + return 1; + } + init_universal_scope(); + // TODO(bill): prevent compiling without a linker timings_start_section(&timings, str_lit("parse files")); @@ -291,49 +462,44 @@ int main(int arg_count, char **arg_ptr) { String output_base = ir_gen.output_base; int base_name_len = output_base.len; - i32 optimization_level = 0; - optimization_level = gb_clamp(optimization_level, 0, 3); + build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3); i32 exit_code = 0; #if defined(GB_SYSTEM_WINDOWS) - // For more passes arguments: http://llvm.org/docs/Passes.html - exit_code = system_exec_command_line_app("llvm-opt", false, - "\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc " - "-mem2reg " - "-memcpyopt " - "-die " - // "-dse " - // "-dce " - // "-S " - "", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base)); - if (exit_code != 0) { - return exit_code; - } + // For more passes arguments: http://llvm.org/docs/Passes.html + exit_code = system_exec_command_line_app("llvm-opt", false, + "\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc %.*s " + "-mem2reg " + "-memcpyopt " + "-die " + "", + LIT(build_context.ODIN_ROOT), + LIT(output_base), LIT(output_base), + LIT(build_context.opt_flags)); + if (exit_code != 0) { + return exit_code; + } #else - // NOTE(zangent): This is separate because it seems that LLVM tools are packaged - // with the Windows version, while they will be system-provided on MacOS and GNU/Linux - exit_code = system_exec_command_line_app("llvm-opt", false, - "opt \"%.*s\".ll -o \"%.*s\".bc " - "-mem2reg " - "-memcpyopt " - "-die " - #if defined(GB_SYSTEM_OSX) - // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. - // NOTE: If you change this (although this minimum is as low as you can go with Odin working) - // make sure to also change the `macosx_version_min` param passed to `llc` - "-mtriple=x86_64-apple-macosx10.8 " - #endif - // "-dse " - // "-dce " - // "-S " - "", - LIT(output_base), LIT(output_base)); - if (exit_code != 0) { - return exit_code; - } + // NOTE(zangent): This is separate because it seems that LLVM tools are packaged + // with the Windows version, while they will be system-provided on MacOS and GNU/Linux + exit_code = system_exec_command_line_app("llvm-opt", false, + "opt \"%.*s\".ll -o \"%.*s\".bc %.*s " + "-mem2reg " + "-memcpyopt " + "-die " + #if defined(GB_SYSTEM_OSX) + // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. + // NOTE: If you change this (although this minimum is as low as you can go with Odin working) + // make sure to also change the `macosx_version_min` param passed to `llc` + "-mtriple=x86_64-apple-macosx10.8 " + #endif + "", + LIT(output_base), LIT(output_base), + LIT(build_context.opt_flags)); + if (exit_code != 0) { + return exit_code; + } #endif #if defined(GB_SYSTEM_WINDOWS) @@ -346,7 +512,7 @@ int main(int arg_count, char **arg_ptr) { "", LIT(build_context.ODIN_ROOT), LIT(output_base), - optimization_level, + build_context.optimization_level, LIT(build_context.llc_flags)); if (exit_code != 0) { return exit_code; @@ -408,7 +574,7 @@ int main(int arg_count, char **arg_ptr) { // "-debug-pass=Arguments " "", LIT(output_base), - optimization_level, + build_context.optimization_level, LIT(build_context.llc_flags)); if (exit_code != 0) { return exit_code; diff --git a/src/parser.cpp b/src/parser.cpp index 37f56d350..bf1b6163e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -475,7 +475,7 @@ String const ast_node_strings[] = { struct AstNode { AstNodeKind kind; - u32 stmt_state_flags; + u32 stmt_state_flags; union { #define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; AST_NODE_KINDS @@ -629,9 +629,12 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { gb_memmove(n, node, gb_size_of(AstNode)); switch (n->kind) { - case AstNode_Ident: break; - case AstNode_Implicit: break; - case AstNode_BasicLit: break; + default: GB_PANIC("Unhandled AstNode %.*s", LIT(ast_node_strings[n->kind])); break; + + case AstNode_Invalid: break; + case AstNode_Ident: break; + case AstNode_Implicit: break; + case AstNode_BasicLit: break; case AstNode_BasicDirective: break; case AstNode_Ellipsis: n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr); diff --git a/src/string.cpp b/src/string.cpp index 1795b411d..916e095e2 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -80,6 +80,13 @@ gb_inline String make_string_c(char *text) { return make_string(cast(u8 *)cast(void *)text, gb_strlen(text)); } +String substring(String s, isize lo, isize hi) { + isize max = s.len; + GB_ASSERT(lo <= hi && hi <= max); + + return make_string(s.text+lo, hi-lo); +} +