Add intrinsics.c_var_*

This commit is contained in:
gingerBill
2026-04-28 15:00:53 +01:00
parent bb291cfcd0
commit 7b97530443
8 changed files with 282 additions and 9 deletions

View File

@@ -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{}

View File

@@ -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 ---

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<Entity *>(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);
}
}

View File

@@ -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},

View File

@@ -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) {

View File

@@ -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