Переглянути джерело

`build_dll`; Require an entry point procedure `main`

Ginger Bill 8 роки тому
батько
коміт
fa89d2775a
17 змінених файлів з 255 додано та 116 видалено
  1. 2 1
      build.bat
  2. 24 1
      code/demo.odin
  3. 2 0
      core/_soft_numbers.odin
  4. 5 4
      core/fmt.odin
  5. 4 4
      core/mem.odin
  6. 5 4
      core/os_windows.odin
  7. 1 0
      src/build.c
  8. 42 8
      src/checker/checker.c
  9. 19 11
      src/checker/decl.c
  10. 8 2
      src/checker/entity.c
  11. 8 6
      src/checker/expr.c
  12. 1 1
      src/checker/stmt.c
  13. 6 6
      src/checker/types.c
  14. 38 13
      src/main.c
  15. 31 30
      src/parser.c
  16. 17 18
      src/ssa.c
  17. 42 7
      src/ssa_print.c

+ 2 - 1
build.bat

@@ -49,7 +49,8 @@ rem pushd %build_dir%
 	cl %compiler_settings% "src\main.c" ^
 		/link %linker_settings% -OUT:%exe_name% ^
 	&& odin run code/demo.odin
-	rem odin run code/demo.odin
+	rem && odin build_dll code/example.odin ^
+	rem && odin run code/demo.odin
 
 
 	:do_not_compile_exe

+ 24 - 1
code/demo.odin

@@ -1,6 +1,29 @@
+#import "win32.odin"
 #import "fmt.odin"
 
+
 main :: proc() {
+	get_proc :: proc(lib: win32.HMODULE, name: string) -> proc() {
+		buf: [4096]byte
+		copy(buf[:], name as []byte)
 
-}
+		proc_handle := win32.GetProcAddress(lib, ^buf[0])
+		return proc_handle as proc()
+	}
+
+	lib := win32.LoadLibraryA(("example.dll\x00" as string).data)
+	if lib == nil {
+		fmt.println("Could not load library")
+		return
+	}
+	defer win32.FreeLibrary(lib)
 
+	proc_handle := get_proc(lib, "some_thing")
+	if proc_handle == nil {
+		fmt.println("Could not load 'some_thing'")
+		return
+	}
+
+	some_thing := (proc_handle as proc())
+	some_thing()
+}

+ 2 - 0
core/_soft_numbers.odin

@@ -1,5 +1,6 @@
 #shared_global_scope
 
+/*
 #import "fmt.odin"
 
 __u128_mod :: proc(a, b: u128) -> u128 #link_name "__umodti3" {
@@ -154,3 +155,4 @@ __u128_quo_mod :: proc(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
 	q.all = (q.all << 1) | (carry as u128)
 	return q.all, r.all
 }
+*/

+ 5 - 4
core/fmt.odin

@@ -123,6 +123,7 @@ bprint_i64 :: proc(buffer: ^[]byte, value: i64) {
 	bprint_u64(buffer, i as u64)
 }
 
+/*
 bprint_u128 :: proc(buffer: ^[]byte, value: u128) {
 	a := value transmute [2]u64
 	if a[1] != 0 {
@@ -138,7 +139,7 @@ bprint_i128 :: proc(buffer: ^[]byte, value: i128) {
 	}
 	bprint_u128(buffer, i as u128)
 }
-
+*/
 
 
 print__f64 :: proc(buffer: ^[]byte, value: f64, decimal_places: int) {
@@ -353,8 +354,8 @@ bprint_any :: proc(buf: ^[]byte, arg: any) {
 		case u32:  bprint_u64(buf, i as u64)
 		case i64:  bprint_i64(buf, i)
 		case u64:  bprint_u64(buf, i)
-		case i128: bprint_i128(buf, i)
-		case u128: bprint_u128(buf, i)
+		// case i128: bprint_i128(buf, i)
+		// case u128: bprint_u128(buf, i)
 
 		case int:  bprint_i64(buf, i as i64)
 		case uint: bprint_u64(buf, i as u64)
@@ -484,7 +485,7 @@ bprint_any :: proc(buf: ^[]byte, arg: any) {
 		bprint_string(buf, "(raw_union)")
 	case Procedure:
 		bprint_type(buf, arg.type_info)
-		bprint_string(buf, " @ 0x")
+		bprint_string(buf, " @ ")
 		bprint_pointer(buf, (arg.data as ^rawptr)^)
 	}
 }

+ 4 - 4
core/mem.odin

@@ -1,7 +1,7 @@
 #import "fmt.odin"
 #import "os.odin"
 
-set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
+set :: proc(data: rawptr, value: i32, len: int) -> rawptr #export "__mem_set" {
 	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
 	llvm_memset_64bit(data, value as byte, len, 1, false)
 	return data
@@ -11,14 +11,14 @@ zero :: proc(data: rawptr, len: int) -> rawptr {
 	return set(data, 0, len)
 }
 
-copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
+copy :: proc(dst, src: rawptr, len: int) -> rawptr #export "__mem_copy" {
 	// NOTE(bill): This _must_ implemented like C's memmove
 	llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64"
 	llvm_memmove_64bit(dst, src, len, 1, false)
 	return dst
 }
 
-copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
+copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #export "__mem_copy_non_overlapping" {
 	// NOTE(bill): This _must_ implemented like C's memcpy
 	llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64"
 	llvm_memcpy_64bit(dst, src, len, 1, false)
@@ -26,7 +26,7 @@ copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "_
 }
 
 
-compare :: proc(dst, src: rawptr, n: int) -> int #link_name "__mem_compare" {
+compare :: proc(dst, src: rawptr, n: int) -> int #export "__mem_compare" {
 	// Translation of http://mgronhol.github.io/fast-strcmp/
 	a := slice_ptr(dst as ^byte, n)
 	b := slice_ptr(src as ^byte, n)

+ 5 - 4
core/os_windows.odin

@@ -65,12 +65,13 @@ last_write_time :: proc(f: ^File) -> File_Time {
 last_write_time_by_name :: proc(name: string) -> File_Time {
 	last_write_time: win32.FILETIME
 	data: win32.WIN32_FILE_ATTRIBUTE_DATA
-
 	buf: [1024]byte
-	path := buf[:0]
-	fmt.bprint(^path, name, "\x00")
 
-	if win32.GetFileAttributesExA(path.data, win32.GetFileExInfoStandard, ^data) != 0 {
+	assert(buf.count > name.count)
+
+	copy(buf[:], name as []byte)
+
+	if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
 		last_write_time = data.last_write_time
 	}
 

+ 1 - 0
src/build.c

@@ -9,6 +9,7 @@ typedef struct BuildContext {
 	i64    max_align;
 	String llc_flags;
 	String link_flags;
+	bool   is_dll;
 } BuildContext;
 
 // TODO(bill): OS dependent versions for the BuildContext

+ 42 - 8
src/checker/checker.c

@@ -292,9 +292,17 @@ bool decl_info_has_init(DeclInfo *d) {
 		return true;
 	}
 	if (d->proc_decl != NULL) {
-		ast_node(pd, ProcDecl, d->proc_decl);
-		if (pd->body != NULL) {
-			return true;
+		switch (d->proc_decl->kind) {
+		case_ast_node(pd, ProcDecl, d->proc_decl);
+			if (pd->body != NULL) {
+				return true;
+			}
+		case_end;
+		case_ast_node(pd, ProcLit, d->proc_decl);
+			if (pd->body != NULL) {
+				return true;
+			}
+		case_end;
 		}
 	}
 
@@ -949,12 +957,15 @@ MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
 	MapEntity map = {0}; // Key: Entity *
 	map_entity_init(&map, heap_allocator());
 
-	for_array(i, info->entities.entries) {
-		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+	for_array(i, info->definitions.entries) {
+		Entity *e = info->definitions.entries.e[i].value;
 		if (e->scope->is_global) {
 			// NOTE(bill): Require runtime stuff
 			add_dependency_to_map(&map, info, e);
+		} else if (e->kind == Entity_Procedure) {
+			if ((e->Procedure.tags & ProcTag_export) != 0) {
+				add_dependency_to_map(&map, info, e);
+			}
 		}
 	}
 
@@ -1189,7 +1200,7 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As
 		case_ast_node(pd, ProcDecl, decl);
 			ast_node(n, Ident, pd->name);
 			Token token = *n;
-			Entity *e = make_entity_procedure(c->allocator, parent_scope, token, NULL);
+			Entity *e = make_entity_procedure(c->allocator, parent_scope, token, NULL, pd->tags);
 			e->identifier = pd->name;
 			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
 			d->proc_decl = decl;
@@ -1386,7 +1397,6 @@ void check_parsed_files(Checker *c) {
 
 	check_import_entities(c, &file_scopes);
 
-	map_scope_destroy(&file_scopes);
 
 	check_all_global_entities(c);
 	init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)`
@@ -1473,6 +1483,30 @@ void check_parsed_files(Checker *c) {
 			i64 align = type_align_of(c->sizes, c->allocator, e->type);
 		}
 	}
+
+	for_array(i, file_scopes.entries) {
+		Scope *s = file_scopes.entries.e[i].value;
+		if (s->is_init) {
+			Entity *e = current_scope_lookup_entity(s, str_lit("main"));
+			if (e == NULL) {
+				Token token = {0};
+				if (s->file->tokens.count > 0) {
+					token = s->file->tokens.e[0];
+				} else {
+					token.pos.file = s->file->tokenizer.fullpath;
+					token.pos.line = 1;
+					token.pos.column = 1;
+				}
+
+				error(token, "Undefined entry point procedure `main`");
+			}
+
+			break;
+		}
+	}
+
+	map_scope_destroy(&file_scopes);
+
 }
 
 

+ 19 - 11
src/checker/decl.c

@@ -139,8 +139,9 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 	Type *init_type = NULL;
 	if (vd->type) {
 		init_type = check_type_extra(c, vd->type, NULL);
-		if (init_type == NULL)
+		if (init_type == NULL) {
 			init_type = t_invalid;
+		}
 	}
 
 	for (isize i = 0; i < entity_count; i++) {
@@ -180,7 +181,9 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
 
 	if (operand->mode != Addressing_Constant) {
 		// TODO(bill): better error
-		error_node(operand->expr, "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
+		gbString str = expr_to_string(operand->expr);
+		error_node(operand->expr, "`%s` is not a constant", str);
+		gb_string_free(str);
 		if (e->type == NULL) {
 			e->type = t_invalid;
 		}
@@ -306,7 +309,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	check_procedure_type(c, proc_type, pd->type);
 
 	bool is_foreign      = (pd->tags & ProcTag_foreign)   != 0;
-	bool is_link_name    = (pd->tags & ProcTag_link_name) != 0;
+	bool is_export  = (pd->tags & ProcTag_export) != 0;
 	bool is_inline       = (pd->tags & ProcTag_inline)    != 0;
 	bool is_no_inline    = (pd->tags & ProcTag_no_inline) != 0;
 
@@ -315,10 +318,9 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		if (proc_type != NULL) {
 			TypeProc *pt = &proc_type->Proc;
 			if (pt->param_count != 0 ||
-			    pt->result_count) {
+			    pt->result_count != 0) {
 				gbString str = type_to_string(proc_type);
-				error(e->token,
-				      "Procedure type of `main` was expected to be `proc()`, got %s", str);
+				error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
 				gb_string_free(str);
 			}
 		}
@@ -328,8 +330,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
 	}
 
-	if (is_foreign && is_link_name) {
-		error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
+	if (is_foreign && is_export) {
+		error_node(pd->type, "You cannot apply both `foreign` and `export_name` to a procedure");
 	}
 
 	if (pd->body != NULL) {
@@ -350,6 +352,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		if (proc_decl->foreign_name.len > 0) {
 			name = proc_decl->foreign_name;
 		}
+
+		e->Procedure.is_foreign = true;
+		e->Procedure.foreign_name = name;
+
 		HashKey key = hash_string(name);
 		Entity **found = map_entity_get(fp, key);
 		if (found) {
@@ -366,10 +372,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		} else {
 			map_entity_set(fp, key, e);
 		}
-	} else if (is_link_name) {
+	} else if (is_export) {
 		MapEntity *fp = &c->info.foreign_procs;
 		AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
-		String name = proc_decl->link_name;
+		String name = proc_decl->export_name;
+
+		e->Procedure.export_name = name;
 
 		HashKey key = hash_string(name);
 		Entity **found = map_entity_get(fp, key);
@@ -377,7 +385,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 			Entity *f = *found;
 			TokenPos pos = f->token.pos;
 			error_node(d->proc_decl,
-			           "Non unique #link_name for procedure `%.*s`\n"
+			           "Non unique #export name for procedure `%.*s`\n"
 			           "\tother at %.*s(%td:%td)",
 			           LIT(name), LIT(pos.file), pos.line, pos.column);
 		} else {

+ 8 - 2
src/checker/entity.c

@@ -59,7 +59,12 @@ struct Entity {
 			i32 field_src_index;
 		} Variable;
 		i32 TypeName;
-		i32 Procedure;
+		struct {
+			bool   is_foreign;
+			String foreign_name;
+			String export_name;
+			u64    tags;
+		} Procedure;
 		struct {
 			BuiltinProcId id;
 		} Builtin;
@@ -138,8 +143,9 @@ Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *
 	return entity;
 }
 
-Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) {
+Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type, u64 tags) {
 	Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
+	entity->Procedure.tags = tags;
 	return entity;
 }
 

+ 8 - 6
src/checker/expr.c

@@ -22,8 +22,9 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
 
 
 typedef struct DelayedEntity {
-	Entity *entity;
-	DeclInfo *decl;
+	AstNode *   ident;
+	Entity *    entity;
+	DeclInfo *  decl;
 } DelayedEntity;
 
 typedef struct DelayedOtherFields {
@@ -104,7 +105,7 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie
 
 				add_entity_and_decl_info(c, name, e, d);
 
-				DelayedEntity delay = {e, d};
+				DelayedEntity delay = {name, e, d};
 				array_add(delayed_entities, delay);
 			}
 
@@ -181,7 +182,7 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie
 
 			add_entity_and_decl_info(c, td->name, e, d);
 
-			DelayedEntity delay = {e, d};
+			DelayedEntity delay = {td->name, e, d};
 			array_add(delayed_entities, delay);
 
 
@@ -231,6 +232,7 @@ void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, Delay
 	for_array(i, delayed_entities) {
 		DelayedEntity delayed = delayed_entities.e[i];
 		if (delayed.entity->kind == Entity_Constant) {
+			add_entity_and_decl_info(c, delayed.ident, delayed.entity, delayed.decl);
 			check_entity_decl(c, delayed.entity, delayed.decl, NULL);
 		}
 	}
@@ -1480,7 +1482,7 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
 		case Basic_i16:
 		case Basic_i32:
 		case Basic_i64:
-		case Basic_i128:
+		// case Basic_i128:
 		case Basic_int:
 			return gb_is_between(i, -imax, imax-1);
 
@@ -1488,7 +1490,7 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
 		case Basic_u16:
 		case Basic_u32:
 		case Basic_u64:
-		case Basic_u128:
+		// case Basic_u128:
 		case Basic_uint:
 			return !(u < 0 || u > umax);
 

+ 1 - 1
src/checker/stmt.c

@@ -1095,7 +1095,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	#if 1
 		// NOTE(bill): This must be handled here so it has access to the parent scope stuff
 		// e.g. using
-		Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
+		Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL, pd->tags);
 		e->identifier = pd->name;
 
 		DeclInfo *d = make_declaration_info(c->allocator, e->scope);

+ 6 - 6
src/checker/types.c

@@ -11,8 +11,8 @@ typedef enum BasicKind {
 	Basic_u32,
 	Basic_i64,
 	Basic_u64,
-	Basic_i128,
-	Basic_u128,
+	// Basic_i128,
+	// Basic_u128,
 	// Basic_f16,
 	Basic_f32,
 	Basic_f64,
@@ -205,8 +205,8 @@ gb_global Type basic_types[] = {
 	{Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned,  4, STR_LIT("u32")}},
 	{Type_Basic, {Basic_i64,            BasicFlag_Integer,                       8, STR_LIT("i64")}},
 	{Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned,  8, STR_LIT("u64")}},
-	{Type_Basic, {Basic_i128,           BasicFlag_Integer,                      16, STR_LIT("i128")}},
-	{Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
+	// {Type_Basic, {Basic_i128,           BasicFlag_Integer,                      16, STR_LIT("i128")}},
+	// {Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
 	// {Type_Basic, {Basic_f16,            BasicFlag_Float,                         2, STR_LIT("f16")}},
 	{Type_Basic, {Basic_f32,            BasicFlag_Float,                         4, STR_LIT("f32")}},
 	{Type_Basic, {Basic_f64,            BasicFlag_Float,                         8, STR_LIT("f64")}},
@@ -239,8 +239,8 @@ gb_global Type *t_i32             = &basic_types[Basic_i32];
 gb_global Type *t_u32             = &basic_types[Basic_u32];
 gb_global Type *t_i64             = &basic_types[Basic_i64];
 gb_global Type *t_u64             = &basic_types[Basic_u64];
-gb_global Type *t_i128            = &basic_types[Basic_i128];
-gb_global Type *t_u128            = &basic_types[Basic_u128];
+// gb_global Type *t_i128            = &basic_types[Basic_i128];
+// gb_global Type *t_u128            = &basic_types[Basic_u128];
 // gb_global Type *t_f16             = &basic_types[Basic_f16];
 gb_global Type *t_f32             = &basic_types[Basic_f32];
 gb_global Type *t_f64             = &basic_types[Basic_f64];

+ 38 - 13
src/main.c

@@ -16,7 +16,7 @@ extern "C" {
 // #include "vm.c"
 
 // NOTE(bill): `name` is used in debugging and profiling modes
-i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
+i32 win32_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
 	STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
 	PROCESS_INFORMATION pi = {0};
 	char cmd_line[4096] = {0};
@@ -29,8 +29,8 @@ i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
 	start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 	start_info.wShowWindow = SW_SHOW;
 	start_info.hStdInput   = GetStdHandle(STD_INPUT_HANDLE);
-	start_info.hStdOutput  = GetStdHandle(STD_OUTPUT_HANDLE);
-	start_info.hStdError   = GetStdHandle(STD_ERROR_HANDLE);
+	start_info.hStdOutput  = is_silent ? NULL : GetStdHandle(STD_OUTPUT_HANDLE);
+	start_info.hStdError   = is_silent ? NULL : GetStdHandle(STD_ERROR_HANDLE);
 
 	va_start(va, fmt);
 	cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
@@ -80,11 +80,11 @@ int main(int argc, char **argv) {
 	Timings timings = {0};
 	timings_init(&timings, str_lit("Total Time"), 128);
 	// defer (timings_destroy(&timings));
-
-#if 1
 	init_string_buffer_memory();
 	init_global_error_collector();
 
+#if 1
+
 	BuildContext build_context = {0};
 	init_build_context(&build_context);
 
@@ -94,9 +94,24 @@ int main(int argc, char **argv) {
 	bool run_output = false;
 	String arg1 = make_string_c(argv[1]);
 	if (str_eq(arg1, str_lit("run"))) {
+		if (argc != 3) {
+			usage(argv[0]);
+			return 1;
+		}
+		init_filename = argv[2];
 		run_output = true;
+	} else if (str_eq(arg1, str_lit("build_dll"))) {
+		if (argc != 3) {
+			usage(argv[0]);
+			return 1;
+		}
 		init_filename = argv[2];
+		build_context.is_dll = true;
 	} else if (str_eq(arg1, str_lit("build"))) {
+		if (argc != 3) {
+			usage(argv[0]);
+			return 1;
+		}
 		init_filename = argv[2];
 	} else if (str_eq(arg1, str_lit("version"))) {
 		gb_printf("%s version %.*s", argv[0], LIT(build_context.ODIN_VERSION));
@@ -136,7 +151,7 @@ int main(int argc, char **argv) {
 #if 1
 
 	ssaGen ssa = {0};
-	if (!ssa_gen_init(&ssa, &checker)) {
+	if (!ssa_gen_init(&ssa, &checker, &build_context)) {
 		return 1;
 	}
 	// defer (ssa_gen_destroy(&ssa));
@@ -164,7 +179,7 @@ int main(int argc, char **argv) {
 
 	i32 exit_code = 0;
 	// For more passes arguments: http://llvm.org/docs/Passes.html
-	exit_code = win32_exec_command_line_app("llvm-opt",
+	exit_code = win32_exec_command_line_app("llvm-opt", false,
 		"%.*sbin/opt %s -o %.*s.bc "
 		"-mem2reg "
 		"-memcpyopt "
@@ -182,7 +197,7 @@ int main(int argc, char **argv) {
 	#if 1
 	timings_start_section(&timings, str_lit("llvm-llc"));
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
-	exit_code = win32_exec_command_line_app("llvm-llc",
+	exit_code = win32_exec_command_line_app("llvm-llc", false,
 		"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
 		"%.*s "
 		// "-debug-pass=Arguments "
@@ -207,14 +222,24 @@ int main(int argc, char **argv) {
 		lib_str = gb_string_appendc(lib_str, lib_str_buf);
 	}
 
-	exit_code = win32_exec_command_line_app("msvc-link",
-		"link %.*s.obj -OUT:%.*s.exe %s "
+	char *output_ext = "exe";
+	char *link_settings = "";
+	if (build_context.is_dll) {
+		output_ext = "dll";
+		link_settings = "/DLL";
+	}
+
+	exit_code = win32_exec_command_line_app("msvc-link", true,
+		"link %.*s.obj -OUT:%.*s.%s %s "
 		"/defaultlib:libcmt "
 		"/nologo /incremental:no /opt:ref /subsystem:console "
 		" %.*s "
+		" %s "
 		"",
-		LIT(output), LIT(output),
-		lib_str, LIT(build_context.link_flags));
+		LIT(output), LIT(output), output_ext,
+		lib_str, LIT(build_context.link_flags),
+		link_settings
+		);
 	if (exit_code != 0) {
 		return exit_code;
 	}
@@ -222,7 +247,7 @@ int main(int argc, char **argv) {
 	// timings_print_all(&timings);
 
 	if (run_output) {
-		win32_exec_command_line_app("odin run", "%.*s.exe", cast(int)base_name_len, output_name);
+		win32_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
 	}
 	#endif
 #endif

+ 31 - 30
src/parser.c

@@ -65,15 +65,15 @@ typedef enum ProcTag {
 	ProcTag_no_bounds_check = GB_BIT(1),
 
 	ProcTag_foreign         = GB_BIT(10),
-	ProcTag_link_name       = GB_BIT(11),
+	ProcTag_export          = GB_BIT(11),
 	ProcTag_inline          = GB_BIT(12),
 	ProcTag_no_inline       = GB_BIT(13),
 	ProcTag_dll_import      = GB_BIT(14),
 	ProcTag_dll_export      = GB_BIT(15),
 
-	ProcTag_stdcall         = GB_BIT(16),
-	ProcTag_fastcall        = GB_BIT(17),
-	// ProcTag_cdecl           = GB_BIT(18),
+	ProcTag_stdcall         = GB_BIT(20),
+	ProcTag_fastcall        = GB_BIT(21),
+	// ProcTag_cdecl           = GB_BIT(22),
 } ProcTag;
 
 typedef enum VarDeclTag {
@@ -106,7 +106,7 @@ AstNodeArray make_ast_node_array(AstFile *f) {
 		AstNode *body;         \
 		u64      tags;         \
 		String   foreign_name; \
-		String   link_name;    \
+		String   export_name;    \
 	}) \
 	AST_NODE_KIND(CompoundLit, "compound literal", struct { \
 		AstNode *type; \
@@ -246,7 +246,7 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 			AstNode *body;         \
 			u64      tags;         \
 			String   foreign_name; \
-			String   link_name;    \
+			String   export_name;    \
 			AstNode *note;         \
 	}) \
 	AST_NODE_KIND(TypeDecl,   "type declaration",   struct { \
@@ -686,13 +686,13 @@ AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) {
 }
 
 
-AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags, String foreign_name, String link_name) {
+AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags, String foreign_name, String export_name) {
 	AstNode *result = make_node(f, AstNode_ProcLit);
 	result->ProcLit.type = type;
 	result->ProcLit.body = body;
 	result->ProcLit.tags = tags;
 	result->ProcLit.foreign_name = foreign_name;
-	result->ProcLit.link_name = link_name;
+	result->ProcLit.export_name = export_name;
 	return result;
 }
 
@@ -926,14 +926,14 @@ AstNode *make_proc_type(AstFile *f, Token token, AstNodeArray params, AstNodeArr
 	return result;
 }
 
-AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String link_name) {
+AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String export_name) {
 	AstNode *result = make_node(f, AstNode_ProcDecl);
 	result->ProcDecl.name = name;
 	result->ProcDecl.type = proc_type;
 	result->ProcDecl.body = body;
 	result->ProcDecl.tags = tags;
 	result->ProcDecl.foreign_name = foreign_name;
-	result->ProcDecl.link_name = link_name;
+	result->ProcDecl.export_name = export_name;
 	return result;
 }
 
@@ -1364,10 +1364,10 @@ bool is_foreign_name_valid(String name) {
 	return true;
 }
 
-void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_name) {
+void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *export_name) {
 	// TODO(bill): Add this to procedure literals too
 	GB_ASSERT(foreign_name != NULL);
-	GB_ASSERT(link_name    != NULL);
+	GB_ASSERT(export_name    != NULL);
 
 	while (f->curr_token.kind == Token_Hash) {
 		AstNode *tag_expr = parse_tag_expr(f, NULL);
@@ -1390,13 +1390,13 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
 
 				next_token(f);
 			}
-		} else if (str_eq(tag_name, str_lit("link_name"))) {
-			check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name);
+		} else if (str_eq(tag_name, str_lit("export"))) {
+			check_proc_add_tag(f, tag_expr, tags, ProcTag_export, tag_name);
 			if (f->curr_token.kind == Token_String) {
-				*link_name = f->curr_token.string;
+				*export_name = f->curr_token.string;
 				// TODO(bill): Check if valid string
-				if (!is_foreign_name_valid(*link_name)) {
-					syntax_error_node(tag_expr, "Invalid alternative link procedure name `%.*s`", LIT(*link_name));
+				if (!is_foreign_name_valid(*export_name)) {
+					syntax_error_node(tag_expr, "Invalid alternative link procedure name `%.*s`", LIT(*export_name));
 				}
 
 				next_token(f);
@@ -1420,8 +1420,8 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
 		#undef ELSE_IF_ADD_TAG
 	}
 
-	if ((*tags & ProcTag_foreign) && (*tags & ProcTag_link_name)) {
-		syntax_error(f->curr_token, "You cannot apply both #foreign and #link_name to a procedure");
+	if ((*tags & ProcTag_foreign) && (*tags & ProcTag_export)) {
+		syntax_error(f->curr_token, "You cannot apply both #foreign and #export to a procedure");
 	}
 
 	if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
@@ -1534,23 +1534,24 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 
 		u64 tags = 0;
 		String foreign_name = {0};
-		String link_name = {0};
-		parse_proc_tags(f, &tags, &foreign_name, &link_name);
+		String export_name = {0};
+		parse_proc_tags(f, &tags, &foreign_name, &export_name);
 		if (tags & ProcTag_foreign) {
 			syntax_error(f->curr_token, "#foreign cannot be applied to procedure literals");
 		}
-		if (tags & ProcTag_link_name) {
-			syntax_error(f->curr_token, "#link_name cannot be applied to procedure literals");
+		if (tags & ProcTag_export) {
+			syntax_error(f->curr_token, "#export cannot be applied to procedure literals");
 		}
 
 		if (f->curr_token.kind == Token_OpenBrace) {
 			AstNode *body;
 
-			f->expr_level++;
-			body = parse_body(f);
-			f->expr_level--;
+			if ((tags & ProcTag_foreign) != 0) {
+				syntax_error(f->curr_token, "A procedure tagged as `#foreign` cannot have a body");
+			}
 
-			type = make_proc_lit(f, type, body, tags, foreign_name, link_name);
+			body = parse_body(f);
+			type = make_proc_lit(f, type, body, tags, foreign_name, export_name);
 		} else if (type != NULL && type->kind == AstNode_ProcType) {
 			type->ProcType.tags = tags;
 		}
@@ -2425,9 +2426,9 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 	AstNode *body = NULL;
 	u64 tags = 0;
 	String foreign_name = {0};
-	String link_name = {0};
+	String export_name = {0};
 
-	parse_proc_tags(f, &tags, &foreign_name, &link_name);
+	parse_proc_tags(f, &tags, &foreign_name, &export_name);
 
 	AstNode *curr_proc = f->curr_proc;
 	f->curr_proc = proc_type;
@@ -2442,7 +2443,7 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 	}
 
 	f->curr_proc = curr_proc;
-	return make_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name);
+	return make_proc_decl(f, name, proc_type, body, tags, foreign_name, export_name);
 }
 
 AstNode *parse_if_stmt(AstFile *f) {

+ 17 - 18
src/ssa.c

@@ -17,6 +17,7 @@ typedef Array(ssaValue *) ssaValueArray;
 
 typedef struct ssaModule {
 	CheckerInfo * info;
+	BuildContext *build_context;
 	BaseTypeSizes sizes;
 	gbArena       arena;
 	gbArena       tmp_arena;
@@ -3188,11 +3189,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len);
 			}
 
