Browse Source

Begin work on aarch64 ABI for `-llvm-api`

gingerBill 4 years ago
parent
commit
2d88c6c6a5
5 changed files with 221 additions and 20 deletions
  1. 37 14
      src/build_settings.cpp
  2. 178 3
      src/llvm_abi.cpp
  3. 4 1
      src/llvm_backend.cpp
  4. 1 1
      src/llvm_backend.hpp
  5. 1 1
      src/parser.hpp

+ 37 - 14
src/build_settings.cpp

@@ -23,6 +23,7 @@ enum TargetArchKind {
 
 
 	TargetArch_amd64,
 	TargetArch_amd64,
 	TargetArch_386,
 	TargetArch_386,
+	TargetArch_aarch64,
 	TargetArch_wasm32,
 	TargetArch_wasm32,
 
 
 	TargetArch_COUNT,
 	TargetArch_COUNT,
@@ -53,6 +54,7 @@ String target_arch_names[TargetArch_COUNT] = {
 	str_lit(""),
 	str_lit(""),
 	str_lit("amd64"),
 	str_lit("amd64"),
 	str_lit("386"),
 	str_lit("386"),
+	str_lit("aarch64"),
 	str_lit("wasm32"),
 	str_lit("wasm32"),
 };
 };
 
 
@@ -268,6 +270,15 @@ gb_global TargetMetrics target_darwin_amd64 = {
 	str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
 	str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
 };
 };
 
 
