mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-25 05:09:53 +00:00
Add intrinsics.c_var_*
This commit is contained in:
@@ -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{}
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user