From 5cfa4ba5807b63bcab537ca2c604b6015ced4459 Mon Sep 17 00:00:00 2001 From: Zac Pierson Date: Mon, 6 Feb 2017 12:26:41 -0600 Subject: [PATCH] Added Linux functions throughout the code, but it segfaults. --- .gitignore | 5 ++ build.sh | 6 +++ src/build.c | 82 +++++++++++++++++++++++++++-- src/gb/gb.h | 147 ++++++++++++++++++++++++++++++++++++++-------------- src/main.c | 75 ++++++++++++++++++++++++++- 5 files changed, 270 insertions(+), 45 deletions(-) create mode 100755 build.sh diff --git a/.gitignore b/.gitignore index ff0568ca6..6eabce127 100644 --- a/.gitignore +++ b/.gitignore @@ -251,7 +251,12 @@ paket-files/ # Project Specific + +# - Windows *.sln !misc/llvm-bim/lli.exe !misc/llvm-bim/opt.exe builds + +# - Linux/MacOS +odin diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..059b593c8 --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +WARNINGS_DISABLE="-Wno-attributes -Wno-implicit-function-declaration -Wno-incompatible-pointer-types" +LIBRARIES="-pthread -ldl -lm" + +gcc src/main.c ${WARNINGS_DISABLE} ${LIBRARIES} -o odin diff --git a/src/build.c b/src/build.c index 3afc05047..6a121708e 100644 --- a/src/build.c +++ b/src/build.c @@ -126,7 +126,64 @@ String odin_root_dir(void) { return path; } #else -#error Implement system + +// NOTE: Linux / Unix is unfinished and not tested very well. +#include + +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); + } + printf("build.c:155 | Link value: \"%s\"\n", &path_buf.e[0]); + exit(1); + 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 @@ -250,18 +307,35 @@ void init_build_context(BuildContext *bc) { 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 + + + // 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 linker_flag_x64 "/machine:x64" + #define linker_flag_x86 "/machine:x86" + #elif defined(GB_SYSTEM_OSX) + #error Run "ld -V" to find out what to build programs as. It may be the same as Linux...? + #else + #define linker_flag_x64 "-m elf_x86_64" + #define linker_flag_x86 "-m elf_i386" + #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(linker_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(linker_flag_x86 " "); } } diff --git a/src/gb/gb.h b/src/gb/gb.h index 8edd4b92f..5ec844daa 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -1,4 +1,4 @@ -/* gb.h - v0.27 - Ginger Bill's C Helper Library - public domain +/* gb.h - v0.26d - Ginger Bill's C Helper Library - public domain - no warranty implied; use at your own risk This is a single header file with a bunch of useful stuff @@ -276,8 +276,6 @@ extern "C" { #include #include - - #if defined(GB_SYSTEM_WINDOWS) #if !defined(GB_NO_WINDOWS_H) #define NOMINMAX 1 @@ -298,9 +296,6 @@ extern "C" { #include #include #include - #ifndef _IOSC11_SOURCE - #define _IOSC11_SOURCE - #endif #include // NOTE(bill): malloc on linux #include #if !defined(GB_SYSTEM_OSX) @@ -314,18 +309,18 @@ extern "C" { #endif #if defined(GB_SYSTEM_OSX) - #include - #include - #include - #include - #include - #include - #include - #include +#include +#include +#include +#include +#include +#include +#include +#include #endif #if defined(GB_SYSTEM_UNIX) - #include +#include #endif @@ -417,20 +412,21 @@ typedef i32 Rune; // NOTE(bill): Unicode codepoint #define GB_RUNE_EOF cast(Rune)(-1) +// NOTE(bill): I think C99 and C++ `bool` is stupid for numerous reasons but there are too many +// to write in this small comment. typedef i8 b8; typedef i16 b16; typedef i32 b32; // NOTE(bill): Prefer this!!! // NOTE(bill): Get true and false #if !defined(__cplusplus) - #if (defined(_MSC_VER) && _MSC_VER <= 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__)) + #if (defined(_MSC_VER) && _MSC_VER <= 1800) || !defined(__STDC_VERSION__) #ifndef true #define true (0 == 0) #endif #ifndef false #define false (0 != 0) #endif - typedef b8 bool; #else #include #endif @@ -619,7 +615,7 @@ extern "C++" { // // NOTE: C++11 (and above) only! // -#if !defined(GB_NO_DEFER) && defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1400) || (__cplusplus >= 201103L)) +#if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1400) || (__cplusplus >= 201103L)) extern "C++" { // NOTE(bill): Stupid fucking templates template struct gbRemoveReference { typedef T Type; }; @@ -1008,7 +1004,12 @@ typedef struct gbAffinity { } gbAffinity; #elif defined(GB_SYSTEM_LINUX) -#error TODO(bill): Implement gbAffinity for linux +typedef struct gbAffinity { + b32 is_accurate; + isize core_count; + isize thread_count; + isize threads_per_core; +} gbAffinity; #else #error TODO(bill): Unknown system #endif @@ -4117,7 +4118,7 @@ gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) { gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) { #if defined(GB_ARCH_64_BIT) - return _InterlockedOr64(cast(i64 volatile *)a, operand); + return _InterlockedAnd64(cast(i64 volatile *)a, operand); #elif GB_CPU_X86 i64 expected = a->value; for (;;) { @@ -4824,15 +4825,18 @@ GB_ALLOCATOR_PROC(gb_heap_allocator_proc) { #else // TODO(bill): *nix version that's decent case gbAllocation_Alloc: { - posix_memalign(&ptr, alignment, size); - - if (flags & gbAllocatorFlag_ClearToZero) { + gbAllocationHeader *header; + isize total_size = size + alignment + gb_size_of(gbAllocationHeader); + ptr = malloc(total_size); + header = cast(gbAllocationHeader *)ptr; + ptr = gb_align_forward(header+1, alignment); + gb_allocation_header_fill(header, ptr, size); + if (flags & gbAllocatorFlag_ClearToZero) gb_zero_size(ptr, size); - } } break; case gbAllocation_Free: { - free(old_memory); + free(gb_allocation_header(old_memory)); } break; case gbAllocation_Resize: { @@ -4929,7 +4933,7 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { void gb_affinity_init(gbAffinity *a) { usize count, count_size = gb_size_of(count); - a->is_accurate = false; + a->is_accurate = false; a->thread_count = 1; a->core_count = 1; a->threads_per_core = 1; @@ -4960,6 +4964,7 @@ void gb_affinity_destroy(gbAffinity *a) { b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { isize index; thread_t thread; + GB_ASSERT(thread < gb_affinity_thread_count thread_affinity_policy_data_t info; kern_return_t result; @@ -4979,7 +4984,81 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { } #elif defined(GB_SYSTEM_LINUX) -#error TODO(bill): Implement gbAffinity for linux +#warning gbAffinity is mostly mostly untested on Linux. All I know is that it compiles and runs. +#warning TODO(bill): gb_affinity_set on Linux is a stub + +// I have to read /proc/cpuinfo to get the number of threads per core. +#include + +void gb_affinity_init(gbAffinity *a) { + usize count, count_size = gb_size_of(count); + + b32 accurate = true; + + a->thread_count = 1; + a->core_count = sysconf(_SC_NPROCESSORS_ONLN); + a->threads_per_core = 1; + + + if(a->core_count <= 0) { + a->core_count = 1; + accurate = false; + } + + // Parsing /proc/cpuinfo to get the number of threads per core. + // NOTE: This calls the CPU's threads "cores", although the wording + // is kind of weird. This should be right, though. + FILE* cpu_info = fopen("/proc/cpuinfo", "r"); + + int threads = 0; + + if(cpu_info) { + while(1) { + // The 'temporary char'. Everything goes into this char, + // so that we can check against EOF at the end of this loop. + char c; + + #define check(letter) ((c = getc(cpu_info)) == letter) + if(check('c') && check('p') && check('u') && check(' ') && + check('c') && check('o') && check('r') && check('e') && check('s')) { + // We're on a CPU info line. + while(!check(EOF)) { + if(c == '\n') break; + else if(c < '0' || c > '9') continue; + threads = threads * 10 + (c - '0'); + } + break; + } else { + while(!check('\n')) {if(c==EOF) break;} + } + if(c == EOF) break; + } + } + + if(threads == 0) { + threads = 1; + accurate = false; + } + + a->threads_per_core = threads; + a->thread_count = a->threads_per_core * a->core_count; + + a->is_accurate = accurate; + +} + +void gb_affinity_destroy(gbAffinity *a) { + gb_unused(a); +} + +b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { + return true; +} + +isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { + GB_ASSERT(core >= 0 && core < a->core_count); + return a->threads_per_core; +} #else #error TODO(bill): Unknown system #endif @@ -7760,18 +7839,7 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) { return gb_alloc_str_len(a, buf, len+1); #else // TODO(bill): Make work on *nix, etc. - char* p = realpath(path, 0); - GB_ASSERT(p && "file does not exist"); - - isize len = gb_strlen(p); - - // bill... gb_alloc_str_len refused to work for this... - char* ret = gb_alloc(a, sizeof(char) * len + 1); - gb_memmove(ret, p, len); - ret[len] = 0; - free(p); - - return ret; + return gb_alloc_str_len(a, path, gb_strlen(path)); #endif } @@ -10385,4 +10453,3 @@ GB_COMPARE_PROC(gb_video_mode_dsc_cmp) { #endif #endif // GB_IMPLEMENTATION - diff --git a/src/main.c b/src/main.c index 3ff20ac61..b091a35e2 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 +#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, ...) { @@ -326,7 +331,75 @@ int main(int argc, char **argv) { } #else - #error Implement build stuff for this platform + + // NOTE: 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]; + // gb_printf_err("Linking lib: %.*s\n", LIT(lib)); + isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), + " \"%.*s\"", LIT(lib)); + 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 = ""; + if (build_context.is_dll) { + // Shared libraries are .dylib on MacOS and .so on Linux. + #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 = ""; + } + + exit_code = system_exec_command_line_app("msvc-link", true, + "ld \"%.*s\".obj -o \"%.*s.%s\" %s " + "-lc " + " %.*s " + " %s " + "", + 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