aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2017-04-09 22:01:22 +0100
committerGitHub <noreply@github.com>2017-04-09 22:01:22 +0100
commit0683d2b4f4859a229b6a31790b5a744da577fcd6 (patch)
tree5759dd6755ef660d0f22332870e66898f3debb99 /src
parentd7fdd3d7b8d310ee31a6f170964ce6847458a4c9 (diff)
parent6b33b254e92993cd0d468351d09e9386de349c41 (diff)
Merge pull request #33 from zangent/master
Base of *nix port
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.c99
-rw-r--r--src/check_decl.c28
-rw-r--r--src/checker.c16
-rw-r--r--src/gb/gb.h20
-rw-r--r--src/ir.c6
-rw-r--r--src/ir_print.c135
-rw-r--r--src/main.c137
-rw-r--r--src/types.c1
8 files changed, 415 insertions, 27 deletions
diff --git a/src/build_settings.c b/src/build_settings.c
index 0190bff55..6d7dbe939 100644
--- a/src/build_settings.c
+++ b/src/build_settings.c
@@ -135,7 +135,62 @@ String odin_root_dir(void) {
return path;
}
#else
-#error Implement system
+
+// NOTE: Linux / Unix is unfinished and not tested very well.
+#include <sys/stat.h>
+
+String odin_root_dir(void) {
+ String path = global_module_path;
+ Array(char) path_buf;
+ isize len, i;
+ gbTempArenaMemory tmp;
+ wchar_t *text;
+
+ if (global_module_path_set) {
+ return global_module_path;
+ }
+
+ array_init_count(&path_buf, heap_allocator(), 300);
+
+ len = 0;
+ for (;;) {
+ // This is not a 100% reliable system, but for the purposes
+ // of this compiler, it should be _good enough_.
+ // That said, there's no solid 100% method on Linux to get the program's
+ // path without checking this link. Sorry.
+ len = readlink("/proc/self/exe", &path_buf.e[0], path_buf.count);
+ if(len == 0) {
+ return make_string(NULL, 0);
+ }
+ if (len < path_buf.count) {
+ break;
+ }
+ array_resize(&path_buf, 2*path_buf.count + 300);
+ }
+
+
+ tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
+ text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
+ gb_memmove(text, &path_buf.e[0], len);
+
+ path = make_string(text, len);
+ for (i = path.len-1; i >= 0; i--) {
+ u8 c = path.text[i];
+ if (c == '/' || c == '\\') {
+ break;
+ }
+ path.len--;
+ }
+
+ global_module_path = path;
+ global_module_path_set = true;
+
+ gb_temp_arena_memory_end(tmp);
+
+ array_free(&path_buf);
+
+ return path;
+}
#endif
@@ -221,18 +276,54 @@ void init_build_context(void) {
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#else
-#error Implement system
+ bc->ODIN_OS = str_lit("linux");
+ bc->ODIN_ARCH = str_lit("amd64");
+ bc->ODIN_ENDIAN = str_lit("little");
#endif
+
+
+ // NOTE(zangent): The linker flags to set the build architecture are different
+ // across OSs. It doesn't make sense to allocate extra data on the heap
+ // here, so I just #defined the linker flags to keep things concise.
+ #if defined(GB_SYSTEM_WINDOWS)
+
+ #define LINK_FLAG_X64 "/machine:x64"
+ #define LINK_FLAG_X86 "/machine:x86"
+
+ #elif defined(GB_SYSTEM_OSX)
+
+ // NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
+ // an architecture option. All compilation done on MacOS must be x64.
+ GB_ASSERT(str_eq(bc->ODIN_ARCH, str_lit("amd64")));
+
+ #define LINK_FLAG_X64 ""
+ #define LINK_FLAG_X86 ""
+ #else
+ // Linux, but also BSDs and the like.
+ // NOTE(zangent): When clang is swapped out with ld as the linker,
+ // the commented flags here should be used. Until then, we'll have
+ // to use alternative build flags made for clang.
+ /*
+ #define LINK_FLAG_X64 "-m elf_x86_64"
+ #define LINK_FLAG_X86 "-m elf_i386"
+ */
+ #define LINK_FLAG_X64 "-arch x86-64"
+ #define LINK_FLAG_X86 "-arch x86"
+ #endif
+
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
bc->word_size = 8;
bc->max_align = 16;
bc->llc_flags = str_lit("-march=x86-64 ");
- bc->link_flags = str_lit("/machine:x64 ");
+ bc->link_flags = str_lit(LINK_FLAG_X64 " ");
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
bc->word_size = 4;
bc->max_align = 8;
bc->llc_flags = str_lit("-march=x86 ");
- bc->link_flags = str_lit("/machine:x86 ");
+ bc->link_flags = str_lit(LINK_FLAG_X86 " ");
}
+
+ #undef LINK_FLAG_X64
+ #undef LINK_FLAG_X86
}
diff --git a/src/check_decl.c b/src/check_decl.c
index 0a6c0e5b6..4522fb85e 100644
--- a/src/check_decl.c
+++ b/src/check_decl.c
@@ -5,8 +5,8 @@ void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
// NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
- operand->type == t_invalid ||
- e->type == t_invalid) {
+ operand->type == t_invalid ||
+ e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr);
@@ -14,9 +14,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
// TODO(bill): is this a good enough error message?
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error_node(operand->expr,
- "Cannot assign builtin procedure `%s` in %.*s",
- expr_str,
- LIT(context_name));
+ "Cannot assign builtin procedure `%s` in %.*s",
+ expr_str,
+ LIT(context_name));
operand->mode = Addressing_Invalid;
@@ -86,8 +86,8 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
- operand->type == t_invalid ||
- e->type == t_invalid) {
+ operand->type == t_invalid ||
+ e->type == t_invalid) {
if (e->type == NULL) {
e->type = t_invalid;
}
@@ -182,7 +182,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
check_init_constant(c, e, &operand);
if (operand.mode == Addressing_Invalid ||
- base_type(operand.type) == t_invalid) {
+ base_type(operand.type) == t_invalid) {
error(e->token, "Invalid declaration type");
}
}
@@ -324,9 +324,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) {
error_node(d->proc_lit,
- "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
- "\tat %.*s(%td:%td)",
- LIT(name), LIT(pos.file), pos.line, pos.column);
+ "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
+ "\tat %.*s(%td:%td)",
+ LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_entity_set(fp, key, e);
@@ -349,9 +349,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
TokenPos pos = f->token.pos;
// TODO(bill): Better error message?
error_node(d->proc_lit,
- "Non unique linking name for procedure `%.*s`\n"
- "\tother at %.*s(%td:%td)",
- LIT(name), LIT(pos.file), pos.line, pos.column);
+ "Non unique linking name for procedure `%.*s`\n"
+ "\tother at %.*s(%td:%td)",
+ LIT(name), LIT(pos.file), pos.line, pos.column);
} else {
map_entity_set(fp, key, e);
}
diff --git a/src/checker.c b/src/checker.c
index 99d2b790f..22b48ca00 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -1578,6 +1578,19 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
continue;
}
+ if (fl->cond != NULL) {
+ Operand operand = {Addressing_Invalid};
+ check_expr(c, &operand, fl->cond);
+ if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
+ error_node(fl->cond, "Non-constant boolean `when` condition");
+ continue;
+ }
+ if (operand.value.kind == ExactValue_Bool &&
+ !operand.value.value_bool) {
+ continue;
+ }
+ }
+
DelayedDecl di = {c->context.scope, decl};
array_add(&c->delayed_foreign_libraries, di);
case_end;
@@ -2004,6 +2017,3 @@ void check_parsed_files(Checker *c) {
map_scope_destroy(&file_scopes);
}
-
-
-
diff --git a/src/gb/gb.h b/src/gb/gb.h
index ed20df1a4..892926a40 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -276,7 +276,9 @@ extern "C" {
// TODO(bill): How many of these headers do I really need?
// #include <stdarg.h>
-// #include <stddef.h>
+#if !defined(GB_SYSTEM_WINDOWS)
+ #include <stddef.h>
+#endif
@@ -3644,6 +3646,13 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
#if defined(_MSC_VER)
// TODO(bill): Is this good enough?
__movsb(cast(u8 *)dest, cast(u8 *)source, n);
+#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
+ // NOTE(zangent): I assume there's a reason this isn't being used elsewhere,
+ // but casting pointers as arguments to an __asm__ call is considered an
+ // error on MacOS and (I think) Linux
+ // TODO(zangent): Figure out how to refactor the asm code so it works on MacOS,
+ // since this is probably not the way the author intended this to work.
+ memcpy(dest, source, n);
#elif defined(GB_CPU_X86)
__asm__ __volatile__("rep movsb" : "+D"(cast(u8 *)dest), "+S"(cast(u8 *)source), "+c"(n) : : "memory");
#else
@@ -4695,7 +4704,7 @@ gb_inline u32 gb_thread_current_id(void) {
#elif defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
- __asm__("mov %%gs:0x10,%0" : "=r"(thread_id));
+ __asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -5019,7 +5028,10 @@ void gb_affinity_init(gbAffinity *a) {
// Parsing /proc/cpuinfo to get the number of threads per core.
// NOTE(zangent): This calls the CPU's threads "cores", although the wording
// is kind of weird. This should be right, though.
- if (fopen("/proc/cpuinfo", "r") != NULL) {
+
+ FILE* cpu_info = fopen("/proc/cpuinfo", "r");
+
+ if (cpu_info != NULL) {
for (;;) {
// The 'temporary char'. Everything goes into this char,
// so that we can check against EOF at the end of this loop.
@@ -5050,6 +5062,8 @@ void gb_affinity_init(gbAffinity *a) {
}
#undef AF__CHECK
}
+
+ fclose(cpu_info);
}
if (threads == 0) {
diff --git a/src/ir.c b/src/ir.c
index bd43fb33c..c9e42af19 100644
--- a/src/ir.c
+++ b/src/ir.c
@@ -6792,6 +6792,12 @@ void ir_gen_tree(irGen *s) {
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
// Handle later
} else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) {
+ #ifdef GB_SYSTEM_OSX
+ } else if (str_eq(name, str_lit("args")) && str_eq(e->token.pos.file, get_fullpath_core(heap_allocator(), str_lit("os_x.odin")))) {
+ #endif
+ #ifdef GB_SYSTEM_LINUX
+ } else if (str_eq(name, str_lit("args")) && str_eq(e->token.pos.file, get_fullpath_core(heap_allocator(), str_lit("os_linux.odin")))) {
+ #endif
} else {
name = ir_mangle_name(s, e->token.pos.file, e);
}
diff --git a/src/ir_print.c b/src/ir_print.c
index 9663f7042..22a575437 100644
--- a/src/ir_print.c
+++ b/src/ir_print.c
@@ -1385,6 +1385,134 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
+
+#ifndef GB_SYSTEM_WINDOWS
+ bool is_main_proc = proc->parent == NULL && str_eq(proc->name, str_lit("main"));
+
+ AstFile fake_file;
+ gb_arena_init_from_allocator(&fake_file.arena, heap_allocator(), gb_size_of(AstNode) * 4);
+
+ bool uses_args = false;
+ if(is_main_proc)
+ for(int i=0;i<proc->module->min_dep_map.entries.count;i++) {
+ Entity *value = proc->module->min_dep_map.entries.e[i].value;
+ if(value == NULL) continue;
+ if(str_eq(str_lit("args"), value->token.string)) {
+ uses_args = true;
+ break;
+ }
+ }
+
+ // TODO(zangent): THIS IS AN UGLY HACK
+ // I _SERIOUSLY_ need to change this system, because this is just disgraceful.
+
+ if(uses_args) {
+
+
+ ir_fprintf(f, "%s", "; Hack to give Linux/OSX launch arguments\n"
+"define i32 @main(i32 %argc, i8** %argv) {\n"
+"decls-0:\n"
+" %0 = alloca i32, align 4\n"
+" %1 = alloca i8**, align 8\n"
+" %2 = alloca i32, align 4\n"
+" %3 = alloca i8*, align 8\n"
+" %4 = alloca %..string, align 8\n"
+" store i32 zeroinitializer, i32* %0\n"
+" store i32 %argc, i32* %0\n"
+" store i8** zeroinitializer, i8*** %1\n"
+" store i8** %argv, i8*** %1\n"
+" call void @.__$startup_runtime()\n"
+" ; reserve\n"
+" ; SelectorExpr\n"
+" %5 = load i32, i32* %0, align 4\n"
+" %6 = sext i32 %5 to i64\n"
+" %7 = bitcast {%..string*, i64, i64,%Allocator}* @.args to %..rawptr\n"
+" %8 = call i1 @.__dynamic_array_reserve(%..rawptr %7, i64 16, i64 8, i64 %6)\n"
+" ; AssignStmt\n"
+" ; SelectorExpr\n"
+" ; SelectorExpr\n"
+" %9 = getelementptr inbounds {%..string*, i64, i64,%Allocator}, {%..string*, i64, i64,%Allocator}* @.args, i64 0, i32 1\n"
+" %10 = load i32, i32* %0, align 4\n"
+" ; cast - cast\n"
+" %11 = sext i32 %10 to i64\n"
+" store i64 %11, i64* %9\n"
+" ; i\n"
+" store i32 zeroinitializer, i32* %2\n"
+" store i32 0, i32* %2\n"
+" ; ForStmt\n"
+" br label %for.loop-1\n"
+"\n"
+"for.loop-1:\n"
+" %12 = load i32, i32* %2, align 4\n"
+" %13 = load i32, i32* %0, align 4\n"
+" %14 = icmp slt i32 %12, %13\n"
+" br i1 %14, label %for.body-2, label %for.done-6\n"
+"\n"
+"for.body-2:\n"
+" ; cstr\n"
+" store i8* zeroinitializer, i8** %3\n"
+" %15 = load i8**, i8*** %1, align 8\n"
+" %16 = load i32, i32* %2, align 4\n"
+" %17 = sext i32 %16 to i64\n"
+" %18 = getelementptr inbounds i8*, i8** %15, i64 %17\n"
+" %19 = getelementptr inbounds i8*, i8** %18, i64 0\n"
+" %20 = load i8*, i8** %19, align 8\n"
+" store i8* %20, i8** %3\n"
+" ; str\n"
+" store %..string zeroinitializer, %..string* %4\n"
+" ; AssignStmt\n"
+" ; SelectorExpr\n"
+" %21 = getelementptr inbounds %..string, %..string* %4, i64 0, i32 0\n"
+" %22 = load i8*, i8** %3, align 8\n"
+" store i8* %22, i8** %21\n"
+" ; ForStmt\n"
+" br label %for.loop-3\n"
+"\n"
+"for.loop-3:\n"
+" %23 = load i8*, i8** %3, align 8\n"
+" ; SelectorExpr\n"
+" %24 = getelementptr inbounds %..string, %..string* %4, i64 0, i32 1\n"
+" %25 = load i64, i64* %24, align 8\n"
+" %26 = getelementptr inbounds i8, i8* %23, i64 %25\n"
+" %27 = getelementptr inbounds i8, i8* %26, i64 0\n"
+" %28 = load i8, i8* %27, align 1\n"
+" %29 = icmp ne i8 %28, 0\n"
+" br i1 %29, label %for.body-4, label %for.done-5\n"
+"\n"
+"for.body-4:\n"
+" ; SelectorExpr\n"
+" %30 = getelementptr inbounds %..string, %..string* %4, i64 0, i32 1\n"
+" %31 = load i64, i64* %30, align 8\n"
+" %32 = add i64 %31, 1\n"
+" store i64 %32, i64* %30\n"
+" br label %for.loop-3\n"
+"\n"
+"for.done-5:\n"
+" ; AssignStmt\n"
+" ; IndexExpr\n"
+" ; SelectorExpr\n"
+" %33 = load {%..string*, i64, i64,%Allocator}, {%..string*, i64, i64,%Allocator}* @.args, align 8\n"
+" %34 = extractvalue {%..string*, i64, i64,%Allocator} %33, 0\n"
+" %35 = extractvalue {%..string*, i64, i64,%Allocator} %33, 1\n"
+" %36 = load i32, i32* %2, align 4\n"
+" %37 = sext i32 %36 to i64\n"
+" %38 = getelementptr inbounds %..string, %..string* %34, i64 %37\n"
+" %39 = load %..string, %..string* %4, align 8\n"
+" store %..string %39, %..string* %38\n"
+" ; AssignStmt\n"
+" %40 = load i32, i32* %2, align 4\n"
+" %41 = add i32 %40, 1\n"
+" store i32 %41, i32* %2\n"
+" br label %for.loop-1\n"
+"\n"
+"for.done-6:\n"
+" call void @.nix_argpatch_main()\n"
+" ret i32 0\n"
+"}\n"
+);
+ }
+#endif
+
if (proc->body == NULL) {
ir_fprintf(f, "declare ");
// if (proc->tags & ProcTag_dll_import) {
@@ -1412,7 +1540,14 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
}
ir_fprintf(f, " ");
+
+#ifndef GB_SYSTEM_WINDOWS
+ if(uses_args)
+ ir_fprintf(f, "@.nix_argpatch_main");
+ else
+#endif
ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc));
+
ir_fprintf(f, "(");
if (proc_type->param_count > 0) {
diff --git a/src/main.c b/src/main.c
index 118d0e50c..ccc2b4c3b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,6 +16,11 @@ extern "C" {
#include "ir_print.c"
// #include "vm.c"
+#if defined(GB_SYSTEM_UNIX)
+// Required for intrinsics on GCC
+#include <xmmintrin.h>
+#endif
+
#if defined(GB_SYSTEM_WINDOWS)
// NOTE(bill): `name` is used in debugging and profiling modes
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
@@ -102,6 +107,8 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
// }
// exit_code = status;
+
+ return exit_code;
}
#endif
@@ -250,9 +257,11 @@ int main(int argc, char **argv) {
optimization_level = gb_clamp(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\" -o \"%.*s.bc\" "
+ "\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
@@ -265,6 +274,29 @@ int main(int argc, char **argv) {
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\" -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 "
+ "",
+ output_name, LIT(output));
+ if (exit_code != 0) {
+ return exit_code;
+ }
+ #endif
#if defined(GB_SYSTEM_WINDOWS)
timings_start_section(&timings, str_lit("llvm-llc"));
@@ -326,7 +358,108 @@ int main(int argc, char **argv) {
}
#else
- #error Implement build stuff for this platform
+
+ // NOTE(zangent): Linux / Unix is unfinished and not tested very well.
+
+
+ timings_start_section(&timings, str_lit("llvm-llc"));
+ // For more arguments: http://llvm.org/docs/CommandGuide/llc.html
+ exit_code = system_exec_command_line_app("llc", false,
+ "llc \"%.*s.bc\" -filetype=obj -O%d "
+ "%.*s "
+ // "-debug-pass=Arguments "
+ "",
+ LIT(output),
+ optimization_level,
+ LIT(build_context.llc_flags));
+ if (exit_code != 0) {
+ return exit_code;
+ }
+
+ timings_start_section(&timings, str_lit("ld-link"));
+
+ gbString lib_str = gb_string_make(heap_allocator(), "");
+ // defer (gb_string_free(lib_str));
+ char lib_str_buf[1024] = {0};
+ for_array(i, ir_gen.module.foreign_library_paths) {
+ String lib = ir_gen.module.foreign_library_paths.e[i];
+
+ // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
+ // This allows you to specify '-f' in a #foreign_system_library,
+ // without having to implement any new syntax specifically for MacOS.
+ #if defined(GB_SYSTEM_OSX)
+ isize len;
+ if(lib.len > 2 && lib.text[0] == '-' && lib.text[1] == 'f') {
+ len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+ " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
+ } else {
+ len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+ " -l%.*s ", LIT(lib));
+ }
+ #else
+ isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+ " -l%.*s ", LIT(lib));
+ #endif
+ lib_str = gb_string_appendc(lib_str, lib_str_buf);
+ }
+
+ // Unlike the Win32 linker code, the output_ext includes the dot, because
+ // typically executable files on *NIX systems don't have extensions.
+ char *output_ext = "";
+ char *link_settings = "";
+ char *linker;
+ if (build_context.is_dll) {
+ // Shared libraries are .dylib on MacOS and .so on Linux.
+ // TODO(zangent): Is that statement entirely truthful?
+ #if defined(GB_SYSTEM_OSX)
+ output_ext = ".dylib";
+ #else
+ output_ext = ".so";
+ #endif
+
+ link_settings = "-shared";
+ } else {
+ // TODO: Do I need anything here?
+ link_settings = "";
+ }
+
+ #if defined(GB_SYSTEM_OSX)
+ linker = "ld";
+ #else
+ // TODO(zangent): Figure out how to make ld work on Linux.
+ // It probably has to do with including the entire CRT, but
+ // that's quite a complicated issue to solve while remaining distro-agnostic.
+ // Clang can figure out linker flags for us, and that's good enough _for now_.
+ linker = "clang";
+ #endif
+
+ exit_code = system_exec_command_line_app("ld-link", true,
+ "%s \"%.*s\".o -o \"%.*s%s\" %s "
+ "-lc "
+ " %.*s "
+ " %s "
+ #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 `mtriple` param passed to `opt`
+ " -macosx_version_min 10.8.0 "
+ // This points the linker to where the entry point is
+ " -e _main "
+ #endif
+ , linker, LIT(output), LIT(output), output_ext,
+ lib_str, LIT(build_context.link_flags),
+ link_settings
+ );
+ if (exit_code != 0) {
+ return exit_code;
+ }
+
+ // timings_print_all(&timings);
+
+ if (run_output) {
+ system_exec_command_line_app("odin run", false, "%.*s", cast(int)base_name_len, output_name);
+ }
+
#endif
#endif
#endif
diff --git a/src/types.c b/src/types.c
index 22bcbae83..773ef5efe 100644
--- a/src/types.c
+++ b/src/types.c
@@ -1091,7 +1091,6 @@ typedef enum ProcTypeOverloadKind {
} ProcTypeOverloadKind;
-
ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
if (x == NULL && y == NULL) return ProcOverload_NotProcedure;
if (x == NULL && y != NULL) return ProcOverload_NotProcedure;