diff options
| author | gingerBill <bill@gingerbill.org> | 2020-11-12 00:43:49 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2020-11-12 00:43:49 +0000 |
| commit | 70b8b3c7dde193c9c67e4b5fd86fd2f99ee1d987 (patch) | |
| tree | ca915a1da9d46bb4a07915750dc421329e3ae8d0 /src | |
| parent | 6ee4f51670e983347feb4acb15f681d56117c137 (diff) | |
Update LLVM backend to begin work on a generic ABI system
Diffstat (limited to 'src')
| -rw-r--r-- | src/llvm_abi.cpp | 762 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 693 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 3 | ||||
| -rw-r--r-- | src/types.cpp | 10 |
4 files changed, 1307 insertions, 161 deletions
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp new file mode 100644 index 000000000..2bede5822 --- /dev/null +++ b/src/llvm_abi.cpp @@ -0,0 +1,762 @@ +enum lbArgKind { + lbArg_Direct, + lbArg_Indirect, + lbArg_Ignore, +}; + +struct lbArgType { + lbArgKind kind; + LLVMTypeRef type; + LLVMTypeRef cast_type; // Optional + LLVMTypeRef pad_type; // Optional + LLVMAttributeRef attribute; // Optional +}; + +lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) { + return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr}; +} +lbArgType lb_arg_type_direct(LLVMTypeRef type) { + return lb_arg_type_direct(type, nullptr, nullptr, nullptr); +} + +lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) { + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr}; +} + +lbArgType lb_arg_type_ignore(LLVMTypeRef type) { + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr}; +} + +struct lbFunctionType { + LLVMContextRef ctx; + ProcCallingConvention calling_convention; + Array<lbArgType> args; + lbArgType ret; +}; + + +bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) { + return LLVMGetTypeKind(type) == kind; +} + +LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) { + unsigned arg_count = cast(unsigned)ft->args.count; + unsigned offset = 0; + + LLVMTypeRef ret = nullptr; + if (ft->ret.kind == lbArg_Direct) { + if (ft->ret.cast_type != nullptr) { + ret = ft->ret.cast_type; + } else { + ret = ft->ret.type; + } + } else if (ft->ret.kind == lbArg_Indirect) { + offset += 1; + ret = LLVMVoidTypeInContext(ft->ctx); + } else if (ft->ret.kind == lbArg_Ignore) { + ret = LLVMVoidTypeInContext(ft->ctx); + } + GB_ASSERT_MSG(ret != nullptr, "%d", ft->ret.kind); + + unsigned maximum_arg_count = offset+arg_count; + LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count); + if (offset == 1) { + GB_ASSERT(ft->ret.kind == lbArg_Indirect); + args[0] = ft->ret.type; + } + + unsigned arg_index = offset; + for (unsigned i = 0; i < arg_count; i++) { + lbArgType *arg = &ft->args[i]; + if (arg->kind == lbArg_Direct) { + LLVMTypeRef arg_type = nullptr; + if (ft->args[i].cast_type != nullptr) { + arg_type = arg->cast_type; + } else { + arg_type = arg->type; + } + args[arg_index++] = arg_type; + } else if (arg->kind == lbArg_Indirect) { + GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind)); + args[arg_index++] = LLVMPointerType(arg->type, 0); + } else if (arg->kind == lbArg_Ignore) { + // ignore + } + } + unsigned total_arg_count = arg_index; + LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg); + return LLVMPointerType(func_type, 0); +} + + +void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) { + if (ft == nullptr) { + return; + } + unsigned arg_count = cast(unsigned)ft->args.count; + unsigned offset = 0; + if (ft->ret.kind == lbArg_Indirect) { + offset += 1; + } + + unsigned arg_index = offset; + for (unsigned i = 0; i < arg_count; i++) { + lbArgType *arg = &ft->args[i]; + if (arg->kind == lbArg_Ignore) { + continue; + } + + if (arg->attribute) { + LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute); + } + + arg_index++; + } + + if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) { + LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute); + } + + lbCallingConventionKind cc_kind = lbCallingConvention_C; + // TODO(bill): Clean up this logic + if (build_context.metrics.os != TargetOs_js) { + cc_kind = lb_calling_convention_map[calling_convention]; + } + LLVMSetFunctionCallConv(fn, cc_kind); + if (calling_convention == ProcCC_Odin) { + unsigned context_index = offset+arg_count; + LLVMContextRef c = ft->ctx; + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "noalias", true)); + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nonnull", true)); + LLVMAddAttributeAtIndex(fn, context_index, lb_create_enum_attribute(c, "nocapture", true)); + } + +} + +i64 lb_sizeof(LLVMTypeRef type); +i64 lb_alignof(LLVMTypeRef type); + +i64 lb_sizeof(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return 0; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + return (w + 7)/8; + } + case LLVMFloatTypeKind: + return 4; + case LLVMDoubleTypeKind: + return 8; + case LLVMPointerTypeKind: + return build_context.word_size; + case LLVMStructTypeKind: + { + unsigned field_count = LLVMCountStructElementTypes(type); + i64 offset = 0; + if (LLVMIsPackedStruct(type)) { + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + offset += lb_sizeof(field); + } + } else { + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + i64 align = lb_alignof(field); + offset = align_formula(offset, align); + offset += lb_sizeof(field); + } + } + offset = align_formula(offset, lb_alignof(type)); + return offset; + } + break; + case LLVMArrayTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return size; + } + break; + + case LLVMX86_MMXTypeKind: + return 8; + case LLVMVectorTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return gb_clamp(next_pow2(size), 1, build_context.max_align); + } + + } + GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMSizeOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return cast(i64)LLVMConstIntGetSExtValue(v); + return 0; +} + +i64 lb_alignof(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return 1; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + return gb_clamp((w + 7)/8, 1, build_context.max_align); + } + case LLVMFloatTypeKind: + return 4; + case LLVMDoubleTypeKind: + return 8; + case LLVMPointerTypeKind: + return build_context.word_size; + case LLVMStructTypeKind: + { + if (LLVMIsPackedStruct(type)) { + return 1; + } else { + unsigned field_count = LLVMCountStructElementTypes(type); + i64 max_align = 1; + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + i64 field_align = lb_alignof(field); + max_align = gb_max(max_align, field_align); + } + return max_align; + } + } + break; + case LLVMArrayTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return size; + } + break; + + case LLVMX86_MMXTypeKind: + return 8; + case LLVMVectorTypeKind: + { + LLVMTypeRef elem = LLVMGetElementType(type); + i64 elem_size = lb_sizeof(elem); + i64 count = LLVMGetVectorSize(type); + i64 size = count * elem_size; + return gb_clamp(next_pow2(size), 1, build_context.max_align); + } + + } + GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMAlignOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return LLVMConstIntGetSExtValue(v); + return 1; +} + +Type *lb_abi_to_odin_type(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMVoidTypeKind: + return nullptr; + case LLVMIntegerTypeKind: + { + unsigned w = LLVMGetIntTypeWidth(type); + if (w == 1) { + return t_llvm_bool; + } + unsigned bytes = (w + 7)/8; + switch (bytes) { + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + case 16: return t_u128; + } + GB_PANIC("Unhandled integer type"); + } + case LLVMFloatTypeKind: + return t_f32; + case LLVMDoubleTypeKind: + return t_f64; + case LLVMPointerTypeKind: + return t_rawptr; + case LLVMStructTypeKind: + { + GB_PANIC("HERE"); + } + break; + case LLVMArrayTypeKind: + { + + i64 count = LLVMGetVectorSize(type); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + return alloc_type_array(elem, count); + } + break; + + case LLVMX86_MMXTypeKind: + return t_vector_x86_mmx; + case LLVMVectorTypeKind: + { + i64 count = LLVMGetVectorSize(type); + Type *elem = lb_abi_to_odin_type(LLVMGetElementType(type)); + return alloc_type_simd_vector(count, elem); + } + + } + GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type)); + + // LLVMValueRef v = LLVMSizeOf(type); + // GB_ASSERT(LLVMIsConstant(v)); + // return cast(i64)LLVMConstIntGetSExtValue(v); + return 0; +} + + + +#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) +typedef LB_ABI_INFO(lbAbiInfoType); + + +// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything +namespace lbAbi386 { + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { + if (build_context.metrics.os == TargetOs_windows && + build_context.word_size == 8 && + lb_is_type_kind(type, LLVMIntegerTypeKind) && + lb_sizeof(type) == 16) { + + LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2); + return lb_arg_type_direct(type, cast_type, nullptr, nullptr); + } + + + + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + // attr = lb_create_enum_attribute(c, "zext", true); + // return lb_arg_type_direct(type, i1, nullptr, attr); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make<lbArgType>(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); + } + } else { + args[i] = non_struct(c, t); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + 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)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + } + return non_struct(c, return_type); + } +}; + +namespace lbAbiAmd64Win64 { + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make<lbArgType>(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + switch (sz) { + case 1: + case 2: + case 4: + case 8: + args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr); + break; + default: + args[i] = lb_arg_type_indirect(t, nullptr); + break; + } + } else { + args[i] = lbAbi386::non_struct(c, t); + } + } + return args; + } +}; + +// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything +namespace lbAbiAmd64SysV { + enum RegClass { + RegClass_NoClass, + RegClass_Int, + RegClass_SSEFs, + RegClass_SSEFv, + RegClass_SSEDs, + RegClass_SSEDv, + RegClass_SSEInt, + RegClass_SSEUp, + RegClass_X87, + RegClass_X87Up, + RegClass_ComplexX87, + RegClass_Memory, + }; + + bool is_sse(RegClass reg_class) { + switch (reg_class) { + case RegClass_SSEFs: + case RegClass_SSEFv: + case RegClass_SSEDv: + return true; + } + return false; + } + + void all_mem(Array<RegClass> *cs) { + for_array(i, *cs) { + (*cs)[i] = RegClass_Memory; + } + } + + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off); + void fixup(LLVMTypeRef t, Array<RegClass> *cls); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + // TODO(bill): THIS IS VERY VERY WRONG! + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) { + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + attr = lb_create_enum_attribute(c, "zext", true); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array<RegClass> classify(LLVMTypeRef t) { + i64 sz = lb_sizeof(t); + i64 words = (sz + 7)/8; + auto reg_classes = array_make<RegClass>(heap_allocator(), cast(isize)words); + if (words > 4) { + all_mem(®_classes); + } else { + classify_with(t, ®_classes, 0, 0); + fixup(t, ®_classes); + } + return reg_classes; + } + + void classify_struct(LLVMTypeRef *fields, unsigned field_count, Array<RegClass> *cls, i64 i, i64 off, LLVMBool packed) { + i64 field_off = off; + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef t = fields[i]; + if (!packed) { + field_off = align_formula(field_off, lb_alignof(t)); + } + classify_with(t, cls, i, field_off); + field_off += lb_sizeof(t); + } + } + + void unify(Array<RegClass> *cls, i64 i, RegClass newv) { + RegClass &oldv = (*cls)[i]; + if (oldv == newv) { + return; + } else if (oldv == RegClass_NoClass) { + oldv = newv; + } else if (newv == RegClass_NoClass) { + return; + } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { + return; + } else if (oldv == RegClass_Int || newv == RegClass_Int) { + return; + } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || + newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { + oldv = RegClass_Memory; + } else { + oldv = newv; + } + } + + void fixup(LLVMTypeRef t, Array<RegClass> *cls) { + i64 i = 0; + i64 e = cls->count; + if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) { + RegClass &oldv = (*cls)[i]; + if (is_sse(oldv)) { + for (i++; i < e; i++) { + if (oldv != RegClass_SSEUp) { + all_mem(cls); + return; + } + } + } else { + all_mem(cls); + return; + } + } else { + while (i < e) { + RegClass &oldv = (*cls)[i]; + if (oldv == RegClass_Memory) { + all_mem(cls); + return; + } else if (oldv == RegClass_X87Up) { + // NOTE(bill): Darwin + all_mem(cls); + return; + } else if (oldv == RegClass_SSEUp) { + oldv = RegClass_SSEDv; + } else if (is_sse(oldv)) { + i++; + while (i != e && oldv == RegClass_SSEUp) { + i++; + } + } else if (oldv == RegClass_X87) { + i++; + while (i != e && oldv == RegClass_X87Up) { + i++; + } + } else { + i++; + } + } + } + } + + unsigned llvec_len(Array<RegClass> const ®_classes) { + unsigned len = 1; + for_array(i, reg_classes) { + if (reg_classes[i] != RegClass_SSEUp) { + break; + } + len++; + } + return len; + } + + + LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes) {; + auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count); + for_array(i, reg_classes) { + switch (reg_classes[i]) { + case RegClass_Int: + array_add(&types, LLVMIntTypeInContext(c, 64)); + break; + case RegClass_SSEFv: + { + unsigned vec_len = llvec_len(array_slice(reg_classes, i+1, reg_classes.count)); + LLVMTypeRef vec_type = LLVMVectorType(LLVMFloatTypeInContext(c), vec_len); + array_add(&types, vec_type); + i += vec_len; + continue; + } + break; + case RegClass_SSEFs: + array_add(&types, LLVMFloatTypeInContext(c)); + break; + case RegClass_SSEDs: + array_add(&types, LLVMDoubleTypeInContext(c)); + break; + default: + GB_PANIC("Unhandled RegClass"); + } + } + + return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false); + } + + void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) { + i64 t_align = lb_alignof(t); + i64 t_size = lb_sizeof(t); + + i64 mis_align = off % t_align; + if (mis_align != 0) { + i64 e = (off + t_size + 7) / 8; + for (i64 i = off / 8; i < e; i++) { + unify(cls, ix+1, RegClass_Memory); + } + return; + } + + switch (LLVMGetTypeKind(t)) { + case LLVMIntegerTypeKind: + case LLVMPointerTypeKind: + unify(cls, ix+off / 8, RegClass_Int); + break; + case LLVMFloatTypeKind: + unify(cls, ix+off / 8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); + break; + case LLVMDoubleTypeKind: + unify(cls, ix+off / 8, RegClass_SSEDs); + break; + case LLVMStructTypeKind: + { + unsigned field_count = LLVMCountStructElementTypes(t); + LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count); // HACK(bill): LEAK + defer (gb_free(heap_allocator(), fields)); + + LLVMGetStructElementTypes(t, fields); + + classify_struct(fields, field_count, cls, ix, off, LLVMIsPackedStruct(t)); + } + break; + case LLVMArrayTypeKind: + { + i64 len = LLVMGetArrayLength(t); + LLVMTypeRef elem = LLVMGetElementType(t); + i64 elem_sz = lb_sizeof(elem); + for (i64 i = 0; i < len; i++) { + classify_with(elem, cls, ix, off + i*elem_sz); + } + } + break; + default: + GB_PANIC("Unhandled type"); + break; + } + } + + Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make<lbArgType>(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + if (kind == LLVMStructTypeKind) { + i64 sz = lb_sizeof(t); + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true)); + } + } else { + args[i] = non_struct(c, t); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + return lb_arg_type_indirect(LLVMPointerType(return_type, 0), lb_create_enum_attribute(c, "sret", true)); + } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) { + return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr); + } + return non_struct(c, return_type); + } +}; + + + + +LB_ABI_INFO(lb_get_abi_info) { + switch (calling_convention) { + case ProcCC_None: + case ProcCC_PureNone: + case ProcCC_InlineAsm: + { + lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = array_make<lbArgType>(heap_allocator(), arg_count); + for (unsigned i = 0; i < arg_count; i++) { + ft->args[i] = lb_arg_type_direct(arg_types[i]); + } + if (return_is_defined) { + ft->ret = lb_arg_type_direct(return_type); + } else { + ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } + ft->calling_convention = calling_convention; + return ft; + } + } + + if (build_context.metrics.arch == TargetArch_amd64) { + if (build_context.metrics.os == TargetOs_windows) { + return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else { + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } + } else if (build_context.metrics.arch == TargetArch_386) { + return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else if (build_context.metrics.arch == TargetArch_wasm32) { + return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } + GB_PANIC("Unsupported ABI"); + return {}; +} diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index aab2248d9..a01dd7ede 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,4 +1,11 @@ #include "llvm_backend.hpp" +#include "llvm_abi.cpp" + +#ifdef USE_NEW_LLVM_ABI_SYSTEM +#define USE_LLVM_ABI 1 +#else +#define USE_LLVM_ABI 0 +#endif gb_global lbAddr lb_global_type_info_data = {}; gb_global lbAddr lb_global_type_info_member_types = {}; @@ -459,7 +466,11 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(value.value != nullptr); value = lb_emit_conv(p, value, lb_addr_type(addr)); - LLVMBuildStore(p->builder, value.value, addr.addr.value); + if (USE_LLVM_ABI) { + lb_emit_store(p, addr.addr, value); + } else { + LLVMBuildStore(p->builder, value.value, addr.addr.value); + } } void lb_const_store(lbValue ptr, lbValue value) { @@ -480,11 +491,25 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { Type *ca = core_type(a); if (ca->kind == Type_Basic) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); - } else { - GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); } - LLVMBuildStore(p->builder, value.value, ptr.value); + if (USE_LLVM_ABI && is_type_proc(a)) { + // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be + // stored as regular pointer with no procedure information + + LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value)); + LLVMValueRef v = LLVMBuildPointerCast(p->builder, value.value, src_t, ""); + LLVMBuildStore(p->builder, v, ptr.value); + } else { + Type *ca = core_type(a); + if (ca->kind == Type_Basic) { + GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); + } else { + GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type)); + } + + LLVMBuildStore(p->builder, value.value, ptr.value); + } } lbValue lb_emit_load(lbProcedure *p, lbValue value) { @@ -1132,7 +1157,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { switch (base->kind) { case Type_Basic: - return lb_type(m, base); + return lb_type_internal(m, base); case Type_Named: case Type_Generic: @@ -1141,7 +1166,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { break; case Type_Opaque: - return lb_type(m, base->Opaque.elem); + return lb_type_internal(m, base->Opaque.elem); case Type_Pointer: case Type_Array: @@ -1152,14 +1177,14 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_Enum: case Type_BitSet: case Type_SimdVector: - return lb_type(m, base); + return lb_type_internal(m, base); // TODO(bill): Deal with this correctly. Can this be named? case Type_Proc: - return lb_type(m, base); + return lb_type_internal(m, base); case Type_Tuple: - return lb_type(m, base); + return lb_type_internal(m, base); } LLVMTypeRef *found = map_get(&m->types, hash_type(base)); @@ -1196,7 +1221,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } - return lb_type(m, base); + return lb_type_internal(m, base); } case Type_Pointer: @@ -1320,16 +1345,83 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { for_array(i, type->Tuple.variables) { Entity *field = type->Tuple.variables[i]; - fields[i] = lb_type(m, field->type); + + LLVMTypeRef param_type = nullptr; + param_type = lb_type(m, field->type); + + fields[i] = param_type; } return LLVMStructTypeInContext(ctx, fields, field_count, type->Tuple.is_packed); } case Type_Proc: - { - set_procedure_abi_types(heap_allocator(), type); + if (USE_LLVM_ABI) { + if (m->internal_type_level > 1) { + return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); + } else { + unsigned param_count = 0; + if (type->Proc.calling_convention == ProcCC_Odin) { + param_count += 1; + } + + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + param_count += 1; + } + } + + LLVMTypeRef ret = nullptr; + LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); + if (type->Proc.result_count != 0) { + Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); + ret = lb_type(m, type->Proc.results); + if (ret != nullptr) { + if (is_calling_convention_none(type->Proc.calling_convention) && + is_type_boolean(single_ret) && + type_size_of(single_ret) <= 1) { + ret = LLVMInt1TypeInContext(m->ctx); + } + } + } + isize param_index = 0; + if (type->Proc.param_count != 0) { + GB_ASSERT(type->Proc.params->kind == Type_Tuple); + for_array(i, type->Proc.params->Tuple.variables) { + Entity *e = type->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + LLVMTypeRef param_type = nullptr; + if (is_calling_convention_none(type->Proc.calling_convention) && + is_type_boolean(e->type) && + type_size_of(e->type) <= 1) { + param_type = LLVMInt1TypeInContext(m->ctx); + } else { + param_type = lb_type(m, e->type); + } + params[param_index++] = param_type; + } + } + if (param_index < param_count) { + params[param_index++] = lb_type(m, t_context_ptr); + } + GB_ASSERT(param_index == param_count); + + + lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); + map_set(&m->function_type_map, hash_type(type), ft); + return lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); + } + } else { + set_procedure_abi_types(heap_allocator(), type); LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx); if (type->Proc.return_by_pointer) { // Void @@ -1452,10 +1544,14 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) { return *found; } - LLVMTypeRef llvm_type = lb_type_internal(m, type); - - map_set(&m->types, hash_type(type), llvm_type); + LLVMTypeRef llvm_type = nullptr; + m->internal_type_level += 1; + llvm_type = lb_type_internal(m, type); + m->internal_type_level -= 1; + if (USE_LLVM_ABI && m->internal_type_level == 0) { + map_set(&m->types, hash_type(type), llvm_type); + } return llvm_type; } @@ -1999,7 +2095,7 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { unsigned kind = LLVMGetEnumAttributeKindForName(name, gb_strlen(name)); - GB_ASSERT(kind != 0); + GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name); return LLVMCreateEnumAttribute(ctx, kind, value); } @@ -2076,12 +2172,26 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { p->value = LLVMAddFunction(m->mod, c_link_name, func_type); - lbCallingConventionKind cc_kind = lbCallingConvention_C; - // TODO(bill): Clean up this logic - if (build_context.metrics.os != TargetOs_js) { - cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + lbFunctionType **ft_found = map_get(&m->function_type_map, hash_type(p->type)); + if (USE_LLVM_ABI && ft_found) { + lbFunctionType *abi_ft = *ft_found; + p->abi_function_type = abi_ft; + lb_add_function_type_attributes(p->value, abi_ft, abi_ft->calling_convention); + } else { + lbCallingConventionKind cc_kind = lbCallingConvention_C; + // TODO(bill): Clean up this logic + if (build_context.metrics.os != TargetOs_js) { + cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + } + LLVMSetFunctionCallConv(p->value, cc_kind); } - LLVMSetFunctionCallConv(p->value, cc_kind); + + // lbCallingConventionKind cc_kind = lbCallingConvention_C; + // // TODO(bill): Clean up this logic + // if (build_context.metrics.os != TargetOs_js) { + // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; + // } + // LLVMSetFunctionCallConv(p->value, cc_kind); lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); @@ -2116,8 +2226,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { // NOTE(bill): offset==0 is the return value isize offset = 1; if (pt->Proc.return_by_pointer) { - lb_add_proc_attribute_at_index(p, 1, "sret"); - lb_add_proc_attribute_at_index(p, 1, "noalias"); + if (!USE_LLVM_ABI) { + lb_add_proc_attribute_at_index(p, 1, "sret"); + lb_add_proc_attribute_at_index(p, 1, "noalias"); + } offset = 2; } @@ -2150,7 +2262,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } } - if (pt->Proc.calling_convention == ProcCC_Odin) { + if (!USE_LLVM_ABI && pt->Proc.calling_convention == ProcCC_Odin) { lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull"); lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); @@ -2242,7 +2354,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type } isize parameter_index = 0; - if (pt->Proc.param_count) { + if (!USE_LLVM_ABI && pt->Proc.param_count) { TypeTuple *params = &pt->Proc.params->Tuple; for (isize i = 0; i < pt->Proc.param_count; i++) { Entity *e = params->variables[i]; @@ -2397,6 +2509,47 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { p->curr_block = b; } +LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) { + LLVMTypeRef src_type = LLVMTypeOf(val); + GB_ASSERT(lb_sizeof(src_type) == lb_sizeof(dst_type)); + + LLVMTypeKind src_kind = LLVMGetTypeKind(src_type); + LLVMTypeKind dst_kind = LLVMGetTypeKind(dst_type); + if (src_kind == dst_kind) { + if (src_kind == LLVMPointerTypeKind) { + return LLVMBuildPointerCast(p->builder, val, dst_type, ""); + } else if (src_kind != LLVMStructTypeKind) { + return LLVMBuildBitCast(p->builder, val, dst_type, ""); + } + } else { + if (src_kind == LLVMPointerTypeKind && dst_kind == LLVMIntegerTypeKind) { + return LLVMBuildPtrToInt(p->builder, val, dst_type, ""); + } else if (src_kind == LLVMIntegerTypeKind && dst_kind == LLVMPointerTypeKind) { + return LLVMBuildIntToPtr(p->builder, val, dst_type, ""); + } + } + + if (LLVMIsALoadInst(val)) { + LLVMValueRef val_ptr = LLVMGetOperand(val, 0); + val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); + return LLVMBuildLoad(p->builder, val_ptr, ""); + } else { + GB_ASSERT(p->decl_block != p->curr_block); + LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); + + LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, ""); + LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); + max_align = gb_max(max_align, 4); + LLVMSetAlignment(ptr, cast(unsigned)max_align); + + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); + LLVMBuildStore(p->builder, val, nptr); + + return LLVMBuildLoad(p->builder, ptr, ""); + } +} + void lb_begin_procedure_body(lbProcedure *p) { DeclInfo *decl = decl_info_of_entity(p->entity); @@ -2429,82 +2582,190 @@ void lb_begin_procedure_body(lbProcedure *p) { GB_ASSERT(p->type != nullptr); - i32 parameter_index = 0; + if (p->abi_function_type) { + lbFunctionType *ft = p->abi_function_type; + unsigned param_offset = 0; - lbValue return_ptr_value = {}; - if (p->type->Proc.return_by_pointer) { - // NOTE(bill): this must be parameter 0 - Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); - e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + lbValue return_ptr_value = {}; + if (ft->ret.kind == lbArg_Indirect) { + // NOTE(bill): this must be parameter 0 + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); + Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; - return_ptr_value.value = LLVMGetParam(p->value, 0); - return_ptr_value.type = ptr_type; - p->return_ptr = lb_addr(return_ptr_value); + return_ptr_value.value = LLVMGetParam(p->value, 0); + return_ptr_value.type = ptr_type; + p->return_ptr = lb_addr(return_ptr_value); - lb_add_entity(p->module, e, return_ptr_value); + lb_add_entity(p->module, e, return_ptr_value); - parameter_index += 1; - } + param_offset += 1; + } - if (p->type->Proc.params != nullptr) { - TypeTuple *params = &p->type->Proc.params->Tuple; - auto abi_types = p->type->Proc.abi_compat_params; + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - Type *abi_type = e->type; - if (abi_types.count > 0) { - abi_type = abi_types[i]; - } - if (e->token.string != "") { - lb_add_param(p, e, nullptr, abi_type, parameter_index); - } - if (is_type_tuple(abi_type)) { - parameter_index += cast(i32)abi_type->Tuple.variables.count; - } else { - parameter_index += 1; + unsigned param_index = 0; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + lbArgType *arg_type = &ft->args[param_index]; + if (arg_type->kind == lbArg_Ignore) { + continue; + } else if (arg_type->kind == lbArg_Direct) { + lbParamPasskind kind = lbParamPass_Value; + LLVMTypeRef param_type = lb_type(p->module, e->type); + if (param_type != arg_type->type) { + kind = lbParamPass_BitCast; + } + LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); + + if (USE_LLVM_ABI && LLVMTypeOf(value) == LLVMInt1TypeInContext(p->module->ctx)) { + value = LLVMBuildZExtOrBitCast(p->builder, value, param_type, ""); + } else { + value = OdinLLVMBuildTransmute(p, value, param_type); + } + + lbValue param = {}; + param.value = value; + param.type = e->type; + array_add(&p->params, param); + + if (e->token.string.len != 0) { + lbAddr l = lb_add_local(p, e->type, e, false, param_index); + lb_addr_store(p, l, param); + } + + param_index += 1; + } else if (arg_type->kind == lbArg_Indirect) { + LLVMValueRef value_ptr = LLVMGetParam(p->value, param_offset+param_index); + LLVMValueRef value = LLVMBuildLoad(p->builder, value_ptr, ""); + + lbValue param = {}; + param.value = value; + param.type = e->type; + array_add(&p->params, param); + + lbValue ptr = {}; + ptr.value = value_ptr; + ptr.type = alloc_type_pointer(e->type); + + lb_add_entity(p->module, e, ptr); + param_index += 1; + } } } - } + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; - if (p->type->Proc.has_named_results) { - GB_ASSERT(p->type->Proc.result_count > 0); - TypeTuple *results = &p->type->Proc.results->Tuple; + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); - for_array(i, results->variables) { - Entity *e = results->variables[i]; - GB_ASSERT(e->kind == Entity_Variable); + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); - if (e->token.string != "") { - GB_ASSERT(!is_blank_ident(e->token)); + lbAddr res = {}; + if (return_ptr_value.value) { + lbValue ptr = return_ptr_value; + if (results->variables.count != 1) { + ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + } - lbAddr res = {}; - if (p->type->Proc.return_by_pointer) { - lbValue ptr = return_ptr_value; - if (results->variables.count != 1) { - ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + res = lb_addr(ptr); + lb_add_entity(p->module, e, ptr); + } else { + res = lb_add_local(p, e->type, e); } - res = lb_addr(ptr); - lb_add_entity(p->module, e, ptr); + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); + lb_addr_store(p, res, c); + } + } + } + } + } else { + i32 parameter_index = 0; + lbValue return_ptr_value = {}; + if (p->type->Proc.return_by_pointer) { + // NOTE(bill): this must be parameter 0 + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); + Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + + return_ptr_value.value = LLVMGetParam(p->value, 0); + return_ptr_value.type = ptr_type; + p->return_ptr = lb_addr(return_ptr_value); + + lb_add_entity(p->module, e, return_ptr_value); + + parameter_index += 1; + } + + if (p->type->Proc.params != nullptr) { + TypeTuple *params = &p->type->Proc.params->Tuple; + auto abi_types = p->type->Proc.abi_compat_params; + + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + Type *abi_type = e->type; + if (abi_types.count > 0) { + abi_type = abi_types[i]; + } + if (e->token.string != "") { + lb_add_param(p, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; } else { - res = lb_add_local(p, e->type, e); + parameter_index += 1; } + } + } + + + if (p->type->Proc.has_named_results) { + GB_ASSERT(p->type->Proc.result_count > 0); + TypeTuple *results = &p->type->Proc.results->Tuple; + + for_array(i, results->variables) { + Entity *e = results->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + + lbAddr res = {}; + if (p->type->Proc.return_by_pointer) { + lbValue ptr = return_ptr_value; + if (results->variables.count != 1) { + ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); + } - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); - lb_addr_store(p, res, c); + res = lb_addr(ptr); + lb_add_entity(p->module, e, ptr); + } else { + res = lb_add_local(p, e->type, e); + } + + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); + lb_addr_store(p, res, c); + } } } } } - if (p->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; @@ -6997,15 +7258,33 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, if (context_ptr.addr.value != nullptr) { args[arg_index++] = context_ptr.addr.value; } + LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); + GB_ASSERT(curr_block != p->decl_block->block); - LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder); - GB_ASSERT(curr_block != p->decl_block->block); + if (USE_LLVM_ABI) { - LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");; - lbValue res = {}; - res.value = ret; - res.type = abi_rt; - return res; + LLVMTypeRef ftp = lb_type(p->module, value.type); + LLVMTypeRef ft = LLVMGetElementType(ftp); + LLVMValueRef fn = value.value; + if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) { + fn = LLVMBuildPointerCast(p->builder, fn, ftp, ""); + } + LLVMTypeRef fnp = LLVMGetElementType(LLVMTypeOf(fn)); + GB_ASSERT_MSG(lb_is_type_kind(fnp, LLVMFunctionTypeKind), "%s", LLVMPrintTypeToString(fnp)); + + LLVMValueRef ret = LLVMBuildCall2(p->builder, ft, fn, args, arg_count, "");; + lbValue res = {}; + res.value = ret; + res.type = abi_rt; + return res; + } else { + + LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");; + lbValue res = {}; + res.value = ret; + res.type = abi_rt; + return res; + } } lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) { @@ -7061,95 +7340,186 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } + lbValue result = {}; + auto processed_args = array_make<lbValue>(heap_allocator(), 0, args.count); - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->Proc.params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - // array_add(&processed_args, args[i]); - continue; + if (USE_LLVM_ABI) { + lbFunctionType **ft_found = nullptr; + ft_found = map_get(&m->function_type_map, hash_type(pt)); + if (!ft_found) { + LLVMTypeRef llvm_proc_type = lb_type(p->module, pt); + ft_found = map_get(&m->function_type_map, hash_type(pt)); } - GB_ASSERT(e->flags & EntityFlag_Param); - - Type *original_type = e->type; - Type *new_type = pt->Proc.abi_compat_params[i]; - Type *arg_type = args[i].type; - - if (are_types_identical(arg_type, new_type)) { - // NOTE(bill): Done - array_add(&processed_args, args[i]); - } else if (!are_types_identical(original_type, new_type)) { - if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { - Type *av = core_type(type_deref(new_type)); - if (are_types_identical(av, core_type(original_type))) { - if (e->flags&EntityFlag_ImplicitReference) { - array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i])); - } else if (!is_type_pointer(arg_type)) { - array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16)); - } - } else { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + GB_ASSERT(ft_found != nullptr); + + lbFunctionType *abi_ft = *ft_found; + bool return_by_pointer = abi_ft->ret.kind == lbArg_Indirect; + + unsigned param_index = 0; + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + GB_ASSERT(e->flags & EntityFlag_Param); + + Type *original_type = e->type; + lbArgType *arg = &abi_ft->args[param_index]; + if (arg->kind == lbArg_Ignore) { + continue; + } + + lbValue x = lb_emit_conv(p, args[i], original_type); + LLVMTypeRef xt = lb_type(p->module, x.type); + + if (arg->kind == lbArg_Direct) { + LLVMTypeRef abi_type = arg->cast_type; + if (!abi_type) { + abi_type = arg->type; } - } else if (new_type == t_llvm_bool) { - array_add(&processed_args, lb_emit_conv(p, args[i], new_type)); - } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_simd_vector(new_type)) { - array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); - } else if (is_type_tuple(new_type)) { - Type *abi_type = pt->Proc.abi_compat_params[i]; - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue x = {}; - i64 st_sz = type_size_of(st); - i64 arg_sz = type_size_of(args[i].type); - if (st_sz == arg_sz) { - x = lb_emit_transmute(p, args[i], st); + if (xt == abi_type) { + array_add(&processed_args, x); } else { - // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32} - GB_ASSERT(st_sz > arg_sz); - lbAddr xx = lb_add_local_generated(p, st, false); - lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type)); - lb_emit_store(p, pp, args[i]); - x = lb_addr_load(p, xx); - } - for (isize j = 0; j < new_type->Tuple.variables.count; j++) { - lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j); - array_add(&processed_args, xx); + Type *at = lb_abi_to_odin_type(abi_type); + if (at == t_llvm_bool) { + x = lb_emit_conv(p, x, at); + } else { + x = lb_emit_transmute(p, x, at); + } + array_add(&processed_args, x); } + + } else if (arg->kind == lbArg_Indirect) { + // lbValue ptr = lb_copy_value_to_ptr(p, x, original_type, 16); + lbValue ptr = lb_address_from_load_or_generate_local(p, x); + array_add(&processed_args, ptr); } - } else { - lbValue x = lb_emit_conv(p, args[i], new_type); - array_add(&processed_args, x); + + param_index += 1; } - } - if (inlining == ProcInlining_none) { - inlining = p->inlining; - } + if (inlining == ProcInlining_none) { + inlining = p->inlining; + } - lbValue result = {}; + Type *rt = reduce_tuple_to_single_type(results); + if (return_by_pointer) { - Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type); - Type *rt = reduce_tuple_to_single_type(results); - if (pt->Proc.return_by_pointer) { - lbValue return_ptr = {}; - if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { - if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { - return_ptr = p->return_ptr_hint_value; - p->return_ptr_hint_used = true; + lbValue return_ptr = {}; + // if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { + // if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { + // return_ptr = p->return_ptr_hint_value; + // p->return_ptr_hint_used = true; + // } + // } + // if (return_ptr.value == nullptr) { + lbAddr r = lb_add_local_generated(p, rt, true); + return_ptr = r.addr; + // } + GB_ASSERT(is_type_pointer(return_ptr.type)); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + result = lb_emit_load(p, return_ptr); + } else { + LLVMTypeRef ret_type = abi_ft->ret.cast_type; + if (!ret_type) { + ret_type = abi_ft->ret.type; + } + Type *abi_rt = lb_abi_to_odin_type(ret_type); + result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); + if (abi_rt != rt) { + result = lb_emit_transmute(p, result, rt); } } - if (return_ptr.value == nullptr) { - lbAddr r = lb_add_local_generated(p, rt, true); - return_ptr = r.addr; - } - GB_ASSERT(is_type_pointer(return_ptr.type)); - lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); - result = lb_emit_load(p, return_ptr); + } else { - result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); - if (abi_rt != rt) { - result = lb_emit_transmute(p, result, rt); + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->Proc.params->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + // array_add(&processed_args, args[i]); + continue; + } + GB_ASSERT(e->flags & EntityFlag_Param); + + Type *original_type = e->type; + Type *new_type = pt->Proc.abi_compat_params[i]; + Type *arg_type = args[i].type; + + if (are_types_identical(arg_type, new_type)) { + // NOTE(bill): Done + array_add(&processed_args, args[i]); + } else if (!are_types_identical(original_type, new_type)) { + if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { + Type *av = core_type(type_deref(new_type)); + if (are_types_identical(av, core_type(original_type))) { + if (e->flags&EntityFlag_ImplicitReference) { + array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i])); + } else if (!is_type_pointer(arg_type)) { + array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16)); + } + } else { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } + } else if (new_type == t_llvm_bool) { + array_add(&processed_args, lb_emit_conv(p, args[i], new_type)); + } else if (is_type_integer(new_type) || is_type_float(new_type) || is_type_boolean(new_type)) { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } else if (is_type_simd_vector(new_type)) { + array_add(&processed_args, lb_emit_transmute(p, args[i], new_type)); + } else if (is_type_tuple(new_type)) { + Type *abi_type = pt->Proc.abi_compat_params[i]; + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + lbValue x = {}; + i64 st_sz = type_size_of(st); + i64 arg_sz = type_size_of(args[i].type); + if (st_sz == arg_sz) { + x = lb_emit_transmute(p, args[i], st); + } else { + // NOTE(bill): struct{f32, f32, f32} != struct{#simd[2]f32, f32} + GB_ASSERT(st_sz > arg_sz); + lbAddr xx = lb_add_local_generated(p, st, false); + lbValue pp = lb_emit_conv(p, xx.addr, alloc_type_pointer(args[i].type)); + lb_emit_store(p, pp, args[i]); + x = lb_addr_load(p, xx); + } + for (isize j = 0; j < new_type->Tuple.variables.count; j++) { + lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j); + array_add(&processed_args, xx); + } + } + } else { + lbValue x = lb_emit_conv(p, args[i], new_type); + array_add(&processed_args, x); + } + } + + if (inlining == ProcInlining_none) { + inlining = p->inlining; + } + + + Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type); + Type *rt = reduce_tuple_to_single_type(results); + if (pt->Proc.return_by_pointer) { + lbValue return_ptr = {}; + if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { + if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { + return_ptr = p->return_ptr_hint_value; + p->return_ptr_hint_used = true; + } + } + if (return_ptr.value == nullptr) { + lbAddr r = lb_add_local_generated(p, rt, true); + return_ptr = r.addr; + } + GB_ASSERT(is_type_pointer(return_ptr.type)); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + result = lb_emit_load(p, return_ptr); + } else { + result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining); + if (abi_rt != rt) { + result = lb_emit_transmute(p, result, rt); + } } } @@ -10969,6 +11339,7 @@ void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->procedures, a); string_map_init(&m->const_strings, a); map_init(&m->anonymous_proc_lits, a); + map_init(&m->function_type_map, a); array_init(&m->procedures_to_generate, a); array_init(&m->foreign_library_paths, a); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4a0816e62..fc5968c93 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -74,6 +74,7 @@ struct lbModule { gbMutex mutex; Map<LLVMTypeRef> types; // Key: Type * + i32 internal_type_level; Map<lbValue> values; // Key: Entity * StringMap<lbValue> members; @@ -83,6 +84,7 @@ struct lbModule { StringMap<LLVMValueRef> const_strings; Map<lbProcedure *> anonymous_proc_lits; // Key: Ast * + Map<struct lbFunctionType *> function_type_map; // Key: Type * u32 global_array_index; u32 global_generated_index; @@ -199,6 +201,7 @@ struct lbProcedure { bool is_entry_point; bool is_startup; + lbFunctionType *abi_function_type; LLVMValueRef value; LLVMBuilderRef builder; diff --git a/src/types.cpp b/src/types.cpp index acc1c7b2e..528ea3a2d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -886,6 +886,16 @@ Type *alloc_type_named(String name, Type *base, Entity *type_name) { return t; } +bool is_calling_convention_none(ProcCallingConvention calling_convention) { + switch (calling_convention) { + case ProcCC_None: + case ProcCC_PureNone: + case ProcCC_InlineAsm: + return true; + } + return false; +} + Type *alloc_type_tuple() { Type *t = alloc_type(Type_Tuple); return t; |