Browse Source

Update LLVM backend to begin work on a generic ABI system

gingerBill 4 years ago
parent
commit
70b8b3c7dd
4 changed files with 1307 additions and 161 deletions
  1. 762 0
      src/llvm_abi.cpp
  2. 532 161
      src/llvm_backend.cpp
  3. 3 0
      src/llvm_backend.hpp
  4. 10 0
      src/types.cpp

+ 762 - 0
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(&reg_classes);
+		} else {
+			classify_with(t, &reg_classes, 0, 0);
+			fixup(t, &reg_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 &reg_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 &reg_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 {};
+}

+ 532 - 161
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);
 

+ 3 - 0
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;

+ 10 - 0
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;