+gb_global TargetMetrics target_darwin_aarch64 = {
+	TargetOs_darwin,
+	TargetArch_aarch64,
+	8,
+	16,
+	str_lit("aarch64-apple-darwin"),
+	str_lit("e-m:o-i64:64-f64:128-n8:16:32:64-S128"), // TODO(bill): Is this correct?
+};
+
 gb_global TargetMetrics target_freebsd_386 = {
 gb_global TargetMetrics target_freebsd_386 = {
 	TargetOs_freebsd,
 	TargetOs_freebsd,
 	TargetArch_386,
 	TargetArch_386,
@@ -303,21 +314,23 @@ gb_global TargetMetrics target_js_wasm32 = {
 };
 };
 
 
 
 
+
 struct NamedTargetMetrics {
 struct NamedTargetMetrics {
 	String name;
 	String name;
 	TargetMetrics *metrics;
 	TargetMetrics *metrics;
 };
 };
 
 
 gb_global NamedTargetMetrics named_targets[] = {
 gb_global NamedTargetMetrics named_targets[] = {
-	{ str_lit("darwin_amd64"),  &target_darwin_amd64 },
-	{ str_lit("essence_amd64"), &target_essence_amd64 },
-	{ str_lit("js_wasm32"),     &target_js_wasm32 },
-	{ str_lit("linux_386"),     &target_linux_386 },
-	{ str_lit("linux_amd64"),   &target_linux_amd64 },
-	{ str_lit("windows_386"),   &target_windows_386 },
-	{ str_lit("windows_amd64"), &target_windows_amd64 },
-	{ str_lit("freebsd_386"),   &target_freebsd_386 },
-	{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
+	{ str_lit("darwin_amd64"),   &target_darwin_amd64   },
+	{ str_lit("darwin_aarch64"), &target_darwin_aarch64 },
+	{ str_lit("essence_amd64"),  &target_essence_amd64  },
+	{ str_lit("js_wasm32"),      &target_js_wasm32      },
+	{ str_lit("linux_386"),      &target_linux_386      },
+	{ str_lit("linux_amd64"),    &target_linux_amd64    },
+	{ str_lit("windows_386"),    &target_windows_386    },
+	{ str_lit("windows_amd64"),  &target_windows_amd64  },
+	{ str_lit("freebsd_386"),    &target_freebsd_386    },
+	{ str_lit("freebsd_amd64"),  &target_freebsd_amd64  },
 };
 };
 
 
 NamedTargetMetrics *selected_target_metrics;
 NamedTargetMetrics *selected_target_metrics;
@@ -807,6 +820,21 @@ void init_build_context(TargetMetrics *cross_target) {
 			bc->link_flags = str_lit("-arch x86 ");
 			bc->link_flags = str_lit("-arch x86 ");
 			break;
 			break;
 		}
 		}
+	} else if (bc->metrics.arch == TargetArch_aarch64) {
+		if (bc->microarch.len == 0) {
+			llc_flags = gb_string_appendc(llc_flags, "-march=aarch64 ");
+		}
+
+		switch (bc->metrics.os) {
+		case TargetOs_darwin:
+			bc->link_flags = str_lit("-arch aarch64 ");
+			break;
+		}
+		if (!bc->use_llvm_api) {
+			gb_printf_err("The aarch64 architecture is only supported with -llvm-api\n");;
+			gb_exit(1);
+		}
+
 	} else if (bc->metrics.arch == TargetArch_wasm32) {
 	} else if (bc->metrics.arch == TargetArch_wasm32) {
 		bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined ");
 		bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined ");
 	} else {
 	} else {
@@ -820,10 +848,6 @@ void init_build_context(TargetMetrics *cross_target) {
 
 
 	gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
 	gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
 
 
-	if (bc->microarch.len == 0) {
-		bc->microarch = str_lit("generic");
-	}
-
 	if (bc->microarch.len != 0) {
 	if (bc->microarch.len != 0) {
 		opt_flags = gb_string_appendc(opt_flags, "-march=");
 		opt_flags = gb_string_appendc(opt_flags, "-march=");
 		opt_flags = gb_string_append_length(opt_flags, bc->microarch.text, bc->microarch.len);
 		opt_flags = gb_string_append_length(opt_flags, bc->microarch.text, bc->microarch.len);
@@ -850,7 +874,6 @@ void init_build_context(TargetMetrics *cross_target) {
 
 
 
 
 
 
-
 	// NOTE(lachsinc): This optimization option was previously required to get
 	// NOTE(lachsinc): This optimization option was previously required to get
 	// around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
 	// around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
 	// is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.
 	// is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.

+ 178 - 3
src/llvm_abi.cpp

@@ -910,14 +910,187 @@ namespace lbAbiAmd64SysV {
 
 
 
 
 namespace lbAbiAarch64 {
 namespace lbAbiAarch64 {
+	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);
+	bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
+
 	LB_ABI_INFO(abi_info) {
 	LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
 		ft->ctx = c;
 		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;
+		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft -> args = compute_arg_types(c, arg_types, arg_count);
+		ft->calling_convention = calling_convention;
 		return ft;
 		return ft;
 	}
 	}
+
+	bool is_register(LLVMTypeRef type) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMIntegerTypeKind:
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+		case LLVMPointerTypeKind:
+			return true;
+		}
+		return false;
+	}
+
+	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+		LLVMAttributeRef attr = nullptr;
+		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+		if (type == i1) {
+			attr = lb_create_enum_attribute(c, "zeroext", true);
+		}
+		return lb_arg_type_direct(type, nullptr, nullptr, attr);
+	}
+
+	bool is_homogenous_array(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+		GB_ASSERT(lb_is_type_kind(type, LLVMArrayTypeKind));
+		unsigned len = LLVMGetArrayLength(type);
+		if (len == 0) {
+			return false;
+		}
+		LLVMTypeRef elem = LLVMGetElementType(type);
+		LLVMTypeRef base_type = nullptr;
+		unsigned member_count = 0;
+		if (is_homogenous_aggregate(c, elem, &base_type, &member_count)) {
+			if (base_type_) *base_type_ = base_type;
+			if (member_count_) *member_count_ = member_count * len;
+			return true;
+
+		}
+		return false;
+	}
+	bool is_homogenous_struct(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+		GB_ASSERT(lb_is_type_kind(type, LLVMStructTypeKind));
+		unsigned elem_count = LLVMCountStructElementTypes(type);
+		if (elem_count == 0) {
+			return false;
+		}
+		LLVMTypeRef base_type = nullptr;
+		unsigned member_count = 0;
+
+		for (unsigned i = 0; i < elem_count; i++) {
+			LLVMTypeRef field_type = nullptr;
+			unsigned field_member_count = 0;
+
+			LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i);
+			if (!is_homogenous_aggregate(c, elem, &field_type, &field_member_count)) {
+				return false;
+			}
+
+			if (base_type == nullptr) {
+				base_type = field_type;
+				member_count = field_member_count;
+			} else {
+				if (base_type != field_type) {
+					return false;
+				}
+				member_count += field_member_count;
+			}
+		}
+
+		if (base_type == nullptr) {
+			return false;
+		}
+
+		if (lb_sizeof(type) == lb_sizeof(base_type) * member_count) {
+			if (base_type_) *base_type_ = base_type;
+			if (member_count_) *member_count_ = member_count;
+			return true;
+		}
+
+		return false;
+	}
+
+
+	bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+			if (base_type_) *base_type_ = type;
+			if (member_count_) *member_count_ = 1;
+			return true;
+		case LLVMArrayTypeKind:
+			return is_homogenous_array(c, type, base_type_, member_count_);
+		case LLVMStructTypeKind:
+			return is_homogenous_struct(c, type, base_type_, member_count_);
+		}
+		return false;
+	}
+
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
+		LLVMTypeRef homo_base_type = {};
+		unsigned homo_member_count = 0;
+
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		} else if (is_register(type)) {
+			return non_struct(c, type);
+		} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
+			return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+		} else {
+			i64 size = lb_sizeof(type);
+			if (size <= 16) {
+				LLVMTypeRef cast_type = nullptr;
+				if (size <= 1) {
+					cast_type = LLVMIntTypeInContext(c, 8);
+				} else if (size <= 2) {
+					cast_type = LLVMIntTypeInContext(c, 16);
+				} else if (size <= 4) {
+					cast_type = LLVMIntTypeInContext(c, 32);
+				} else if (size <= 8) {
+					cast_type = LLVMIntTypeInContext(c, 64);
+				} else {
+					unsigned count = cast(unsigned)((size+7)/8);
+					cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+				}
+				return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+			} else {
+				LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret", true);
+				return lb_arg_type_indirect(type, 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 type = arg_types[i];
+
+			LLVMTypeRef homo_base_type = {};
+			unsigned homo_member_count = 0;
+
+			if (is_register(type)) {
+				args[i] = non_struct(c, type);
+			} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
+				args[i] = lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+			} else {
+				i64 size = lb_sizeof(type);
+				if (size <= 16) {
+					LLVMTypeRef cast_type = nullptr;
+					if (size <= 1) {
+						cast_type = LLVMIntTypeInContext(c, 8);
+					} else if (size <= 2) {
+						cast_type = LLVMIntTypeInContext(c, 16);
+					} else if (size <= 4) {
+						cast_type = LLVMIntTypeInContext(c, 32);
+					} else if (size <= 8) {
+						cast_type = LLVMIntTypeInContext(c, 64);
+					} else {
+						unsigned count = cast(unsigned)((size+7)/8);
+						cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+					}
+					args[i] = lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+				} else {
+					args[i] = lb_arg_type_indirect(type, nullptr);
+				}
+			}
+		}
+		return args;
+	}
 }
 }
 
 
 
 