-			if (args[0]->kind == ssaValue_Constant) {
-				ssaValueConstant *c = &args[0]->Constant;
-				gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind);
-			}
-
 			arg_count = type->param_count;
 			args[arg_count-1] = ssa_emit_load(proc, slice);
 		}
@@ -3934,8 +3930,8 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 			// parent.name-guid
 			String original_name = pd->name->Ident.string;
 			String pd_name = original_name;
-			if (pd->link_name.len > 0) {
-				pd_name = pd->link_name;
+			if (pd->export_name.len > 0) {
+				pd_name = pd->export_name;
 			}
 
 			isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1;
@@ -4676,7 +4672,7 @@ void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
 	map_ssa_value_set(&m->values, hash_pointer(e), v);
 }
 
-void ssa_init_module(ssaModule *m, Checker *c) {
+void ssa_init_module(ssaModule *m, Checker *c, BuildContext *build_context) {
 	// TODO(bill): Determine a decent size for the arena
 	isize token_count = c->parser->total_token_count;
 	isize arena_size = 4 * token_count * gb_size_of(ssaValue);
@@ -4686,6 +4682,7 @@ void ssa_init_module(ssaModule *m, Checker *c) {
 	m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
 	m->info = &c->info;
 	m->sizes = c->sizes;
+	m->build_context = build_context;
 
 	map_ssa_value_init(&m->values,  heap_allocator());
 	map_ssa_value_init(&m->members, heap_allocator());
@@ -4769,7 +4766,7 @@ void ssa_destroy_module(ssaModule *m) {
 ////////////////////////////////////////////////////////////////
 
 
-bool ssa_gen_init(ssaGen *s, Checker *c) {
+bool ssa_gen_init(ssaGen *s, Checker *c, BuildContext *build_context) {
 	if (global_error_collector.count != 0) {
 		return false;
 	}
@@ -4779,7 +4776,7 @@ bool ssa_gen_init(ssaGen *s, Checker *c) {
 		return false;
 	}
 
-	ssa_init_module(&s->module, c);
+	ssa_init_module(&s->module, c, build_context);
 	s->module.generate_debug_info = false;
 
 	// TODO(bill): generate appropriate output name
@@ -4870,6 +4867,7 @@ void ssa_gen_tree(ssaGen *s) {
 		} else if (e->kind == Entity_Procedure) {
 			if (e->scope->is_init && str_eq(name, str_lit("main"))) {
 				entry_point = e;
+				break;
 			}
 		}
 	}
@@ -4944,13 +4942,13 @@ void ssa_gen_tree(ssaGen *s) {
 			AstNodeProcDecl *pd = &decl->proc_decl->ProcDecl;
 			String original_name = name;
 			AstNode *body = pd->body;
-			if (pd->tags & ProcTag_foreign) {
-				name = pd->name->Ident.string;
+			if (e->Procedure.is_foreign) {
+				name = e->token.string; // NOTE(bill): Don't use the mangled name
 			}
 			if (pd->foreign_name.len > 0) {
 				name = pd->foreign_name;
-			} else if (pd->link_name.len > 0) {
-				name = pd->link_name;
+			} else if (pd->export_name.len > 0) {
+				name = pd->export_name;
 			}
 
 			ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
@@ -4968,8 +4966,9 @@ void ssa_gen_tree(ssaGen *s) {
 	for_array(i, m->members.entries) {
 		MapSsaValueEntry *entry = &m->members.entries.e[i];
 		ssaValue *v = entry->value;
-		if (v->kind == ssaValue_Proc)
+		if (v->kind == ssaValue_Proc) {
 			ssa_build_proc(v, NULL);
+		}
 	}
 
 	ssaDebugInfo *compile_unit = m->debug_info.entries.e[0].value;
@@ -5011,7 +5010,7 @@ void ssa_gen_tree(ssaGen *s) {
 		ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name);
 		Token token = {0};
 		token.string = name;
-		Entity *e = make_entity_procedure(a, NULL, token, proc_type);
+		Entity *e = make_entity_procedure(a, NULL, token, proc_type, 0);
 
 		map_ssa_value_set(&m->values, hash_pointer(e), p);
 		map_ssa_value_set(&m->members, hash_string(name), p);
@@ -5103,8 +5102,8 @@ void ssa_gen_tree(ssaGen *s) {
 					case Basic_u32:
 					case Basic_i64:
 					case Basic_u64:
-					case Basic_i128:
-					case Basic_u128:
+					// case Basic_i128:
+					// case Basic_u128:
 					case Basic_int:
 					case Basic_uint: {
 						tag = ssa_add_local_generated(proc, t_type_info_integer);

+ 42 - 7
src/ssa_print.c

@@ -154,8 +154,8 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 		case Basic_u32:    ssa_fprintf(f, "i32");                     break;
 		case Basic_i64:    ssa_fprintf(f, "i64");                     break;
 		case Basic_u64:    ssa_fprintf(f, "i64");                     break;
-		case Basic_i128:   ssa_fprintf(f, "i128");                    break;
-		case Basic_u128:   ssa_fprintf(f, "i128");                    break;
+		// case Basic_i128:   ssa_fprintf(f, "i128");                    break;
+		// case Basic_u128:   ssa_fprintf(f, "i128");                    break;
 		// case Basic_f16:    ssa_fprintf(f, "half");                    break;
 		case Basic_f32:    ssa_fprintf(f, "float");                   break;
 		case Basic_f64:    ssa_fprintf(f, "double");                  break;
@@ -623,7 +623,7 @@ void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type
 		ssa_print_encoded_local(f, value->Param.entity->token.string);
 		break;
 	case ssaValue_Proc:
-		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
+		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_export)) != 0);
 		break;
 	case ssaValue_Instr:
 		ssa_fprintf(f, "%%%d", value->index);
@@ -1226,11 +1226,14 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 		if (proc->tags & ProcTag_dll_import) {
 			ssa_fprintf(f, "dllimport ");
 		}
-		if (proc->tags & ProcTag_dll_export) {
+	} else {
+		ssa_fprintf(f, "\n");
+		ssa_fprintf(f, "define ");
+		if (proc->tags & ProcTag_export) {
+			ssa_fprintf(f, "dllexport ");
+		} else if (proc->tags & ProcTag_dll_export) {
 			ssa_fprintf(f, "dllexport ");
 		}
-	} else {
-		ssa_fprintf(f, "\ndefine ");
 	}
 
 	if (proc->tags & ProcTag_stdcall) {
@@ -1248,7 +1251,7 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 	}
 
 	ssa_fprintf(f, " ");
-	ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
+	ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_export)) != 0);
 	ssa_fprintf(f, "(");
 
 	if (proc_type->param_count > 0) {
@@ -1357,12 +1360,21 @@ void ssa_print_llvm_ir(ssaGen *ssa) {
 
 	ssa_fprintf(f, "\n");
 
+	bool dll_main_found = false;
+
 	for_array(member_index, m->members.entries) {
 		MapSsaValueEntry *entry = &m->members.entries.e[member_index];
 		ssaValue *v = entry->value;
 		if (v->kind != ssaValue_Proc) {
 			continue;
 		}
+
+#if defined(GB_SYSTEM_WINDOWS)
+		if (str_eq(v->Proc.name, str_lit("DllMain"))) {
+			dll_main_found = true;
+		}
+#endif
+
 		if (v->Proc.body == NULL) {
 			ssa_print_proc(f, m, &v->Proc);
 		}
@@ -1374,11 +1386,34 @@ void ssa_print_llvm_ir(ssaGen *ssa) {
 		if (v->kind != ssaValue_Proc) {
 			continue;
 		}
+
+#if defined(GB_SYSTEM_WINDOWS)
+		if (str_eq(v->Proc.name, str_lit("DllMain"))) {
+			dll_main_found = true;
+		}
+#endif
+
 		if (v->Proc.body != NULL) {
 			ssa_print_proc(f, m, &v->Proc);
 		}
 	}
 
+	if (m->build_context->is_dll) {
+#if !defined(GB_SYSTEM_WINDOWS)
+#error Setup dll initialization on linux if appropriate
+#else
+		if (!dll_main_found) {
+			ssa_fprintf(f,
+			"define i32 @DllMain(%%..rawptr %%inst, i32 %%reason, %%..rawptr %%reserved) {\n"
+			"entry:\n"
+			"	call void @main()\n"
+			"	ret i32 1\n"
+			"}\n"
+			);
+		}
+#endif
+	}
+
 
 	for_array(member_index, m->members.entries) {
 		MapSsaValueEntry *entry = &m->members.entries.e[member_index];