From d209af50948d3ae78142fda77e5ee654257861a4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 18 Mar 2025 15:39:18 +0000 Subject: Update to LLVM 20.1.0 --- src/llvm_abi.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 0b2bb7956..6d9f6d958 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -256,8 +256,10 @@ gb_internal i64 lb_sizeof(LLVMTypeRef type) { } break; +#if LLVM_VERSION_MAJOR < 20 case LLVMX86_MMXTypeKind: return 8; +#endif case LLVMVectorTypeKind: { LLVMTypeRef elem = OdinLLVMGetVectorElementType(type); @@ -310,8 +312,10 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) { case LLVMArrayTypeKind: return lb_alignof(OdinLLVMGetArrayElementType(type)); +#if LLVM_VERSION_MAJOR < 20 case LLVMX86_MMXTypeKind: return 8; +#endif case LLVMVectorTypeKind: { // TODO(bill): This appears to be correct but LLVM isn't necessarily "great" with regards to documentation -- cgit v1.2.3 From 2d4cb79baa40635cfd1d87fc677879a0925b3abd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Apr 2025 11:26:41 +0100 Subject: Fix SysV ABI bug --- src/llvm_abi.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 9 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 6d9f6d958..c8e1ca764 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -573,7 +573,9 @@ namespace lbAbiAmd64SysV { gb_internal void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off); gb_internal void fixup(LLVMTypeRef t, Array *cls); - gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention); + gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention, + bool is_arg, + i32 *int_regs, i32 *sse_regs); gb_internal Array classify(LLVMTypeRef t); gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes, LLVMTypeRef type); @@ -583,7 +585,9 @@ namespace lbAbiAmd64SysV { } LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); - return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention); + return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention, + false, + nullptr, nullptr); } gb_internal LB_ABI_INFO(abi_info) { @@ -592,10 +596,16 @@ namespace lbAbiAmd64SysV { ft->ctx = c; ft->calling_convention = calling_convention; + i32 int_regs = 6; // rdi, rsi, rdx, rcx, r8, r9 + i32 sse_regs = 8; // xmm0-xmm7 + ft->args = array_make(lb_function_type_args_allocator(), arg_count); for (unsigned i = 0; i < arg_count; i++) { - ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention); + ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention, + true, + &int_regs, &sse_regs); } + ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); return ft; @@ -654,17 +664,79 @@ namespace lbAbiAmd64SysV { } - gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) { + + gb_internal bool is_aggregate(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMStructTypeKind: + if (LLVMCountStructElementTypes(type) == 1) { + return is_aggregate(LLVMStructGetTypeAtIndex(type, 0)); + } + return true; + case LLVMArrayTypeKind: + if (LLVMGetArrayLength(type) == 1) { + return is_aggregate(LLVMGetElementType(type)); + } + return true; + } + return false; + }; + + gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention, + bool is_arg, + i32 *int_regs, i32 *sse_regs) { + auto cls = classify(type); + i32 needed_int = 0; + i32 needed_sse = 0; + for (auto c : cls) { + switch (c) { + case RegClass_Int: + needed_int += 1; + break; + case RegClass_SSEFs: + case RegClass_SSEFv: + case RegClass_SSEDs: + case RegClass_SSEDv: + case RegClass_SSEInt8: + case RegClass_SSEInt16: + case RegClass_SSEInt32: + case RegClass_SSEInt64: + case RegClass_SSEInt128: + case RegClass_SSEUp: + needed_sse += 1; + break; + } + } + + bool ran_out_of_regs = false; + if (int_regs && sse_regs) { + *int_regs -= needed_int; + *sse_regs -= needed_sse; + bool int_ok = *int_regs >= 0; + bool sse_ok = *sse_regs >= 0; + + *int_regs = gb_max(*int_regs, 0); + *sse_regs = gb_max(*sse_regs, 0); + + if ((!int_ok || !sse_ok) && is_aggregate(type)) { + ran_out_of_regs = true; + } + } + if (is_register(type)) { LLVMAttributeRef attribute = nullptr; if (type == LLVMInt1TypeInContext(c)) { attribute = lb_create_enum_attribute(c, "zeroext"); } return lb_arg_type_direct(type, nullptr, nullptr, attribute); - } - - auto cls = classify(type); - if (is_mem_cls(cls, attribute_kind)) { + } else if (ran_out_of_regs) { + if (is_arg) { + return lb_arg_type_indirect_byval(c, type); + } else { + LLVMAttributeRef attribute = lb_create_enum_attribute_with_type(c, "sret", type); + return lb_arg_type_indirect(type, attribute); + } + } else if (is_mem_cls(cls, attribute_kind)) { LLVMAttributeRef attribute = nullptr; if (attribute_kind == Amd64TypeAttribute_ByVal) { if (is_calling_convention_odin(calling_convention)) { @@ -1818,7 +1890,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) { return_type, return_is_defined, ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention), calling_convention, - base_type(original_type)); + base_type(original_type) + ); // NOTE(bill): this is handled here rather than when developing the type in `lb_type_internal_for_procedures_raw` -- cgit v1.2.3 From f9b9e9e7dcbb605bc64bc5af1331855375f58494 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 9 May 2025 22:27:35 +0200 Subject: some ABI fixups and improvements Started with trying to enable asan in the CI for MacOS, noticed it wasn't enabled on the `tests/internal` folder, it came up with a couple of issues with the abi/OdinLLVMBuildTransmute that this also solves. - Looking at clang output for arm64, we should be promoting `{ i64, i32 }` to `{ i64, i64 }` - after doing the previous point, I noticed this is not handled well in OdinLLVMBuildTransmute which was emitting loads and stores into the space of a value that was alignment, asan does not want this, looking at clang output again, a memcpy is the appropriate way of handling this. - Having done this we don't need the hacky "return is packed" set anymore in the amd64 sysv ABI anymore either --- src/llvm_abi.cpp | 42 +++++++++++--------------------- src/llvm_backend_general.cpp | 58 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 31 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c8e1ca764..baad3f873 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -977,7 +977,7 @@ namespace lbAbiAmd64SysV { return types[0]; } - return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0); + return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false); } gb_internal void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off) { @@ -1231,38 +1231,24 @@ namespace lbAbiArm64 { } } else { i64 size = lb_sizeof(return_type); - if (size <= 16) { - LLVMTypeRef cast_type = nullptr; - - if (size == 0) { - cast_type = LLVMStructTypeInContext(c, nullptr, 0, false); - } else if (size <= 8) { - cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8)); - } else { - unsigned count = cast(unsigned)((size+7)/8); - - LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64); - LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count); - - i64 size_copy = size; - for (unsigned i = 0; i < count; i++) { - if (size_copy >= 8) { - types[i] = llvm_i64; - } else { - types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy); - } - size_copy -= 8; - } - GB_ASSERT(size_copy <= 0); - cast_type = LLVMStructTypeInContext(c, types, count, true); - } - return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr); - } else { + if (size > 16) { LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); } + + GB_ASSERT(size <= 16); + LLVMTypeRef cast_type = nullptr; + if (size == 0) { + cast_type = LLVMStructTypeInContext(c, nullptr, 0, false); + } else if (size <= 8) { + cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8)); + } else { + LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64); + cast_type = LLVMArrayType2(llvm_i64, 2); + } + return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr); } } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index c52551b36..504c8234e 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2525,10 +2525,13 @@ general_end:; } } - src_size = align_formula(src_size, src_align); - dst_size = align_formula(dst_size, dst_align); + // NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not + // want you to load/store the space of a value that is there for alignment. +#if 0 + i64 aligned_src_size = align_formula(src_size, src_align); + i64 aligned_dst_size = align_formula(dst_size, dst_align); - if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) { + if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) { LLVMValueRef val_ptr = LLVMGetOperand(val, 0); val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr); @@ -2536,8 +2539,57 @@ general_end:; // LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align)); return loaded_val; + } +#endif + + if (src_size > dst_size) { + GB_ASSERT(p->decl_block != p->curr_block); + // NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want. + + LLVMValueRef val_ptr; + if (LLVMIsALoadInst(val)) { + val_ptr = LLVMGetOperand(val, 0); + } else if (LLVMIsAAllocaInst(val)) { + val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), ""); + } else { + // NOTE(laytan): we need a pointer to memcpy from. + LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align); + val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), ""); + LLVMBuildStore(p->builder, val, val_ptr); + } + + i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); + max_align = gb_max(max_align, 16); + + LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align); + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), ""); + + LLVMTypeRef types[3] = { + lb_type(p->module, t_rawptr), + lb_type(p->module, t_rawptr), + lb_type(p->module, t_int) + }; + + LLVMValueRef args[4] = { + nptr, + val_ptr, + LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0), + LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0), + }; + + lb_call_intrinsic( + p, + "llvm.memcpy.inline", + args, + gb_count_of(args), + types, + gb_count_of(types) + ); + + return OdinLLVMBuildLoad(p, dst_type, ptr); } else { GB_ASSERT(p->decl_block != p->curr_block); + GB_ASSERT(dst_size >= src_size); i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); max_align = gb_max(max_align, 16); -- cgit v1.2.3 From 2d00f8d69d62344e6ff419998899348244e66ed2 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Thu, 15 May 2025 19:20:04 +0200 Subject: fix compat with earlier llvm versions --- src/llvm_abi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index baad3f873..af08722c3 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1246,7 +1246,7 @@ namespace lbAbiArm64 { cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8)); } else { LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64); - cast_type = LLVMArrayType2(llvm_i64, 2); + cast_type = llvm_array_type(llvm_i64, 2); } return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr); } -- cgit v1.2.3 From 3a86bc9c6d3ca51ce02397f73605a7fbd7f3a899 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 20 Jun 2025 21:56:50 +0200 Subject: Fix WASM C ABI for raw unions --- src/llvm_abi.cpp | 70 ++++++++++++++++++++++++++++++++-------- src/llvm_backend_general.cpp | 2 +- src/types.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 15 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index af08722c3..e1cbe7558 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1313,7 +1313,7 @@ namespace lbAbiWasm { registers/arguments if possible rather than by pointer. */ gb_internal Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *original_type); - gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type); + gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type); enum {MAX_DIRECT_STRUCT_SIZE = 32}; @@ -1323,7 +1323,9 @@ namespace lbAbiWasm { ft->ctx = c; ft->calling_convention = calling_convention; ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention, original_type); - ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); + + GB_ASSERT(original_type->kind == Type_Proc); + ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple, original_type->Proc.results); return ft; } @@ -1359,7 +1361,7 @@ namespace lbAbiWasm { return false; } - gb_internal bool type_can_be_direct(LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal bool type_can_be_direct(LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { LLVMTypeKind kind = LLVMGetTypeKind(type); i64 sz = lb_sizeof(type); if (sz == 0) { @@ -1372,9 +1374,21 @@ namespace lbAbiWasm { return false; } else if (kind == LLVMStructTypeKind) { unsigned count = LLVMCountStructElementTypes(type); + + // NOTE(laytan): raw unions are always structs with 1 field in LLVM, need to check our own def. + Type *bt = base_type(original_type); + if (bt->kind == Type_Struct && bt->Struct.is_raw_union) { + count = cast(unsigned)bt->Struct.fields.count; + } + if (count == 1) { - return type_can_be_direct(LLVMStructGetTypeAtIndex(type, 0), calling_convention); + return type_can_be_direct( + LLVMStructGetTypeAtIndex(type, 0), + type_internal_index(original_type, 0), + calling_convention + ); } + } else if (is_basic_register_type(type)) { return true; } @@ -1398,7 +1412,7 @@ namespace lbAbiWasm { return false; } - gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { LLVMTypeKind kind = LLVMGetTypeKind(type); GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind); @@ -1406,15 +1420,15 @@ namespace lbAbiWasm { if (sz == 0) { return lb_arg_type_ignore(type); } - if (type_can_be_direct(type, calling_convention)) { + if (type_can_be_direct(type, original_type, calling_convention)) { return lb_arg_type_direct(type); } return lb_arg_type_indirect(type, nullptr); } - gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { if (build_context.metrics.ptr_size < build_context.metrics.int_size && - type_can_be_direct(type, calling_convention)) { + type_can_be_direct(type, original_type, calling_convention)) { LLVMTypeRef types[2] = { LLVMStructGetTypeAtIndex(type, 0), // ignore padding @@ -1423,7 +1437,7 @@ namespace lbAbiWasm { LLVMTypeRef new_type = LLVMStructTypeInContext(c, types, gb_count_of(types), false); return lb_arg_type_direct(type, new_type, nullptr, nullptr); } else { - return is_struct(c, type, calling_convention); + return is_struct(c, type, original_type, calling_convention); } } @@ -1444,9 +1458,9 @@ namespace lbAbiWasm { LLVMTypeKind kind = LLVMGetTypeKind(t); if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) { if (is_type_slice(ptype) || is_type_string(ptype)) { - args[i] = pseudo_slice(c, t, calling_convention); + args[i] = pseudo_slice(c, t, ptype, calling_convention); } else { - args[i] = is_struct(c, t, calling_convention); + args[i] = is_struct(c, t, ptype, calling_convention); } } else { args[i] = non_struct(c, t, false); @@ -1455,11 +1469,11 @@ namespace lbAbiWasm { return args; } - gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) { + gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type) { if (!return_is_defined) { return lb_arg_type_direct(LLVMVoidTypeInContext(c)); } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) { - if (type_can_be_direct(return_type, ft->calling_convention)) { + if (type_can_be_direct(return_type, original_type, ft->calling_convention)) { return lb_arg_type_direct(return_type); } else if (ft->calling_convention != ProcCC_CDecl) { i64 sz = lb_sizeof(return_type); @@ -1471,7 +1485,35 @@ namespace lbAbiWasm { } } - LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); + // Multiple returns. + if (return_is_tuple) { \ + lbArgType return_arg = {}; + if (lb_is_type_kind(return_type, LLVMStructTypeKind)) { + unsigned field_count = LLVMCountStructElementTypes(return_type); + if (field_count > 1) { + ft->original_arg_count = ft->args.count; + ft->multiple_return_original_type = return_type; + + for (unsigned i = 0; i < field_count-1; i++) { + LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i); + LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0); + lbArgType ret_partial = lb_arg_type_direct(field_pointer_type); + array_add(&ft->args, ret_partial); + } + + return_arg = compute_return_type( + ft, + c, + LLVMStructGetTypeAtIndex(return_type, field_count-1), + true, false, + type_internal_index(original_type, field_count-1) + ); + } + } + if (return_arg.type != nullptr) { + return return_arg; + } + } LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5aaa7f63a..5d6a55973 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2206,7 +2206,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { field_count = 3; } LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count); - fields[0] = LLVMPointerType(lb_type(m, type->Pointer.elem), 0); + fields[0] = LLVMPointerType(lb_type(m, type->SoaPointer.elem), 0); if (bigger_int) { fields[1] = lb_type_padding_filler(m, build_context.ptr_size, build_context.ptr_size); fields[2] = LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.int_size); diff --git a/src/types.cpp b/src/types.cpp index c7573173c..861aa5bf8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4614,6 +4614,82 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ // return type; // } +// Index a type that is internally a struct or array. +gb_internal Type *type_internal_index(Type *t, isize index) { + Type *bt = base_type(t); + if (bt == nullptr) { + return nullptr; + } + + switch (bt->kind) { + case Type_Basic: + { + switch (bt->Basic.kind) { + case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion64: return t_f16; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_string: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_u8_ptr : t_int; + } + case Basic_any: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_typeid; + } + } + } + break; + + case Type_Array: return bt->Array.elem; + case Type_EnumeratedArray: return bt->EnumeratedArray.elem; + case Type_SimdVector: return bt->SimdVector.elem; + case Type_Slice: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_typeid; + } + case Type_DynamicArray: + { + switch (index) { + case 0: return t_rawptr; + case 1: return t_int; + case 2: return t_int; + case 3: return t_allocator; + default: GB_PANIC("invalid raw dynamic array index"); + }; + } + case Type_Struct: + return get_struct_field_type(bt, index); + case Type_Union: + if (index < bt->Union.variants.count) { + return bt->Union.variants[index]; + } + return union_tag_type(bt); + case Type_Tuple: + return bt->Tuple.variables[index]->type; + case Type_Matrix: + return bt->Matrix.elem; + case Type_SoaPointer: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_int; + } + case Type_Map: + return type_internal_index(bt->Map.debug_metadata_type, index); + case Type_BitField: + return type_internal_index(bt->BitField.backing_type, index); + case Type_Generic: + return type_internal_index(bt->Generic.specialized, index); + }; + + GB_PANIC("Unhandled type %s", type_to_string(bt)); +}; + gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false, bool allow_polymorphic=false) { if (type == nullptr) { return gb_string_appendc(str, ""); -- cgit v1.2.3