@@ -950,6 +1123,8 @@ LB_ABI_INFO(lb_get_abi_info) {
 		}
 		}
 	} else if (build_context.metrics.arch == TargetArch_386) {
 	} else if (build_context.metrics.arch == TargetArch_386) {
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	} else if (build_context.metrics.arch == TargetArch_aarch64) {
+		return lbAbiAarch64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 	} else if (build_context.metrics.arch == TargetArch_wasm32) {
 	} else if (build_context.metrics.arch == TargetArch_wasm32) {
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 	}
 	}

+ 4 - 1
src/llvm_backend.cpp

@@ -2423,6 +2423,8 @@ LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRe
 	if (src_kind == dst_kind) {
 	if (src_kind == dst_kind) {
 		if (src_kind == LLVMPointerTypeKind) {
 		if (src_kind == LLVMPointerTypeKind) {
 			return LLVMBuildPointerCast(p->builder, val, dst_type, "");
 			return LLVMBuildPointerCast(p->builder, val, dst_type, "");
+		} else if (src_kind == LLVMArrayTypeKind) {
+			// ignore
 		} else if (src_kind != LLVMStructTypeKind) {
 		} else if (src_kind != LLVMStructTypeKind) {
 			return LLVMBuildBitCast(p->builder, val, dst_type, "");
 			return LLVMBuildBitCast(p->builder, val, dst_type, "");
 		}
 		}
@@ -8203,7 +8205,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		}
 		}
 
 
 	case BuiltinProc_cpu_relax:
 	case BuiltinProc_cpu_relax:
-		{
+		if (build_context.metrics.arch == TargetArch_386 ||
+		    build_context.metrics.arch == TargetArch_amd64) {
 			LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
 			LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
 			LLVMValueRef the_asm = LLVMGetInlineAsm(func_type,
 			LLVMValueRef the_asm = LLVMGetInlineAsm(func_type,
 				cast(char *)"pause", 5,
 				cast(char *)"pause", 5,

+ 1 - 1
src/llvm_backend.hpp

@@ -446,10 +446,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
 	lbCallingConvention_C,            // ProcCC_Invalid,
 	lbCallingConvention_C,            // ProcCC_Invalid,
 	lbCallingConvention_C,            // ProcCC_Odin,
 	lbCallingConvention_C,            // ProcCC_Odin,
 	lbCallingConvention_C,            // ProcCC_Contextless,
 	lbCallingConvention_C,            // ProcCC_Contextless,
-	lbCallingConvention_C,            // ProcCC_Pure,
 	lbCallingConvention_C,            // ProcCC_CDecl,
 	lbCallingConvention_C,            // ProcCC_CDecl,
 	lbCallingConvention_X86_StdCall,  // ProcCC_StdCall,
 	lbCallingConvention_X86_StdCall,  // ProcCC_StdCall,
 	lbCallingConvention_X86_FastCall, // ProcCC_FastCall,
 	lbCallingConvention_X86_FastCall, // ProcCC_FastCall,
 
 
 	lbCallingConvention_C,            // ProcCC_None,
 	lbCallingConvention_C,            // ProcCC_None,
+	lbCallingConvention_C,            // ProcCC_InlineAsm,
 };
 };

+ 1 - 1
src/parser.hpp

@@ -213,7 +213,7 @@ enum ProcCallingConvention {
 
 
 	ProcCC_None = 6,
 	ProcCC_None = 6,
 
 
-	ProcCC_InlineAsm = 9,
+	ProcCC_InlineAsm = 7,
 
 
 	ProcCC_MAX,
 	ProcCC_MAX,