diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 00ce5d2ed..75a157d9e 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -404,6 +404,14 @@ x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) --- x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- +// C specific things +c_va_list :: struct{/*platform specific implementation*/} + +c_va_start :: proc(list: ^c_va_list, /*#c_vararg parameter*/ args: ..$T) --- +c_va_end :: proc(list: ^c_va_list) --- +c_va_copy :: proc(dst, src: ^c_va_list) --- +c_va_arg :: proc(list: ^c_va_list, $T: typeid) -> T --- + // Darwin targets only objc_object :: struct{} diff --git a/core/c/libc/wchar.odin b/core/c/libc/wchar.odin index 248611409..73db7296c 100644 --- a/core/c/libc/wchar.odin +++ b/core/c/libc/wchar.odin @@ -17,12 +17,12 @@ foreign libc { fwscanf :: proc(stream: ^FILE, format: [^]wchar_t, #c_vararg arg: ..any) -> int --- swprintf :: proc(stream: ^FILE, n: size_t, format: [^]wchar_t, #c_vararg arg: ..any) -> int --- swscanf :: proc(s, format: [^]wchar_t, #c_vararg arg: ..any) -> int --- - vfwprintf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int --- - vfwscanf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int --- - vswprintf :: proc(s: [^]wchar_t, n: size_t, format: [^]wchar_t, arg: va_list) -> int --- - vswscanf :: proc(s, format: [^]wchar_t, arg: va_list) -> int --- - vwprintf :: proc(format: [^]wchar_t, arg: va_list) -> int --- - vwscanf :: proc(format: [^]wchar_t, arg: va_list) -> int --- + vfwprintf :: proc(stream: ^FILE, format: [^]wchar_t, arg: ^va_list) -> int --- + vfwscanf :: proc(stream: ^FILE, format: [^]wchar_t, arg: ^va_list) -> int --- + vswprintf :: proc(s: [^]wchar_t, n: size_t, format: [^]wchar_t, arg: ^va_list) -> int --- + vswscanf :: proc(s, format: [^]wchar_t, arg: ^va_list) -> int --- + vwprintf :: proc(format: [^]wchar_t, arg: ^va_list) -> int --- + vwscanf :: proc(format: [^]wchar_t, arg: ^va_list) -> int --- wprintf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int --- wscanf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a31e9cd33..91aecdc41 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -739,6 +739,131 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan } } +gb_internal bool check_builtin_c_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + String const &builtin_name = builtin_procs[id].name; + + ast_node(ce, CallExpr, call); + switch (id) { + default: + GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name)); + return false; + + case BuiltinProc_c_va_start: { + Operand list = {}; + check_expr(c, &list, ce->args[0]); + if (list.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(list.type, t_c_va_list_ptr)) { + gbString lpt = type_to_string(t_c_va_list_ptr); + gbString t = type_to_string(list.type); + error(list.expr, "'%.*s' expected a value of type %s, got type %s", LIT(builtin_name), lpt, t); + gb_string_free(t); + gb_string_free(lpt); + return false; + } + + Operand args = {}; + check_expr(c, &args, ce->args[1]); + if (list.mode == Addressing_Invalid) { + return false; + } + Entity *e = entity_of_node(args.expr); + if (e == nullptr || (e->flags & EntityFlag_CVarArg) == 0) { + error(list.expr, "'%.*s' expected a `#c_vararg` parameter", LIT(builtin_name)); + } + + operand->mode = Addressing_NoValue; + operand->type = nullptr; + return true; + + } break; + + case BuiltinProc_c_va_end: { + Operand list = {}; + check_expr(c, &list, ce->args[0]); + if (list.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(list.type, t_c_va_list_ptr)) { + gbString lpt = type_to_string(t_c_va_list_ptr); + gbString t = type_to_string(list.type); + error(list.expr, "'%.*s' expected a value of type %s, got type %s", LIT(builtin_name), lpt, t); + gb_string_free(t); + gb_string_free(lpt); + return false; + } + + operand->mode = Addressing_NoValue; + operand->type = nullptr; + return true; + + } break; + + case BuiltinProc_c_va_copy: { + Operand dst = {}; + check_expr(c, &dst, ce->args[0]); + if (dst.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(dst.type, t_c_va_list_ptr)) { + gbString lpt = type_to_string(t_c_va_list_ptr); + gbString t = type_to_string(dst.type); + error(dst.expr, "'%.*s' expected a value of type %s, got type %s", LIT(builtin_name), lpt, t); + gb_string_free(t); + gb_string_free(lpt); + return false; + } + + Operand src = {}; + check_expr(c, &src, ce->args[1]); + if (src.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(src.type, t_c_va_list_ptr)) { + gbString lpt = type_to_string(t_c_va_list_ptr); + gbString t = type_to_string(src.type); + error(src.expr, "'%.*s' expected a value of type %s, got type %s", LIT(builtin_name), lpt, t); + gb_string_free(t); + gb_string_free(lpt); + return false; + } + + operand->mode = Addressing_NoValue; + operand->type = nullptr; + return true; + + } break; + + case BuiltinProc_c_va_arg: { + Operand list = {}; + check_expr(c, &list, ce->args[0]); + if (list.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(list.type, t_c_va_list_ptr)) { + gbString lpt = type_to_string(t_c_va_list_ptr); + gbString t = type_to_string(list.type); + error(list.expr, "'%.*s' expected a value of type %s, got type %s", LIT(builtin_name), lpt, t); + gb_string_free(t); + gb_string_free(lpt); + return false; + } + + + Type *type = check_type(c, ce->args[1]); + if (type == nullptr || type == t_invalid) { + error(ce->args[1], "'%.*s' expected a type as the second parameter to intrinsics.%.*s", LIT(builtin_name)); + return false; + } + + operand->mode = Addressing_Value; + operand->type = type; + return true; + } break; + } +} + gb_internal bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) { Operand x = {}; check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order); @@ -2688,6 +2813,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_super: return check_builtin_objc_procedure(c, operand, call, id, type_hint); + case BuiltinProc_c_va_start: + case BuiltinProc_c_va_end: + case BuiltinProc_c_va_copy: + case BuiltinProc_c_va_arg: + return check_builtin_c_procedure(c, operand, call, id, type_hint); + case BuiltinProc___entry_point: operand->mode = Addressing_NoValue; operand->type = nullptr; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index afa819dd9..44d0300f9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1556,9 +1556,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (is_foreign) { error(pl->body, "A foreign procedure cannot have a body"); } - if (proc_type->Proc.c_vararg) { - error(pl->body, "A procedure with a '#c_vararg' field cannot have a body and must be foreign"); - } + // if (proc_type->Proc.c_vararg) { + // error(pl->body, "A procedure with a '#c_vararg' field cannot have a body and must be foreign"); + // } d->scope = ctx->scope; diff --git a/src/checker.cpp b/src/checker.cpp index 646940ed7..968a46215 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1449,6 +1449,72 @@ gb_internal void init_universal(void) { t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id); } + + // intrinsics types for C stuff + { + Scope *scope = create_scope(nullptr, nullptr); + + auto dummy_field = [](Scope *scope, Type *type, i32 field_index, char const *name = nullptr) -> Entity * { + auto token = blank_token; + if (name) { + token.string = make_string_c(name); + } + return alloc_entity_field(scope, token, type, false, 0, EntityState_Resolved); + }; + + auto fields = array_make(permanent_allocator(), 0, 8); + + switch (build_context.metrics.arch) { + case TargetArch_amd64: + switch (build_context.metrics.os) { + case TargetOs_freestanding: + /*check*/ + case TargetOs_linux: + case TargetOs_freebsd: + case TargetOs_netbsd: + case TargetOs_openbsd: + array_add(&fields, dummy_field(scope, t_u32, 0, "gp_offset")); + array_add(&fields, dummy_field(scope, t_u32, 1, "fp_offset")); + array_add(&fields, dummy_field(scope, t_rawptr, 2, "overflow_arg_area")); + array_add(&fields, dummy_field(scope, t_rawptr, 3, "reg_save_area")); + break; + } + break; + + case TargetArch_arm64: + switch (build_context.metrics.os) { + case TargetOs_darwin: + // AARCH64 architecture is different to other arm64 platforms + array_add(&fields, dummy_field(scope, t_rawptr, 0)); + break; + + case TargetOs_freestanding: + case TargetOs_linux: + case TargetOs_freebsd: + case TargetOs_netbsd: + case TargetOs_openbsd: // Not sure if this is correct + array_add(&fields, dummy_field(scope, t_rawptr, 0, "__stack")); + array_add(&fields, dummy_field(scope, t_rawptr, 1, "__gr_top")); + array_add(&fields, dummy_field(scope, t_rawptr, 2, "__vr_top")); + array_add(&fields, dummy_field(scope, t_i32, 3, "__gr_offs")); + array_add(&fields, dummy_field(scope, t_i32, 4, "__vr_offs")); + break; + } + break; + } + + if (fields.count == 0) { + array_add(&fields, dummy_field(scope, t_rawptr, 0)); + } + + Type *va_list_struct = alloc_type_struct_complete(); + va_list_struct->Struct.scope = scope; + va_list_struct->Struct.fields = slice_from_array(fields); + GB_ASSERT(type_size_of(va_list_struct) > 0); + + t_c_va_list = add_global_type_name(intrinsics_pkg->scope, str_lit("c_va_list"), va_list_struct); + t_c_va_list_ptr = alloc_type_pointer(t_c_va_list); + } } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 4b9ff809c..8baa92e58 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -388,6 +388,11 @@ BuiltinProc__type_end, BuiltinProc_objc_block, BuiltinProc_objc_super, + BuiltinProc_c_va_start, + BuiltinProc_c_va_end, + BuiltinProc_c_va_copy, + BuiltinProc_c_va_arg, + BuiltinProc_constant_utf16_cstring, BuiltinProc_wasm_memory_grow, @@ -781,6 +786,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_super"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("c_va_start"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("c_va_end"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("c_va_copy"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("c_va_arg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 4bd11b535..abe3d6b5a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -625,6 +625,11 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { continue; } + if (e->flags & EntityFlag_CVarArg) { + GB_ASSERT(i+1 == params->variables.count); + continue; + } + lbArgType *arg_type = &ft->args[param_index]; defer (param_index += 1); @@ -4279,6 +4284,53 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_objc_super: return lb_handle_objc_super(p, expr); + case BuiltinProc_c_va_start: + { + lbValue ptr = lb_build_expr(p, ce->args[0]); + lbValue args = lb_build_expr(p, ce->args[1]); + + gb_unused(args); + + LLVMValueRef va_start_args[] = {ptr.value}; + LLVMTypeRef va_start_types[] = {lb_type(p->module, ptr.type)}; + LLVMValueRef res = lb_call_intrinsic(p, "llvm.va_start", va_start_args, gb_count_of(va_start_args), va_start_types, gb_count_of(va_start_types)); + gb_unused(res); + + return {}; + } break; + case BuiltinProc_c_va_end: + { + lbValue ptr = lb_build_expr(p, ce->args[0]); + + LLVMValueRef va_end_args[] = {ptr.value}; + LLVMTypeRef va_end_types[] = {lb_type(p->module, ptr.type)}; + LLVMValueRef res = lb_call_intrinsic(p, "llvm.va_end", va_end_args, gb_count_of(va_end_args), va_end_types, gb_count_of(va_end_types)); + gb_unused(res); + + return {}; + } break; + case BuiltinProc_c_va_copy: + { + lbValue dst = lb_build_expr(p, ce->args[0]); + lbValue src = lb_build_expr(p, ce->args[1]); + + LLVMValueRef va_end_args[] = {dst.value, src.value}; + LLVMTypeRef va_end_types[] = {lb_type(p->module, dst.type)}; + LLVMValueRef res = lb_call_intrinsic(p, "llvm.va_copy", va_end_args, gb_count_of(va_end_args), va_end_types, gb_count_of(va_end_types)); + gb_unused(res); + + return {}; + } break; + case BuiltinProc_c_va_arg: + { + lbValue ptr = lb_build_expr(p, ce->args[0]); + Type *type = type_of_expr(ce->args[1]); + LLVMValueRef value = LLVMBuildVAArg(p->builder, ptr.value, lb_type(p->module, type), ""); + + return {value, type}; + } break; + + case BuiltinProc_constant_utf16_cstring: { auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) { diff --git a/src/types.cpp b/src/types.cpp index 543658cea..a59d9a9d8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -776,6 +776,11 @@ gb_global Type *t_objc_Class = nullptr; gb_global Type *t_objc_Ivar = nullptr; gb_global Type *t_objc_instancetype = nullptr; // Special distinct variant of t_objc_id used mimic auto-typing of instancetype* in Objective-C + +gb_global Type *t_c_va_list = nullptr; +gb_global Type *t_c_va_list_ptr = nullptr; + + enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered OdinAtomicMemoryOrder_consume = 1, // monotonic