diff --git a/.gitignore b/.gitignore index 8421c3560..9286b374c 100644 --- a/.gitignore +++ b/.gitignore @@ -251,6 +251,12 @@ paket-files/ # Project Specific + +# - Windows *.sln builds/ bin/ + +# - Linux/MacOS +odin +odin.dSYM \ No newline at end of file diff --git a/README.md b/README.md index df8b76309..c742e5706 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,23 @@ Website: [https://odin.handmade.network/](https://odin.handmade.network/) ## Requirements to build and run -* Windows -* x86-64 -* MSVC 2015 installed (C99 support) -* Requires MSVC's link.exe as the linker - - run `vcvarsall.bat` to setup the path -* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe` +- Windows + * x86-64 + * MSVC 2015 installed (C99 support) + * [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe` + * Requires MSVC's link.exe as the linker + * run `vcvarsall.bat` to setup the path + +- MacOS + * x86-64 + * LLVM explicitly installed (`brew install llvm`) + * XCode installed (for the linker) + +- GNU/Linux + * x86-64 + * Build tools (ld) + * LLVM installed + * Clang installed (temporary - this is calling the linker for now) ## Warnings diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..cda80781c --- /dev/null +++ b/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +release_mode=0 + +warnings_to_disable="-Wno-attributes -Wno-implicit-function-declaration -Wno-incompatible-pointer-types -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare" +libraries="-pthread -ldl -lm" +other_args="-x c" +compiler="gcc" + +if [ "$release_mode" -eq "0" ]; then + other_args="${other_args} -g -fno-inline-functions" +fi +if [[ "$(uname)" == "Darwin" ]]; then + + # Set compiler to clang on MacOS + # MacOS provides a symlink to clang called gcc, but it's nice to be explicit here. + compiler="clang" + + other_args="${other_args} -liconv" +fi + +${compiler} src/main.c ${warnings_to_disable} ${libraries} ${other_args} -o odin diff --git a/core/math.odin b/core/math.odin index b52855746..44e9b50b4 100644 --- a/core/math.odin +++ b/core/math.odin @@ -376,6 +376,3 @@ F64_MIN_10_EXP :: -307; // min decimal exponent F64_MIN_EXP :: -1021; // min binary exponent F64_RADIX :: 2; // exponent radix F64_ROUNDS :: 1; // addition rounding: near - - - diff --git a/core/os.odin b/core/os.odin index d10b08bd2..8d9eadb75 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,3 +1,3 @@ #load "os_windows.odin" when ODIN_OS == "windows"; #load "os_x.odin" when ODIN_OS == "osx"; - +#load "os_linux.odin" when ODIN_OS == "linux"; \ No newline at end of file diff --git a/core/os_linux.odin b/core/os_linux.odin new file mode 100644 index 000000000..731361594 --- /dev/null +++ b/core/os_linux.odin @@ -0,0 +1,313 @@ +#import "fmt.odin"; +#import "strings.odin"; + +Handle :: i32; +File_Time :: u64; +Errno :: int; + +// TODO(zangent): Find out how to make this work on x64 and x32. +AddressSize :: i64; + +// INVALID_HANDLE: Handle : -1; + +O_RDONLY :: 0x00000; +O_WRONLY :: 0x00001; +O_RDWR :: 0x00002; +O_CREAT :: 0x00040; +O_EXCL :: 0x00080; +O_NOCTTY :: 0x00100; +O_TRUNC :: 0x00200; +O_NONBLOCK :: 0x00800; +O_APPEND :: 0x00400; +O_SYNC :: 0x01000; +O_ASYNC :: 0x02000; +O_CLOEXEC :: 0x80000; +SEEK_SET :: 0; +SEEK_CUR :: 1; +SEEK_END :: 2; +SEEK_DATA :: 3; +SEEK_HOLE :: 4; +SEEK_MAX :: SEEK_HOLE; + +// NOTE(zangent): These are OS specific! +// Do not mix these up! +RTLD_LAZY :: 0x001; +RTLD_NOW :: 0x002; +RTLD_BINDING_MASK :: 0x3; +RTLD_GLOBAL :: 0x100; + +args: [dynamic]string; + +FileTime :: struct #ordered { + seconds: i64, + nanoseconds: i32, + reserved: i32 +} + +// Translated from +// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h +// Validity is not guaranteed. + +Stat :: struct #ordered { + device_id : u64, // ID of device containing file + serial : u64, // File serial number + nlink : u32, // Number of hard links + mode : u32, // Mode of the file + uid : u32, // User ID of the file's owner + gid : u32, // Group ID of the file's group + _padding : i32, // 32 bits of padding + rdev : u64, // Device ID, if device + size : i64, // Size of the file, in bytes + block_size : i64, // Optimal bllocksize for I/O + blocks : i64, // Number of 512-byte blocks allocated + + last_access : FileTime, // Time of last access + modified : FileTime, // Time of last modification + status_change : FileTime, // Time of last status change + + _reserve1, + _reserve2, + _reserve3 : i64, + serial : u64, // File serial number...? Maybe. + _reserve4 : i64 +}; + +// File type + +S_IFMT :: 0170000; // Type of file mask +S_IFIFO :: 0010000; // Named pipe (fifo) +S_IFCHR :: 0020000; // Character special +S_IFDIR :: 0040000; // Directory +S_IFBLK :: 0060000; // Block special +S_IFREG :: 0100000; // Regular +S_IFLNK :: 0120000; // Symbolic link +S_IFSOCK :: 0140000; // Socket + +// File mode +// Read, write, execute/search by owner + +S_IRWXU :: 0000700; // RWX mask for owner +S_IRUSR :: 0000400; // R for owner +S_IWUSR :: 0000200; // W for owner +S_IXUSR :: 0000100; // X for owner + +// Read, write, execute/search by group + +S_IRWXG :: 0000070; // RWX mask for group +S_IRGRP :: 0000040; // R for group +S_IWGRP :: 0000020; // W for group +S_IXGRP :: 0000010; // X for group + +// Read, write, execute/search by others + +S_IRWXO :: 0000007; // RWX mask for other +S_IROTH :: 0000004; // R for other +S_IWOTH :: 0000002; // W for other +S_IXOTH :: 0000001; // X for other + +S_ISUID :: 0004000; // Set user id on execution +S_ISGID :: 0002000; // Set group id on execution +S_ISVTX :: 0001000; // Directory restrcted delete + +S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; } +S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; } +S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; } +S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; } +S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; } +S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; } +S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;} + +R_OK :: 4; // Test for read permission +W_OK :: 2; // Test for write permission +X_OK :: 1; // Test for execute permission +F_OK :: 0; // Test for file existance + +#foreign_system_library dl "dl"; +#foreign_system_library libc "c"; + +unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open"; +unix_close :: proc(handle: Handle) #foreign libc "close"; +unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read"; +unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write"; +unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek"; +unix_gettid :: proc() -> u64 #foreign libc "gettid"; +unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat"; +unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access"; + +unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc"; +unix_free :: proc(ptr: rawptr) #foreign libc "free"; +unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc"; +unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv"; + +unix_exit :: proc(status: int) #foreign libc "exit"; + +unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen"; +unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym"; +unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose"; +unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror"; + +// TODO(zangent): Change this to just `open` when Bill fixes overloading. +open_simple :: proc(path: string, mode: int) -> (Handle, Errno) { + + cstr := strings.new_c_string(path); + handle := unix_open(cstr, mode); + free(cstr); + if(handle == -1) { + return 0, 1; + } + return handle, 0; +} +// NOTE(zangent): This is here for compatability reasons. Should this be here? +open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { + return open_simple(path, mode); +} + +close :: proc(fd: Handle) { + unix_close(fd); +} + +write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) { + assert(fd != -1); + + bytes_written := unix_write(fd, ^data[0], len(data)); + if(bytes_written == -1) { + return 0, 1; + } + return bytes_written, 0; +} + +read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) { + assert(fd != -1); + + bytes_read := unix_read(fd, ^data[0], len(data)); + if(bytes_read == -1) { + return 0, 1; + } + return bytes_read, 0; +} + +seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) { + assert(fd != -1); + + final_offset := unix_lseek(fd, offset, whence); + if(final_offset == -1) { + return 0, 1; + } + return final_offset, 0; +} + + +// NOTE(bill): Uses startup to initialize it +stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE); +stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE); +stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE); + +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ + +stat :: proc(path: string) -> (Stat, int) #inline { + s: Stat; + cstr := strings.new_c_string(path); + defer free(cstr); + ret_int := unix_stat(cstr, ^s); + return s, ret_int; +} + +access :: proc(path: string, mask: int) -> bool #inline { + cstr := strings.new_c_string(path); + defer free(cstr); + return unix_access(cstr, mask) == 0; +} + +read_entire_file :: proc(name: string) -> ([]byte, bool) { + + handle, err := open_simple(name, O_RDONLY); + if(err != 0) { + fmt.println("Failed to open file."); + return nil, false; + } + defer(close(handle)); + + // We have a file! + + size: AddressSize; + size, err = seek(handle, 0, SEEK_END); + if(err != 0) { + fmt.println("Failed to seek to end of file."); + return nil, false; + } + + _, err = seek(handle, 0, SEEK_SET); + if(err != 0) { + fmt.println("Failed to seek to beginning of file."); + return nil, false; + } + + // We have a file size! + + data := make([]u8, size+1); + if data == nil { + fmt.println("Failed to allocate file buffer."); + return nil, false; + } + + read(handle, data); + data[size] = 0; + + return data, true; +} + +heap_alloc :: proc(size: int) -> rawptr { + assert(size > 0); + return unix_malloc(size); +} + +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + return unix_realloc(ptr, new_size); +} + +heap_free :: proc(ptr: rawptr) { + unix_free(ptr); +} + +getenv :: proc(name: string) -> (string, bool) { + path_str := strings.new_c_string(name); + cstr: ^u8 = unix_getenv(path_str); + free(path_str); + if(cstr == nil) { + return "", false; + } + return strings.to_odin_string(cstr), true; +} + +exit :: proc(code: int) { + unix_exit(code); +} + +current_thread_id :: proc() -> int { + // return cast(int) unix_gettid(); + return 0; +} + +dlopen :: proc(filename: string, flags: int) -> rawptr #inline { + cstr := strings.new_c_string(filename); + handle := unix_dlopen(cstr, flags); + free(cstr); + return handle; +} +dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline { + assert(handle != nil); + cstr := strings.new_c_string(symbol); + proc_handle := unix_dlsym(handle, cstr); + free(cstr); + return proc_handle; +} +dlclose :: proc(handle: rawptr) -> bool #inline { + assert(handle != nil); + return unix_dlclose(handle) == 0; +} +dlerror :: proc() -> string { + return strings.to_odin_string(unix_dlerror()); +} diff --git a/core/os_x.odin b/core/os_x.odin index 7627adece..fc2bcd3d3 100644 --- a/core/os_x.odin +++ b/core/os_x.odin @@ -1,11 +1,14 @@ #import "fmt.odin"; +#import "strings.odin"; Handle :: i32; File_Time :: u64; Errno :: int; -// INVALID_HANDLE: Handle : -1; +// TODO(zangent): Find out how to make this work on x64 and x32. +AddressSize :: i64; +// INVALID_HANDLE: Handle : -1; O_RDONLY :: 0x00000; O_WRONLY :: 0x00001; @@ -19,91 +22,178 @@ O_APPEND :: 0x00400; O_SYNC :: 0x01000; O_ASYNC :: 0x02000; O_CLOEXEC :: 0x80000; +SEEK_SET :: 0; +SEEK_CUR :: 1; +SEEK_END :: 2; +SEEK_DATA :: 3; +SEEK_HOLE :: 4; +SEEK_MAX :: SEEK_HOLE; -// ERROR_NONE: Errno : 0; -// ERROR_FILE_NOT_FOUND: Errno : 2; -// ERROR_PATH_NOT_FOUND: Errno : 3; -// ERROR_ACCESS_DENIED: Errno : 5; -// ERROR_NO_MORE_FILES: Errno : 18; -// ERROR_HANDLE_EOF: Errno : 38; -// ERROR_NETNAME_DELETED: Errno : 64; -// ERROR_FILE_EXISTS: Errno : 80; -// ERROR_BROKEN_PIPE: Errno : 109; -// ERROR_BUFFER_OVERFLOW: Errno : 111; -// ERROR_INSUFFICIENT_BUFFER: Errno : 122; -// ERROR_MOD_NOT_FOUND: Errno : 126; -// ERROR_PROC_NOT_FOUND: Errno : 127; -// ERROR_DIR_NOT_EMPTY: Errno : 145; -// ERROR_ALREADY_EXISTS: Errno : 183; -// ERROR_ENVVAR_NOT_FOUND: Errno : 203; -// ERROR_MORE_DATA: Errno : 234; -// ERROR_OPERATION_ABORTED: Errno : 995; -// ERROR_IO_PENDING: Errno : 997; -// ERROR_NOT_FOUND: Errno : 1168; -// ERROR_PRIVILEGE_NOT_HELD: Errno : 1314; -// WSAEACCES: Errno : 10013; -// WSAECONNRESET: Errno : 10054; +// NOTE(zangent): These are OS specific! +// Do not mix these up! +RTLD_LAZY :: 0x1; +RTLD_NOW :: 0x2; +RTLD_LOCAL :: 0x4; +RTLD_GLOBAL :: 0x8; +RTLD_NODELETE :: 0x80; +RTLD_NOLOAD :: 0x10; +RTLD_FIRST :: 0x100; -// Windows reserves errors >= 1<<29 for application use -// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0; +args: [dynamic]string; +FileTime :: struct #ordered { + seconds: i64, + nanoseconds: i64 +} + +Stat :: struct #ordered { + device_id : i32, // ID of device containing file + mode : u16, // Mode of the file + nlink : u16, // Number of hard links + serial : u64, // File serial number + uid : u32, // User ID of the file's owner + gid : u32, // Group ID of the file's group + rdev : i32, // Device ID, if device + + last_access : FileTime, // Time of last access + modified : FileTime, // Time of last modification + status_change : FileTime, // Time of last status change + created : FileTime, // Time of creation + + size : i64, // Size of the file, in bytes + blocks : i64, // Number of blocks allocated for the file + block_size: i32, // Optimal blocksize for I/O + flags : u32, // User-defined flags for the file + gen_num : u32, // File generation number ...? + _spare : i32, // RESERVED + _reserve1, + _reserve2 : i64, // RESERVED +}; + +// File type + +S_IFMT :: 0170000; // Type of file mask +S_IFIFO :: 0010000; // Named pipe (fifo) +S_IFCHR :: 0020000; // Character special +S_IFDIR :: 0040000; // Directory +S_IFBLK :: 0060000; // Block special +S_IFREG :: 0100000; // Regular +S_IFLNK :: 0120000; // Symbolic link +S_IFSOCK :: 0140000; // Socket + +// File mode +// Read, write, execute/search by owner + +S_IRWXU :: 0000700; // RWX mask for owner +S_IRUSR :: 0000400; // R for owner +S_IWUSR :: 0000200; // W for owner +S_IXUSR :: 0000100; // X for owner + +// Read, write, execute/search by group + +S_IRWXG :: 0000070; // RWX mask for group +S_IRGRP :: 0000040; // R for group +S_IWGRP :: 0000020; // W for group +S_IXGRP :: 0000010; // X for group + +// Read, write, execute/search by others + +S_IRWXO :: 0000007; // RWX mask for other +S_IROTH :: 0000004; // R for other +S_IWOTH :: 0000002; // W for other +S_IXOTH :: 0000001; // X for other + +S_ISUID :: 0004000; // Set user id on execution +S_ISGID :: 0002000; // Set group id on execution +S_ISVTX :: 0001000; // Directory restrcted delete + +S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; } +S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; } +S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; } +S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; } +S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; } +S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; } +S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;} + +R_OK :: 4; // Test for read permission +W_OK :: 2; // Test for write permission +X_OK :: 1; // Test for execute permission +F_OK :: 0; // Test for file existance + +#foreign_system_library dl "dl"; #foreign_system_library libc "c"; -unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open"; -unix_close :: proc(handle: Handle) #foreign libc "close"; -unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read"; -unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write"; -unix_gettid :: proc() -> u64 #foreign libc "gettid"; +unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open"; +unix_close :: proc(handle: Handle) #foreign libc "close"; +unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read"; +unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write"; +unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek"; +unix_gettid :: proc() -> u64 #foreign libc "gettid"; +unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat"; +unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access"; -unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc"; -unix_free :: proc(ptr: rawptr) #foreign libc "free"; -unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc"; - -unix_exit :: proc(status: int) #foreign libc "exit"; +unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc"; +unix_free :: proc(ptr: rawptr) #foreign libc "free"; +unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc"; +unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv"; +unix_exit :: proc(status: int) #foreign libc "exit"; + +unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen"; +unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym"; +unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose"; +unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror"; +// TODO(zangent): Change this to just `open` when Bill fixes overloading. +open_simple :: proc(path: string, mode: int) -> (Handle, Errno) { + + cstr := strings.new_c_string(path); + handle := unix_open(cstr, mode); + free(cstr); + if(handle == -1) { + return 0, 1; + } + return handle, 0; +} +// NOTE(zangent): This is here for compatability reasons. Should this be here? open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { - return unix_open(path.data, mode, perm), 0; + return open_simple(path, mode); } close :: proc(fd: Handle) { unix_close(fd); } -write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return unix_write(fd, data.data, data.count), 0; +write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) { + assert(fd != -1); + + bytes_written := unix_write(fd, ^data[0], len(data)); + if(bytes_written == -1) { + return 0, 1; + } + return bytes_written, 0; } -read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return unix_read(fd, data.data, data.count), 0; +read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) { + assert(fd != -1); + + bytes_read := unix_read(fd, ^data[0], len(data)); + if(bytes_read == -1) { + return 0, 1; + } + return bytes_read, 0; } -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - /* - using win32; - w: u32; - match whence { - case 0: w = FILE_BEGIN; - case 1: w = FILE_CURRENT; - case 2: w = FILE_END; - } - hi := cast(i32)(offset>>32); - lo := cast(i32)(offset); - ft := GetFileType(cast(HANDLE)fd); - if ft == FILE_TYPE_PIPE { - return 0, ERROR_FILE_IS_PIPE; - } - dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w); - if dw_ptr == INVALID_SET_FILE_POINTER { - err := GetLastError(); - return 0, cast(Errno)err; - } - return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE; +seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) { + assert(fd != -1); - */ - return 0, 0; + final_offset := unix_lseek(fd, offset, whence); + if(final_offset == -1) { + return 0, 1; + } + return final_offset, 0; } @@ -112,111 +202,85 @@ stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE); stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE); stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE); +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ -/* -get_std_handle :: proc(h: int) -> Handle { - fd := win32.GetStdHandle(cast(i32)h); - win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); - return cast(Handle)fd; +stat :: proc(path: string) -> (Stat, bool) #inline { + s: Stat; + cstr := strings.new_c_string(path); + defer free(cstr); + ret_int := unix_stat(cstr, ^s); + return s, ret_int==0; } - - - - - -last_write_time :: proc(fd: Handle) -> File_Time { - file_info: win32.BY_HANDLE_FILE_INFORMATION; - win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info); - lo := cast(File_Time)file_info.last_write_time.lo; - hi := cast(File_Time)file_info.last_write_time.hi; - return lo | hi << 32; +access :: proc(path: string, mask: int) -> bool #inline { + cstr := strings.new_c_string(path); + defer free(cstr); + return unix_access(cstr, mask) == 0; } -last_write_time_by_name :: proc(name: string) -> File_Time { - last_write_time: win32.FILETIME; - data: win32.FILE_ATTRIBUTE_DATA; - buf: [1024]byte; - - assert(buf.count > name.count); - - copy(buf[:], cast([]byte)name); - - if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 { - last_write_time = data.last_write_time; - } - - l := cast(File_Time)last_write_time.lo; - h := cast(File_Time)last_write_time.hi; - return l | h << 32; -} - - - - - read_entire_file :: proc(name: string) -> ([]byte, bool) { - buf: [300]byte; - copy(buf[:], cast([]byte)name); - fd, err := open(name, O_RDONLY, 0); - if err != ERROR_NONE { + handle, err := open_simple(name, O_RDONLY); + if(err != 0) { + fmt.println("Failed to open file."); return nil, false; } - defer close(fd); + defer(close(handle)); - length: i64; - file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0; - if !file_size_ok { + // We have a file! + + size: AddressSize; + size, err = seek(handle, 0, SEEK_END); + if(err != 0) { + fmt.println("Failed to seek to end of file."); return nil, false; } - data := new_slice(u8, length); - if data.data == nil { + _, err = seek(handle, 0, SEEK_SET); + if(err != 0) { + fmt.println("Failed to seek to beginning of file."); return nil, false; } - single_read_length: i32; - total_read: i64; + // We have a file size! - for total_read < length { - remaining := length - total_read; - to_read: u32; - MAX :: 1<<32-1; - if remaining <= MAX { - to_read = cast(u32)remaining; - } else { - to_read = MAX; - } - - win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil); - if single_read_length <= 0 { - free(data); - return nil, false; - } - - total_read += cast(i64)single_read_length; + data := make([]u8, size+1); + if data == nil { + fmt.println("Failed to allocate file buffer."); + return nil, false; } + read(handle, data); + data[size] = 0; + return data, true; } - -*/ - -heap_alloc :: proc(size: int) -> rawptr { +heap_alloc :: proc(size: int) -> rawptr #inline { assert(size > 0); return unix_malloc(size); } -heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline { return unix_realloc(ptr, new_size); } -heap_free :: proc(ptr: rawptr) { +heap_free :: proc(ptr: rawptr) #inline { unix_free(ptr); } +getenv :: proc(name: string) -> (string, bool) { + path_str := strings.new_c_string(name); + cstr: ^u8 = unix_getenv(path_str); + free(path_str); + if(cstr == nil) { + return "", false; + } + return strings.to_odin_string(cstr), true; +} -exit :: proc(code: int) { +exit :: proc(code: int) #inline { unix_exit(code); } @@ -226,5 +290,23 @@ current_thread_id :: proc() -> int { return 0; } - - +dlopen :: proc(filename: string, flags: int) -> rawptr #inline { + cstr := strings.new_c_string(filename); + handle := unix_dlopen(cstr, flags); + free(cstr); + return handle; +} +dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline { + assert(handle != nil); + cstr := strings.new_c_string(symbol); + proc_handle := unix_dlsym(handle, cstr); + free(cstr); + return proc_handle; +} +dlclose :: proc(handle: rawptr) -> bool #inline { + assert(handle != nil); + return unix_dlclose(handle) == 0; +} +dlerror :: proc() -> string { + return strings.to_odin_string(unix_dlerror()); +} \ No newline at end of file diff --git a/core/sys/windows.odin b/core/sys/windows.odin index f218e15c1..2493a29e1 100644 --- a/core/sys/windows.odin +++ b/core/sys/windows.odin @@ -624,4 +624,3 @@ Key_Code :: enum i32 { PA1 = 0xFD, OEM_CLEAR = 0xFE, } - diff --git a/misc/shell.bat b/misc/shell.bat index 5d9271622..6694eedb4 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -1,8 +1,8 @@ @echo off -rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL -call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL -rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL +rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL +rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL set _NO_DEBUG_HEAP=1 set path=w:\Odin\misc;%path% diff --git a/odin.exe b/odin.exe new file mode 100644 index 000000000..48f24a8bd Binary files /dev/null and b/odin.exe differ 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 + +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 -// #include +#if !defined(GB_SYSTEM_WINDOWS) + #include +#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;imodule->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 +#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;