Basic command line flags: e.g. -opt=0

This commit is contained in:
Ginger Bill
2017-06-24 22:58:50 +01:00
parent 18f885efab
commit 1d81b73df9
8 changed files with 314 additions and 90 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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<Operand> 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;
}
}

View File

@@ -12,6 +12,7 @@
#include <math.h>
gbAllocator heap_allocator(void) {
return gb_heap_allocator();
}

View File

@@ -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)

View File

@@ -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<String> 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<String> 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<BuildFlag> *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<String> args) {
Array<BuildFlag> build_flags = {};
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
Array<String> 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;

View File

@@ -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);

View File

@@ -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);
}