From 5957d7f7bee7e5fac4035d47ecaaaad022adbfb8 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 20 Jun 2017 12:38:05 +0100 Subject: [PATCH] Implicit Parameter Passing based `context` system (replacing Thread Local Storage (TLS) approach) --- core/_preload.odin | 87 +++++++++++++++++++++++------------------- misc/shell.bat | 3 +- src/check_decl.cpp | 5 +++ src/check_expr.cpp | 6 ++- src/checker.cpp | 12 +++--- src/ir.cpp | 95 +++++++++++++++++++++++++++++++++------------- src/ir_print.cpp | 38 +++++++++++++++---- src/parser.cpp | 19 ++++++---- 8 files changed, 178 insertions(+), 87 deletions(-) diff --git a/core/_preload.odin b/core/_preload.odin index e4df6bc87..85941c768 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -32,11 +32,12 @@ type ( } // NOTE(bill): This must match the compiler's CallingConvention enum { - Invalid = 0, - Odin = 1, - C = 2, - Std = 3, - Fast = 4, + Invalid = 0, + Odin = 1, + Contextless = 2, + C = 3, + Std = 4, + Fast = 5, } TypeInfoRecord struct #ordered { @@ -182,7 +183,7 @@ type ( } ) -#thread_local var __context: Context; +// #thread_local var __context: Context; @@ -192,7 +193,7 @@ type SourceCodeLocation struct { procedure: string, } -proc make_source_code_location(file: string, line, column: i64, procedure: string) -> SourceCodeLocation #inline { +proc make_source_code_location(file: string, line, column: i64, procedure: string) -> SourceCodeLocation #cc_contextless #inline { return SourceCodeLocation{file, line, column, procedure}; } @@ -200,8 +201,14 @@ proc make_source_code_location(file: string, line, column: i64, procedure: strin const DEFAULT_ALIGNMENT = align_of([vector 4]f32); +proc __init_context_from_ptr(c: ^Context, other: ^Context) #cc_contextless { + if c == nil { + return; + } + c^ = other^; +} -proc __init_context(c: ^Context) { +proc __init_context(c: ^Context) #cc_contextless { if c == nil { return; } @@ -214,12 +221,14 @@ proc __init_context(c: ^Context) { } +/* proc __check_context() { __init_context(&__context); } +*/ proc alloc(size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline { - __check_context(); + // __check_context(); var a = context.allocator; return a.procedure(a.data, AllocatorMode.Alloc, size, alignment, nil, 0, 0); } @@ -235,19 +244,19 @@ proc free_ptr_with_allocator(a: Allocator, ptr: rawptr) #inline { } proc free_ptr(ptr: rawptr) #inline { - __check_context(); + // __check_context(); free_ptr_with_allocator(context.allocator, ptr); } proc free_all() #inline { - __check_context(); + // __check_context(); var a = context.allocator; a.procedure(a.data, AllocatorMode.FreeAll, 0, 0, nil, 0, 0); } proc resize(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline { - __check_context(); + // __check_context(); var a = context.allocator; return a.procedure(a.data, AllocatorMode.Resize, new_size, alignment, ptr, old_size, 0); } @@ -312,7 +321,7 @@ proc default_allocator() -> Allocator { } -proc assert(condition: bool, message = "", using location = #caller_location) -> bool { +proc assert(condition: bool, message = "", using location = #caller_location) -> bool #cc_contextless { if !condition { if len(message) > 0 { fmt.printf("%s(%d:%d) Runtime assertion: %s\n", fully_pathed_filename, line, column, message); @@ -324,7 +333,7 @@ proc assert(condition: bool, message = "", using location = #caller_location) -> return condition; } -proc panic(message = "", using location = #caller_location) { +proc panic(message = "", using location = #caller_location) #cc_contextless { if len(message) > 0 { fmt.printf("%s(%d:%d) Panic: %s\n", fully_pathed_filename, line, column, message); } else { @@ -336,7 +345,7 @@ proc panic(message = "", using location = #caller_location) { -proc __string_eq(a, b: string) -> bool { +proc __string_eq(a, b: string) -> bool #cc_contextless { if len(a) != len(b) { return false; } @@ -349,25 +358,25 @@ proc __string_eq(a, b: string) -> bool { return __string_cmp(a, b) == 0; } -proc __string_cmp(a, b: string) -> int { +proc __string_cmp(a, b: string) -> int #cc_contextless { return __mem_compare(&a[0], &b[0], min(len(a), len(b))); } -proc __string_ne(a, b: string) -> bool #inline { return !__string_eq(a, b); } -proc __string_lt(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; } -proc __string_gt(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; } -proc __string_le(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; } -proc __string_ge(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; } +proc __string_ne(a, b: string) -> bool #cc_contextless #inline { return !__string_eq(a, b); } +proc __string_lt(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) < 0; } +proc __string_gt(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) > 0; } +proc __string_le(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) <= 0; } +proc __string_ge(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) >= 0; } -proc __complex64_eq (a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); } -proc __complex64_ne (a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); } +proc __complex64_eq (a, b: complex64) -> bool #cc_contextless #inline { return real(a) == real(b) && imag(a) == imag(b); } +proc __complex64_ne (a, b: complex64) -> bool #cc_contextless #inline { return real(a) != real(b) || imag(a) != imag(b); } -proc __complex128_eq(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); } -proc __complex128_ne(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); } +proc __complex128_eq(a, b: complex128) -> bool #cc_contextless #inline { return real(a) == real(b) && imag(a) == imag(b); } +proc __complex128_ne(a, b: complex128) -> bool #cc_contextless #inline { return real(a) != real(b) || imag(a) != imag(b); } -proc __bounds_check_error(file: string, line, column: int, index, count: int) { +proc __bounds_check_error(file: string, line, column: int, index, count: int) #cc_contextless { if 0 <= index && index < count { return; } @@ -376,7 +385,7 @@ proc __bounds_check_error(file: string, line, column: int, index, count: int) { __debug_trap(); } -proc __slice_expr_error(file: string, line, column: int, low, high, max: int) { +proc __slice_expr_error(file: string, line, column: int, low, high, max: int) #cc_contextless { if 0 <= low && low <= high && high <= max { return; } @@ -385,7 +394,7 @@ proc __slice_expr_error(file: string, line, column: int, low, high, max: int) { __debug_trap(); } -proc __substring_expr_error(file: string, line, column: int, low, high: int) { +proc __substring_expr_error(file: string, line, column: int, low, high: int) #cc_contextless { if 0 <= low && low <= high { return; } @@ -393,7 +402,7 @@ proc __substring_expr_error(file: string, line, column: int, low, high: int) { file, line, column, low, high); __debug_trap(); } -proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to: ^TypeInfo) { +proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to: ^TypeInfo) #cc_contextless { if !ok { fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n", file, line, column, from, to); @@ -401,12 +410,12 @@ proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to: } } -proc __string_decode_rune(s: string) -> (rune, int) #inline { +proc __string_decode_rune(s: string) -> (rune, int) #cc_contextless #inline { return utf8.decode_rune(s); } -proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr { +proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless { when size_of(rawptr) == 8 { foreign __llvm_core proc llvm_memset_64bit(dst: rawptr, val: u8, len: int, align: i32, is_volatile: bool) #link_name "llvm.memset.p0i8.i64"; llvm_memset_64bit(data, u8(value), len, 1, false); @@ -417,10 +426,10 @@ proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr { return data; } } -proc __mem_zero(data: rawptr, len: int) -> rawptr { +proc __mem_zero(data: rawptr, len: int) -> rawptr #cc_contextless { return __mem_set(data, 0, len); } -proc __mem_copy(dst, src: rawptr, len: int) -> rawptr { +proc __mem_copy(dst, src: rawptr, len: int) -> rawptr #cc_contextless { // NOTE(bill): This _must_ be implemented like C's memmove when size_of(rawptr) == 8 { foreign __llvm_core proc llvm_memmove_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memmove.p0i8.p0i8.i64"; @@ -432,7 +441,7 @@ proc __mem_copy(dst, src: rawptr, len: int) -> rawptr { return dst; } } -proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr { +proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr #cc_contextless { // NOTE(bill): This _must_ be implemented like C's memcpy when size_of(rawptr) == 8 { foreign __llvm_core proc llvm_memcpy_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memcpy.p0i8.p0i8.i64"; @@ -445,7 +454,7 @@ proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr { } } -proc __mem_compare(a, b: ^u8, n: int) -> int { +proc __mem_compare(a, b: ^u8, n: int) -> int #cc_contextless { for i in 0.. f32 #link_name "llvm.sqrt.f32"; proc __sqrt_f64(x: f64) -> f64 #link_name "llvm.sqrt.f64"; } -proc __abs_complex64(x: complex64) -> f32 #inline { +proc __abs_complex64(x: complex64) -> f32 #inline #cc_contextless { var r, i = real(x), imag(x); return __sqrt_f32(r*r + i*i); } -proc __abs_complex128(x: complex128) -> f64 #inline { +proc __abs_complex128(x: complex128) -> f64 #inline #cc_contextless { var r, i = real(x), imag(x); return __sqrt_f64(r*r + i*i); } @@ -475,7 +484,7 @@ proc __abs_complex128(x: complex128) -> f64 #inline { proc __dynamic_array_make(array_: rawptr, elem_size, elem_align: int, len, cap: int) { var array = ^raw.DynamicArray(array_); - __check_context(); + // __check_context(); array.allocator = context.allocator; assert(array.allocator.procedure != nil); @@ -492,7 +501,7 @@ proc __dynamic_array_reserve(array_: rawptr, elem_size, elem_align: int, cap: in return true; } - __check_context(); + // __check_context(); if array.allocator.procedure == nil { array.allocator = context.allocator; } diff --git a/misc/shell.bat b/misc/shell.bat index 8b7da6f80..6135fa507 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -1,9 +1,10 @@ @echo off rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL -call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL +rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL rem 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\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL set _NO_DEBUG_HEAP=1 set path=w:\Odin\misc;%path% diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dede1a26c..672673faf 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -333,6 +333,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str); gb_string_free(str); } + if (proc_type->Proc.calling_convention != ProcCC_Odin && + proc_type->Proc.calling_convention != ProcCC_Contextless) { + error(e->token, "Procedure `main` cannot have a custom calling convention"); + } + proc_type->Proc.calling_convention = ProcCC_Contextless; } if (is_inline && is_no_inline) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 05096bf52..df16ab2b4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1419,7 +1419,9 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a if (abi_return_type == NULL) { return false; } - if (cc == ProcCC_Odin) { + switch (cc) { + case ProcCC_Odin: + case ProcCC_Contextless: return false; } @@ -1465,6 +1467,8 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { if (end->flags&EntityFlag_CVarArg) { if (pt->calling_convention == ProcCC_Odin) { error(end->token, "Odin calling convention does not support #c_vararg"); + } else if (pt->calling_convention == ProcCC_Contextless) { + error(end->token, "Odin's contextless calling convention does not support #c_vararg"); } else if (pt->calling_convention == ProcCC_Fast) { error(end->token, "Fast calling convention does not support #c_vararg"); } else { diff --git a/src/checker.cpp b/src/checker.cpp index eaa38aaa4..c5b17cc2f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1779,14 +1779,16 @@ void check_all_global_entities(Checker *c) { continue; } - if (e->kind != Entity_Procedure && e->token.string == "main") { - if (e->scope->is_init) { + if (e->token.string == "main") { + if (e->kind != Entity_Procedure) { + if (e->scope->is_init) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + } else if (e->scope->is_global) { error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); continue; } - } else if (e->scope->is_global && e->token.string == "main") { - error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); - continue; } CheckerContext prev_context = c->context; diff --git a/src/ir.cpp b/src/ir.cpp index f5b431ec1..cbe81fdbe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -28,6 +28,8 @@ struct irModule { i32 global_array_index; // For ConstantSlice i32 global_generated_index; + irValue * global_default_context; + // NOTE(bill): To prevent strings from being copied a lot // Mainly used for file names Map const_strings; // Key: String @@ -125,6 +127,9 @@ struct irProcedure { irTargetList * target_list; Array referrers; + Array context_stack; + + Array branch_blocks; i32 local_count; @@ -216,6 +221,7 @@ struct irProcedure { irValue * return_ptr; \ irValue **args; \ isize arg_count; \ + irValue * context_ptr; \ }) \ IR_INSTR_KIND(StartupRuntime, i32) \ IR_INSTR_KIND(DebugDeclare, struct { \ @@ -986,22 +992,23 @@ irValue *ir_instr_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) return v; } -irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, irValue **args, isize arg_count, Type *result_type) { +irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, irValue **args, isize arg_count, Type *result_type, irValue *context_ptr) { irValue *v = ir_alloc_instr(p, irInstr_Call); - v->Instr.Call.value = value; - v->Instr.Call.return_ptr = return_ptr; - v->Instr.Call.args = args; - v->Instr.Call.arg_count = arg_count; - v->Instr.Call.type = result_type; + v->Instr.Call.value = value; + v->Instr.Call.return_ptr = return_ptr; + v->Instr.Call.args = args; + v->Instr.Call.arg_count = arg_count; + v->Instr.Call.type = result_type; + v->Instr.Call.context_ptr = context_ptr; return v; } irValue *ir_instr_conv(irProcedure *p, irConvKind kind, irValue *value, Type *from, Type *to) { irValue *v = ir_alloc_instr(p, irInstr_Conv); - v->Instr.Conv.kind = kind; + v->Instr.Conv.kind = kind; v->Instr.Conv.value = value; - v->Instr.Conv.from = from; - v->Instr.Conv.to = to; + v->Instr.Conv.from = from; + v->Instr.Conv.to = to; return v; } @@ -1472,11 +1479,30 @@ irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) { irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); + +irValue *ir_emit_global_call(irProcedure *proc, char *name_, irValue **args, isize arg_count); + +irValue *ir_find_or_generate_context_ptr(irProcedure *proc) { + if (proc->context_stack.count > 0) { + return proc->context_stack[proc->context_stack.count-1]; + } + irValue *c = ir_add_local_generated(proc, t_context); + ir_emit_store(proc, c, ir_emit_load(proc, proc->module->global_default_context)); + array_add(&proc->context_stack, c); + return c; +} + + irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; + irValue *context_ptr = NULL; + if (pt->Proc.calling_convention == ProcCC_Odin) { + context_ptr = ir_find_or_generate_context_ptr(p); + } + isize param_count = pt->Proc.param_count; if (pt->Proc.c_vararg) { GB_ASSERT(param_count-1 <= arg_count); @@ -1500,11 +1526,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_ if (pt->Proc.return_by_pointer) { irValue *return_ptr = ir_add_local_generated(p, rt); GB_ASSERT(is_type_pointer(ir_type(return_ptr))); - ir_emit(p, ir_instr_call(p, value, return_ptr, args, arg_count, NULL)); + ir_emit(p, ir_instr_call(p, value, return_ptr, args, arg_count, NULL, context_ptr)); return ir_emit_load(p, return_ptr); } - irValue *result = ir_emit(p, ir_instr_call(p, value, NULL, args, arg_count, abi_rt)); + irValue *result = ir_emit(p, ir_instr_call(p, value, NULL, args, arg_count, abi_rt, context_ptr)); if (abi_rt != results) { result = ir_emit_transmute(p, result, rt); } @@ -4865,7 +4891,8 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { irValue *v = NULL; switch (i->kind) { case Token_context: - v = ir_find_global_variable(proc, str_lit("__context")); + v = ir_find_or_generate_context_ptr(proc); + // v = ir_find_global_variable(proc, str_lit("__context")); break; } @@ -6716,13 +6743,13 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_emit_comment(proc, str_lit("PushAllocator")); ir_open_scope(proc); - irValue *context_ptr = ir_find_global_variable(proc, str_lit("__context")); - irValue *prev_context = ir_add_local_generated(proc, t_context); - ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr)); + irValue *prev = ir_find_or_generate_context_ptr(proc); + irValue *next = ir_add_local_generated(proc, t_context); + ir_emit_store(proc, next, ir_emit_load(proc, prev)); + array_add(&proc->context_stack, next); + defer (array_pop(&proc->context_stack)); - ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false)); - - irValue *gep = ir_emit_struct_ep(proc, context_ptr, 1); + irValue *gep = ir_emit_struct_ep(proc, next, 1); ir_emit_store(proc, gep, ir_build_expr(proc, pa->expr)); ir_build_stmt(proc, pa->body); @@ -6735,13 +6762,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_emit_comment(proc, str_lit("PushContext")); ir_open_scope(proc); - irValue *context_ptr = ir_find_global_variable(proc, str_lit("__context")); - irValue *prev_context = ir_add_local_generated(proc, t_context); - ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr)); + irValue *prev = ir_find_or_generate_context_ptr(proc); + irValue *next = ir_add_local_generated(proc, t_context); + array_add(&proc->context_stack, next); + defer (array_pop(&proc->context_stack)); - ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false)); - - ir_emit_store(proc, context_ptr, ir_build_expr(proc, pc->expr)); + ir_emit_store(proc, next, ir_build_expr(proc, pc->expr)); ir_build_stmt(proc, pc->body); @@ -6785,12 +6811,14 @@ void ir_number_proc_registers(irProcedure *proc) { } void ir_begin_procedure_body(irProcedure *proc) { + gbAllocator a = proc->module->allocator; array_add(&proc->module->procs, proc); array_init(&proc->blocks, heap_allocator()); array_init(&proc->defer_stmts, heap_allocator()); array_init(&proc->children, heap_allocator()); array_init(&proc->branch_blocks, heap_allocator()); + array_init(&proc->context_stack, heap_allocator()); DeclInfo *decl = decl_info_of_entity(proc->module->info, proc->entity); if (decl != NULL) { @@ -6808,7 +6836,6 @@ void ir_begin_procedure_body(irProcedure *proc) { if (proc->type->Proc.return_by_pointer) { // NOTE(bill): this must be the first parameter stored - gbAllocator a = proc->module->allocator; Type *ptr_type = make_type_pointer(a, reduce_tuple_to_single_type(proc->type->Proc.results)); Entity *e = make_entity_param(a, NULL, make_token_ident(str_lit("agg.result")), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; @@ -6848,6 +6875,13 @@ void ir_begin_procedure_body(irProcedure *proc) { } + if (proc->type->Proc.calling_convention == ProcCC_Odin) { + Entity *e = make_entity_param(a, NULL, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); + e->flags |= EntityFlag_NoAlias; + irValue *param = ir_value_param(a, proc, e, e->type); + ir_module_add_value(proc->module, e, param); + array_add(&proc->context_stack, param); + } } @@ -7222,6 +7256,9 @@ void ir_gen_tree(irGen *s) { } } + { // Add global default context + m->global_default_context = ir_add_global_generated(m, t_context, NULL); + } struct irGlobalVariable { irValue *var, *init; DeclInfo *decl; @@ -7478,7 +7515,7 @@ void ir_gen_tree(irGen *s) { String name = str_lit(IR_STARTUP_RUNTIME_PROC_NAME); Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), NULL, 0, - NULL, 0, false, ProcCC_Odin); + NULL, 0, false, ProcCC_Contextless); AstNode *body = gb_alloc_item(a, AstNode); Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); irValue *p = ir_value_procedure(a, m, e, proc_type, NULL, body, name); @@ -7492,6 +7529,12 @@ void ir_gen_tree(irGen *s) { ir_begin_procedure_body(proc); + { + irValue **args = gb_alloc_array(a, irValue *, 1); + args[0] = m->global_default_context; + ir_emit_global_call(proc, "__init_context", args, 1); + } + // TODO(bill): Should do a dependency graph do check which order to initialize them in? for_array(i, global_variables) { irGlobalVariable *var = &global_variables[i]; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c39fc6acf..a3af21b6d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -199,6 +199,12 @@ void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { ir_print_type(f, m, t->Proc.abi_compat_params[i]); } } + if (t->Proc.calling_convention == ProcCC_Odin) { + if (param_count > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t_context_ptr); + } ir_fprintf(f, ")"); } @@ -737,10 +743,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConvention cc) { switch (cc) { - case ProcCC_Odin: ir_fprintf(f, ""); break; - case ProcCC_C: ir_fprintf(f, "ccc "); break; - case ProcCC_Std: ir_fprintf(f, "cc 64 "); break; - case ProcCC_Fast: ir_fprintf(f, "cc 65 "); break; + case ProcCC_Odin: ir_fprintf(f, ""); break; + case ProcCC_Contextless: ir_fprintf(f, ""); break; + case ProcCC_C: ir_fprintf(f, "ccc "); break; + case ProcCC_Std: ir_fprintf(f, "cc 64 "); break; + case ProcCC_Fast: ir_fprintf(f, "cc 65 "); break; default: GB_PANIC("unknown calling convention: %d", cc); } } @@ -1261,8 +1268,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (call->arg_count > 0) { - Type *proc_type = base_type(ir_type(call->value)); - GB_ASSERT(proc_type->kind == Type_Proc); TypeTuple *params = &proc_type->Proc.params->Tuple; if (proc_type->Proc.c_vararg) { isize i = 0; @@ -1293,7 +1298,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_print_value(f, m, arg, t); } } else { - for (isize i = 0; i < call->arg_count; i++) { + GB_ASSERT(call->arg_count == params->variable_count); + isize param_count = params->variable_count; + for (isize i = 0; i < param_count; i++) { Entity *e = params->variables[i]; GB_ASSERT(e != NULL); irValue *arg = call->args[i]; @@ -1310,6 +1317,14 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { } } } + if (proc_type->Proc.calling_convention == ProcCC_Odin) { + if (proc_type->Proc.param_count > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t_context_ptr); + ir_fprintf(f, " noalias nonnull"); + ir_print_value(f, m, call->context_ptr, t_context_ptr); + } ir_fprintf(f, ")\n"); } break; @@ -1538,7 +1553,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (param_count > 0) { TypeTuple *params = &proc_type->params->Tuple; - for (isize i = 0; i < params->variable_count; i++) { + for (isize i = 0; i < param_count; i++) { Entity *e = params->variables[i]; Type *original_type = e->type; Type *abi_type = proc_type->abi_compat_params[i]; @@ -1564,6 +1579,13 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { } } } + if (proc_type->calling_convention == ProcCC_Odin) { + if (param_count > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t_context_ptr); + ir_fprintf(f, " noalias nonnull %%__.context_ptr"); + } ir_fprintf(f, ") "); diff --git a/src/parser.cpp b/src/parser.cpp index d09db81d2..1b1bfcdaf 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -91,11 +91,12 @@ enum ProcTag { }; enum ProcCallingConvention { - ProcCC_Invalid = 0, - ProcCC_Odin = 1, - ProcCC_C = 2, - ProcCC_Std = 3, - ProcCC_Fast = 4, + ProcCC_Invalid = 0, + ProcCC_Odin = 1, + ProcCC_Contextless = 2, + ProcCC_C = 3, + ProcCC_Std = 4, + ProcCC_Fast = 5, }; enum VarDeclFlag { @@ -2070,14 +2071,18 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven ELSE_IF_ADD_TAG(no_bounds_check) ELSE_IF_ADD_TAG(inline) ELSE_IF_ADD_TAG(no_inline) - // ELSE_IF_ADD_TAG(dll_import) - // ELSE_IF_ADD_TAG(dll_export) else if (tag_name == "cc_odin") { if (cc == ProcCC_Invalid) { cc = ProcCC_Odin; } else { syntax_error(tag_expr, "Multiple calling conventions for procedure type"); } + } else if (tag_name == "cc_contextless") { + if (cc == ProcCC_Invalid) { + cc = ProcCC_Contextless; + } else { + syntax_error(tag_expr, "Multiple calling conventions for procedure type"); + } } else if (tag_name == "cc_c") { if (cc == ProcCC_Invalid) { cc = ProcCC_C;