Преглед на файлове

Merge pull request #33 from zangent/master

Base of *nix port
gingerBill преди 8 години
родител
ревизия
0683d2b4f4
променени са 18 файла, в които са добавени 1005 реда и са изтрити 187 реда
  1. 6 0
      .gitignore
  2. 17 6
      README.md
  3. 22 0
      build.sh
  4. 0 3
      core/math.odin
  5. 1 1
      core/os.odin
  6. 313 0
      core/os_linux.odin
  7. 228 146
      core/os_x.odin
  8. 0 1
      core/sys/windows.odin
  9. 3 3
      misc/shell.bat
  10. BIN
      odin.exe
  11. 95 4
      src/build_settings.c
  12. 14 14
      src/check_decl.c
  13. 13 3
      src/checker.c
  14. 17 3
      src/gb/gb.h
  15. 6 0
      src/ir.c
  16. 135 0
      src/ir_print.c
  17. 135 2
      src/main.c
  18. 0 1
      src/types.c

+ 6 - 0
.gitignore

@@ -251,6 +251,12 @@ paket-files/
 
 
 # Project Specific
+
+# - Windows
 *.sln
 builds/
 bin/
+
+# - Linux/MacOS
+odin
+odin.dSYM

+ 17 - 6
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
 

+ 22 - 0
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

+ 0 - 3
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
-
-
-

+ 1 - 1
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";

+ 313 - 0
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());
+}

+ 228 - 146
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;
+
+// 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;
+
+args: [dynamic]string;
+
+FileTime :: struct #ordered {
+	seconds: i64,
+	nanoseconds: i64
+}
 
-// 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;
-
-// Windows reserves errors >= 1<<29 for application use
-// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
-
+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_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_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 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);
 
-read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
-	return unix_read(fd, data.data, data.count), 0;
+	bytes_written := unix_write(fd, ^data[0], len(data));
+	if(bytes_written == -1) {
+		return 0, 1;
+	}
+	return bytes_written, 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;
+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 cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
+	return bytes_read, 0;
+}
+
+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);
+read_entire_file :: proc(name: string) -> ([]byte, bool) {
 
-	if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
-		last_write_time = data.last_write_time;
+	handle, err := open_simple(name, O_RDONLY);
+	if(err != 0) {
+		fmt.println("Failed to open file.");
+		return nil, false;
 	}
+	defer(close(handle));
 
-	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);
+	// We have a file!
 
-	fd, err := open(name, O_RDONLY, 0);
-	if err != ERROR_NONE {
+	size: AddressSize;
+	size, err = seek(handle, 0, SEEK_END);
+	if(err != 0) {
+		fmt.println("Failed to seek to end of file.");
 		return nil, false;
 	}
-	defer close(fd);
 
-	length: i64;
-	file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
-	if !file_size_ok {
+	_, err = seek(handle, 0, SEEK_SET);
+	if(err != 0) {
+		fmt.println("Failed to seek to beginning of file.");
 		return nil, false;
 	}
 
-	data := new_slice(u8, length);
-	if data.data == nil {
+	// We have a file size!
+
+	data := make([]u8, size+1);
+	if data == nil {
+		fmt.println("Failed to allocate file buffer.");
 		return nil, false;
 	}
 
-	single_read_length: i32;
-	total_read: i64;
-
-	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;
-	}
+	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());
+}

+ 0 - 1
core/sys/windows.odin

@@ -624,4 +624,3 @@ Key_Code :: enum i32 {
 	PA1        = 0xFD,
 	OEM_CLEAR  = 0xFE,
 }
-

+ 3 - 3
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%

BIN
odin.exe


+ 95 - 4
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
 }

+ 14 - 14
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);
 			}

+ 13 - 3
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);
 
 }
-
-
-

+ 17 - 3
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) {

+ 6 - 0
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);
 			}

+ 135 - 0
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) {

+ 135 - 2
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

+ 0 - 1
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;