Browse Source

Add basic arm32 ABI support (linux_arm32)

gingerBill 3 years ago
parent
commit
10a311092b
2 changed files with 102 additions and 1 deletions
  1. 14 1
      src/build_settings.cpp
  2. 88 0
      src/llvm_abi.cpp

+ 14 - 1
src/build_settings.cpp

@@ -30,6 +30,7 @@ enum TargetArchKind : u16 {
 
 	TargetArch_amd64,
 	TargetArch_i386,
+	TargetArch_arm32,
 	TargetArch_arm64,
 	TargetArch_wasm32,
 	TargetArch_wasm64,
@@ -75,6 +76,7 @@ String target_arch_names[TargetArch_COUNT] = {
 	str_lit(""),
 	str_lit("amd64"),
 	str_lit("i386"),
+	str_lit("arm32"),
 	str_lit("arm64"),
 	str_lit("wasm32"),
 	str_lit("wasm64"),
@@ -98,6 +100,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
 	TargetEndian_Little,
 	TargetEndian_Little,
 	TargetEndian_Little,
+	TargetEndian_Little,
 };
 
 #ifndef ODIN_VERSION_RAW
@@ -367,7 +370,16 @@ gb_global TargetMetrics target_linux_arm64 = {
 	8,
 	16,
 	str_lit("aarch64-linux-elf"),
-	str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
+};
+
+gb_global TargetMetrics target_linux_arm32 = {
+	TargetOs_linux,
+	TargetArch_arm32,
+	4,
+	8,
+	str_lit("aapcs-linux-gnu"),
+	str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
 };
 
 gb_global TargetMetrics target_darwin_amd64 = {
@@ -483,6 +495,7 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("linux_i386"),          &target_linux_i386     },
 	{ str_lit("linux_amd64"),         &target_linux_amd64    },
 	{ str_lit("linux_arm64"),         &target_linux_arm64    },
+	{ str_lit("linux_arm32"),         &target_linux_arm32    },
 	{ str_lit("windows_i386"),        &target_windows_i386   },
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
 	{ str_lit("freebsd_i386"),        &target_freebsd_i386   },

+ 88 - 0
src/llvm_abi.cpp

@@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV {
 
 	bool is_register(LLVMTypeRef type) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		i64 sz = lb_sizeof(type);
+		if (sz == 0) {
+			return false;
+		}
 		switch (kind) {
 		case LLVMIntegerTypeKind:
 		case LLVMHalfTypeKind:
@@ -1164,6 +1168,88 @@ namespace lbAbiWasm32 {
 	}
 }
 
+namespace lbAbiArm32 {
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+		ft->ctx = c;
+		ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
+		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft->calling_convention = calling_convention;
+		return ft;
+	}
+
+	bool is_register(LLVMTypeRef type, bool is_return) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMHalfTypeKind:
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+			return true;
+		case LLVMIntegerTypeKind:
+			return lb_sizeof(type) <= 8;
+		case LLVMFunctionTypeKind:
+			return true;
+		case LLVMPointerTypeKind:
+			return true;
+		case LLVMVectorTypeKind:
+			return true;
+		}
+		return false;
+	}
+
+	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+		LLVMAttributeRef attr = nullptr;
+		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+		if (type == i1) {
+			attr = lb_create_enum_attribute(c, "zeroext");
+		}
+		return lb_arg_type_direct(type, nullptr, nullptr, attr);
+	}
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
+		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef t = arg_types[i];
+			if (is_register(t, false)) {
+				args[i] = non_struct(c, t, false);
+			} else {
+				i64 sz = lb_sizeof(t);
+				i64 a = lb_alignof(t);
+				if (is_calling_convention_odin(calling_convention) && sz > 8) {
+					// Minor change to improve performance using the Odin calling conventions
+					args[i] = lb_arg_type_indirect(t, nullptr);
+				} else if (a <= 4) {
+					unsigned n = cast(unsigned)((sz + 3) / 4);
+					args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n));
+				} else {
+					unsigned n = cast(unsigned)((sz + 7) / 8);
+					args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n));
+				}
+			}
+		}
+		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 (!is_register(return_type, true)) {
+			switch (lb_sizeof(return_type)) {
+			case 1:         return lb_arg_type_direct(LLVMIntTypeInContext(c, 8),  return_type, nullptr, nullptr);
+			case 2:         return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr);
+			case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr);
+			}
+			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+			return lb_arg_type_indirect(return_type, attr);
+		}
+		return non_struct(c, return_type, true);
+	}
+};
+
 
 LB_ABI_INFO(lb_get_abi_info) {
 	switch (calling_convention) {
@@ -1203,6 +1289,8 @@ LB_ABI_INFO(lb_get_abi_info) {
 		}
 	case TargetArch_i386:
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	case TargetArch_arm32:
+		return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 	case TargetArch_arm64:
 		return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 	case TargetArch_wasm32: