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 From c3bae964d0223121985a52d3c860f879f842f0d3 Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 20:26:22 +0200 Subject: amd64 abi fixes regarding vectors - Fixes the code so SSEUp is grouped/skipped over properly (Fixes #5429) - Fixes f16 vectors using garbage widths, because it would call LLVMGetIntTypeWidth and an f16 is not an int so doesn't have that function --- src/llvm_abi.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index e1cbe7558..c4c5b5a63 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -850,14 +850,18 @@ namespace lbAbiAmd64SysV { } else if (oldv == RegClass_SSEUp) { oldv = RegClass_SSEDv; } else if (is_sse(oldv)) { - i++; - while (i != e && oldv == RegClass_SSEUp) { - i++; + for (i++; i < e; i++) { + RegClass v = (*cls)[cast(isize)i]; + if (v != RegClass_SSEUp) { + break; + } } } else if (oldv == RegClass_X87) { - i++; - while (i != e && oldv == RegClass_X87Up) { - i++; + for (i++; i < e; i++) { + RegClass v = (*cls)[cast(isize)i]; + if (v != RegClass_X87Up) { + break; + } } } else { i++; @@ -1046,10 +1050,9 @@ namespace lbAbiAmd64SysV { i64 elem_sz = lb_sizeof(elem); LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); RegClass reg = RegClass_NoClass; - unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_kind) { - case LLVMIntegerTypeKind: - case LLVMHalfTypeKind: + case LLVMIntegerTypeKind: { + unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_width) { case 8: reg = RegClass_SSEInt8; break; case 16: reg = RegClass_SSEInt16; break; @@ -1065,6 +1068,10 @@ namespace lbAbiAmd64SysV { GB_PANIC("Unhandled integer width for vector type %u", elem_width); } break; + }; + case LLVMHalfTypeKind: + reg = RegClass_SSEInt16; + break; case LLVMFloatTypeKind: reg = RegClass_SSEFv; break; -- cgit v1.2.3 From f32ee28e6d90d15f5c5fb225c19b5292e76302ed Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 20:50:46 +0200 Subject: amd64 support `half` in the abi too --- src/llvm_abi.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c4c5b5a63..3666c74b5 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -527,6 +527,8 @@ namespace lbAbiAmd64SysV { enum RegClass { RegClass_NoClass, RegClass_Int, + RegClass_SSEHs, + RegClass_SSEHv, RegClass_SSEFs, RegClass_SSEFv, RegClass_SSEDs, @@ -545,6 +547,8 @@ namespace lbAbiAmd64SysV { gb_internal bool is_sse(RegClass reg_class) { switch (reg_class) { + case RegClass_SSEHs: + case RegClass_SSEHv: case RegClass_SSEFs: case RegClass_SSEFv: case RegClass_SSEDs: @@ -693,6 +697,8 @@ namespace lbAbiAmd64SysV { case RegClass_Int: needed_int += 1; break; + case RegClass_SSEHs: + case RegClass_SSEHv: case RegClass_SSEFs: case RegClass_SSEFv: case RegClass_SSEDs: @@ -804,6 +810,8 @@ namespace lbAbiAmd64SysV { to_write = RegClass_Memory; } else if (newv == RegClass_SSEUp) { switch (oldv) { + case RegClass_SSEHv: + case RegClass_SSEHs: case RegClass_SSEFv: case RegClass_SSEFs: case RegClass_SSEDv: @@ -918,6 +926,7 @@ namespace lbAbiAmd64SysV { sz -= rs; break; } + case RegClass_SSEHv: case RegClass_SSEFv: case RegClass_SSEDv: case RegClass_SSEInt8: @@ -928,6 +937,10 @@ namespace lbAbiAmd64SysV { unsigned elems_per_word = 0; LLVMTypeRef elem_type = nullptr; switch (reg_class) { + case RegClass_SSEHv: + elems_per_word = 4; + elem_type = LLVMHalfTypeInContext(c); + break; case RegClass_SSEFv: elems_per_word = 2; elem_type = LLVMFloatTypeInContext(c); @@ -962,6 +975,10 @@ namespace lbAbiAmd64SysV { continue; } break; + case RegClass_SSEHs: + array_add(&types, LLVMHalfTypeInContext(c)); + sz -= 2; + break; case RegClass_SSEFs: array_add(&types, LLVMFloatTypeInContext(c)); sz -= 4; @@ -1008,9 +1025,11 @@ namespace lbAbiAmd64SysV { break; } case LLVMPointerTypeKind: - case LLVMHalfTypeKind: unify(cls, ix + off/8, RegClass_Int); break; + case LLVMHalfTypeKind: + unify(cls, ix + off/8, (off%8 == 2) ? RegClass_SSEHv : RegClass_SSEHs); + break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); break; @@ -1070,7 +1089,7 @@ namespace lbAbiAmd64SysV { break; }; case LLVMHalfTypeKind: - reg = RegClass_SSEInt16; + reg = RegClass_SSEHv; break; case LLVMFloatTypeKind: reg = RegClass_SSEFv; -- cgit v1.2.3 From 5527982c20b043ebfc15273a1718343a7f5669b8 Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 22:01:17 +0200 Subject: fix vec offset calculation --- 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 3666c74b5..b6ed395d9 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1028,7 +1028,7 @@ namespace lbAbiAmd64SysV { unify(cls, ix + off/8, RegClass_Int); break; case LLVMHalfTypeKind: - unify(cls, ix + off/8, (off%8 == 2) ? RegClass_SSEHv : RegClass_SSEHs); + unify(cls, ix + off/8, (off%8 == 6) ? RegClass_SSEHv : RegClass_SSEHs); break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); -- cgit v1.2.3 From d0890c2135f236395cf4a412c6e36f48db77cce9 Mon Sep 17 00:00:00 2001 From: Laytan Date: Fri, 1 Aug 2025 19:18:43 +0200 Subject: fix dropping part of particular vecs --- 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 b6ed395d9..8dbb8fa76 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1028,7 +1028,7 @@ namespace lbAbiAmd64SysV { unify(cls, ix + off/8, RegClass_Int); break; case LLVMHalfTypeKind: - unify(cls, ix + off/8, (off%8 == 6) ? RegClass_SSEHv : RegClass_SSEHs); + unify(cls, ix + off/8, (off%8 != 0) ? RegClass_SSEHv : RegClass_SSEHs); break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); -- cgit v1.2.3 From 5d3092bf2d00a46311a5f785658f04c823a9f3fa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 24 Sep 2025 14:27:44 +0100 Subject: Again, better const union stuff --- src/llvm_abi.cpp | 34 ++++---- src/llvm_backend_const.cpp | 192 +++++++++++++-------------------------------- 2 files changed, 72 insertions(+), 154 deletions(-) (limited to 'src/llvm_abi.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 8dbb8fa76..9f2faaa08 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -522,6 +522,23 @@ namespace lbAbiAmd64Win64 { } }; + +gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) { + if (!lb_is_type_kind(type, LLVMStructTypeKind)) { + return false; + } + if (LLVMCountStructElementTypes(type) != 2) { + return false; + } + LLVMTypeRef fields[2] = {}; + LLVMGetStructElementTypes(type, fields); + if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) { + return false; + } + return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8; + +} + // NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything namespace lbAbiAmd64SysV { enum RegClass { @@ -652,23 +669,6 @@ namespace lbAbiAmd64SysV { return false; } - gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) { - if (!lb_is_type_kind(type, LLVMStructTypeKind)) { - return false; - } - if (LLVMCountStructElementTypes(type) != 2) { - return false; - } - LLVMTypeRef fields[2] = {}; - LLVMGetStructElementTypes(type, fields); - if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) { - return false; - } - return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8; - - } - - gb_internal bool is_aggregate(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 39a223027..edcc241dc 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -540,22 +540,38 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, gb_internal Slice lb_construct_const_union_flatten_values(lbModule *m, LLVMValueRef variant_value, Type *variant_type, LLVMTypeRef elem) { LLVMTypeRef llvm_variant_type = lb_type(m, variant_type); LLVMTypeKind variant_kind = LLVMGetTypeKind(llvm_variant_type); + LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); - if (are_types_identical(base_type(variant_type), t_string)) { - LLVMValueRef *elems = temporary_alloc_array(2); - elems[0] = llvm_const_extract_value(m, variant_value, 0); - elems[0] = LLVMConstPtrToInt(elems[0], elem); - - elems[1] = llvm_const_extract_value(m, variant_value, 1); + if (is_type_struct(variant_type)) { + Type *st = base_type(variant_type); + GB_ASSERT(st->kind == Type_Struct); + if (st->Struct.fields.count == 1) { + LLVMValueRef f = llvm_const_extract_value(m, variant_value, 0); + return lb_construct_const_union_flatten_values(m, f, st->Struct.fields[0]->type, elem); + } + } else if (is_llvm_type_slice_like(llvm_variant_type)) { + if (lb_sizeof(elem) == build_context.ptr_size) { + LLVMValueRef *elems = temporary_alloc_array(2); + elems[0] = llvm_const_extract_value(m, variant_value, 0); + elems[0] = LLVMConstPtrToInt(elems[0], elem); - return {elems, 2}; - } + elems[1] = llvm_const_extract_value(m, variant_value, 1); - if (is_type_struct(variant_type)) { - // Type *st = base_type(variant_type); - // if (st->Struct.fields.count == 1) { - // return lb_construct_const_union_flatten_values(m, llvm_const_extract_value(m, variant_value, 0), st->Struct.fields[0]->type, elem); - // } + return {elems, 2}; + } + } else if (is_type_array_like(variant_type)) { + Type *array_elem = base_array_type(variant_type); + isize array_count = get_array_type_count(variant_type); + Slice array = temporary_slice_make(array_count); + for (isize i = 0; i < array_count; i++) { + LLVMValueRef v = llvm_const_extract_value(m, variant_value, 0); + auto res = lb_construct_const_union_flatten_values(m, v, array_elem, elem); + if (res.count != 1) { + return {}; + } + array[i] = res[0]; + } + return array; } else if (variant_kind == LLVMIntegerTypeKind) { if (elem == llvm_variant_type) { LLVMValueRef *elems = temporary_alloc_array(1); @@ -572,7 +588,8 @@ gb_internal Slice lb_construct_const_union_flatten_values(lbModule return {elems, 1}; } } - } else if (!is_type_different_to_arch_endianness(variant_type)) { + } else if (!is_type_different_to_arch_endianness(variant_type) && + elem_kind == LLVMIntegerTypeKind) { switch (variant_kind) { case LLVMHalfTypeKind: { @@ -687,138 +704,39 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari LLVMTypeKind block_kind = LLVMGetTypeKind(block_type); LLVMTypeKind variant_kind = LLVMGetTypeKind(llvm_variant_type); - if (block_size != variant_size) { - if (block_kind == LLVMArrayTypeKind) { - LLVMTypeRef elem = LLVMGetElementType(block_type); - unsigned count = LLVMGetArrayLength(block_type); - - if (variant_kind == LLVMIntegerTypeKind) { - if (elem == llvm_variant_type) { - LLVMValueRef *elems = temporary_alloc_array(count); - elems[0] = variant_value; - for (unsigned j = 1; j < count; j++) { - elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, elems, count); - goto assign_value_wrapped; - } else if (!is_type_different_to_arch_endianness(variant_type)) { - i64 elem_size = lb_sizeof(elem); - i64 variant_size = lb_sizeof(llvm_variant_type); - if (elem_size > variant_size) { - u64 val = LLVMConstIntGetZExtValue(variant_value); - - LLVMValueRef *elems = temporary_alloc_array(count); - elems[0] = LLVMConstInt(elem, val, false); - for (unsigned j = 1; j < count; j++) { - elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, elems, count); - goto assign_value_wrapped; - } - } - } else if (!is_type_different_to_arch_endianness(variant_type)) { - switch (variant_kind) { - case LLVMHalfTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - u16 val = f32_to_f16(cast(f32)res); - - LLVMValueRef *elems = temporary_alloc_array(count); - elems[0] = LLVMConstInt(elem, val, false); - for (unsigned j = 1; j < count; j++) { - elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, elems, count); - goto assign_value_wrapped; - } - break; - case LLVMFloatTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - union { f32 f; u32 i; } val = {}; - val.f = cast(f32)res; - - LLVMValueRef *elems = temporary_alloc_array(count); - elems[0] = LLVMConstInt(elem, val.i, false); - for (unsigned j = 1; j < count; j++) { - elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, elems, count); - goto assign_value_wrapped; - } - break; - case LLVMDoubleTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - union { f64 f; u64 i; } val = {}; - val.f = res; - - LLVMValueRef *elems = temporary_alloc_array(count); - elems[0] = LLVMConstInt(elem, val.i, false); - for (unsigned j = 1; j < count; j++) { - elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, elems, count); - goto assign_value_wrapped; - } - break; - } - } + if (block_kind == LLVMArrayTypeKind) { + LLVMTypeRef elem = LLVMGetElementType(block_type); + unsigned count = LLVMGetArrayLength(block_type); - } else if (block_kind == LLVMIntegerTypeKind && !is_type_different_to_arch_endianness(variant_type)) { - if (variant_kind == LLVMIntegerTypeKind) { - i64 variant_size = lb_sizeof(llvm_variant_type); - if (block_size > variant_size) { - u64 val = LLVMConstIntGetZExtValue(variant_value); - block_value = LLVMConstInt(block_type, val, false); + Slice partial_elems = lb_construct_const_union_flatten_values(m, variant_value, variant_type, elem); + if (partial_elems.count == count) { + block_value = LLVMConstArray(elem, partial_elems.data, count); + goto assign_value_wrapped; + } - goto assign_value_wrapped; - } - } else { - switch (variant_kind) { - case LLVMHalfTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - u16 val = f32_to_f16(cast(f32)res); - - block_value = LLVMConstInt(block_type, val, false); - goto assign_value_wrapped; - } - break; - case LLVMFloatTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - union { f32 f; u32 i; } val = {}; - val.f = cast(f32)res; - - block_value = LLVMConstInt(block_type, val.i, false); - goto assign_value_wrapped; - } - break; - case LLVMDoubleTypeKind: - { - LLVMBool loses = false; - f64 res = LLVMConstRealGetDouble(variant_value, &loses); - union { f64 f; u64 i; } val = {}; - val.f = res; - - block_value = LLVMConstInt(block_type, val.i, false); - goto assign_value_wrapped; - } - break; - } + Slice full_elems = temporary_slice_make(count); + slice_copy(&full_elems, partial_elems); + for (isize j = partial_elems.count; j < count; j++) { + full_elems[j] = LLVMConstNull(elem); + } + block_value = LLVMConstArray(elem, full_elems.data, count); + goto assign_value_wrapped; + + } else if (block_size != variant_size) { + if (block_kind == LLVMIntegerTypeKind && !is_type_different_to_arch_endianness(variant_type)) { + Slice partial_elems = lb_construct_const_union_flatten_values(m, variant_value, variant_type, block_type); + if (partial_elems.count == 1) { + block_value = partial_elems[0]; + goto assign_value_wrapped; } } return nullptr; } if (block_kind == LLVMIntegerTypeKind) { + GB_ASSERT(block_size == variant_size); + switch (variant_kind) { case LLVMHalfTypeKind: case LLVMFloatTypeKind: -- cgit v1.2.3