Browse Source

Add `intrinsics.x86_cpuid` and `intrinsics.x86_xgetbv`

gingerBill 3 years ago
parent
commit
d7eaf0f87b

+ 4 - 0
core/intrinsics/intrinsics.odin

@@ -278,6 +278,10 @@ wasm_memory_size :: proc(index: uintptr)        -> int ---
 wasm_memory_atomic_wait32   :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
 wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
 
+// x86 Targets (i386, amd64)
+cpuid  :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
+xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
+
 
 // Darwin targets only
 objc_object   :: struct{}

+ 2 - 1
core/simd/x86/ssse3.odin

@@ -121,4 +121,5 @@ foreign _ {
 	@(link_name = "llvm.x86.ssse3.psign.w.128")
 	psignw128    :: proc(a, b: i16x8) -> i16x8 ---
 	@(link_name = "llvm.x86.ssse3.psign.d.128")
-	psignd128    :: proc(a, b: i32x4) -> i32x4 ---}
+	psignd128    :: proc(a, b: i32x4) -> i32x4 ---
+}

+ 9 - 0
src/build_settings.cpp

@@ -629,6 +629,15 @@ bool is_arch_wasm(void) {
 	return false;
 }
 
+bool is_arch_x86(void) {
+	switch (build_context.metrics.arch) {
+	case TargetArch_i386:
+	case TargetArch_amd64:
+		return true;
+	}
+	return false;
+}
+
 bool allow_check_foreign_filepath(void) {
 	switch (build_context.metrics.arch) {
 	case TargetArch_wasm32:

+ 59 - 1
src/check_builtin.cpp

@@ -1060,8 +1060,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call
 			operand->type = t_untyped_integer;
 			operand->mode = Addressing_Constant;
 			operand->value = exact_value_i64(result);
+			return true;
 		}
-
 	default:
 		GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name));
 	}
@@ -5275,6 +5275,64 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		}
 		break;
 
+	case BuiltinProc_x86_cpuid:
+		{
+			if (!is_arch_x86()) {
+				error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
+				return false;
+			}
+
+			Operand ax = {};
+			Operand cx = {};
+
+			check_expr_with_type_hint(c, &ax, ce->args[0], t_u32); if (ax.mode == Addressing_Invalid) return false;
+			check_expr_with_type_hint(c, &cx, ce->args[1], t_u32); if (cx.mode == Addressing_Invalid) return false;
+			convert_to_typed(c, &ax, t_u32); if (ax.mode == Addressing_Invalid) return false;
+			convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
+			if (!are_types_identical(ax.type, t_u32)) {
+				gbString str = type_to_string(ax.type);
+				error(ax.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+				return false;
+			}
+			if (!are_types_identical(cx.type, t_u32)) {
+				gbString str = type_to_string(cx.type);
+				error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+				return false;
+			}
+			Type *types[4] = {t_u32, t_u32, t_u32, t_u32}; // eax ebc ecx edx
+			operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
+			operand->mode = Addressing_Value;
+			operand->value = {};
+			return true;
+		}
+		break;
+	case BuiltinProc_x86_xgetbv:
+		{
+			if (!is_arch_x86()) {
+				error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
+				return false;
+			}
+
+			Operand cx = {};
+			check_expr_with_type_hint(c, &cx, ce->args[0], t_u32); if (cx.mode == Addressing_Invalid) return false;
+			convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
+			if (!are_types_identical(cx.type, t_u32)) {
+				gbString str = type_to_string(cx.type);
+				error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+				return false;
+			}
+
+			Type *types[2] = {t_u32, t_u32};
+			operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
+			operand->mode = Addressing_Value;
+			operand->value = {};
+			return true;
+		}
+		break;
+
 	}
 
 	return true;

+ 6 - 0
src/checker_builtin_procs.hpp

@@ -187,6 +187,9 @@ BuiltinProc__simd_end,
 	// Platform specific intrinsics
 	BuiltinProc_syscall,
 
+	BuiltinProc_x86_cpuid,
+	BuiltinProc_x86_xgetbv,
+
 	// Constant type tests
 
 BuiltinProc__type_begin,
@@ -470,10 +473,13 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("simd_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("x86_cpuid"),  2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

+ 44 - 19
src/llvm_backend_proc.cpp

@@ -1007,9 +1007,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	lbValue res = {};
 	res.type = tv.type;
 
-	lbValue arg0 = lb_build_expr(p, ce->args[0]);
-	lbValue arg1 = {};
-	lbValue arg2 = {};
+	lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);
+	lbValue arg1 = {}; if (ce->args.count > 1) arg0 = lb_build_expr(p, ce->args[1]);
+	lbValue arg2 = {}; if (ce->args.count > 2) arg0 = lb_build_expr(p, ce->args[2]);
 
 	Type *elem = base_array_type(arg0.type);
 
@@ -1024,7 +1024,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_mul:
 	case BuiltinProc_simd_div:
 	case BuiltinProc_simd_rem:
-		arg1 = lb_build_expr(p, ce->args[1]);
 		if (is_float) {
 			switch (builtin_id) {
 			case BuiltinProc_simd_add: op_code = LLVMFAdd; break;
@@ -1062,7 +1061,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_shr: // Odin logic
 	case BuiltinProc_simd_shl_masked: // C logic
 	case BuiltinProc_simd_shr_masked: // C logic
-		arg1 = lb_build_expr(p, ce->args[1]);
 		{
 			i64 sz = type_size_of(elem);
 			GB_ASSERT(arg0.type->kind == Type_SimdVector);
@@ -1098,7 +1096,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_or:
 	case BuiltinProc_simd_xor:
 	case BuiltinProc_simd_and_not:
-		arg1 = lb_build_expr(p, ce->args[1]);
 		switch (builtin_id) {
 		case BuiltinProc_simd_and: op_code = LLVMAnd; break;
 		case BuiltinProc_simd_or:  op_code = LLVMOr;  break;
@@ -1143,7 +1140,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 		}
 		return res;
 	case BuiltinProc_simd_max:
-		arg1 = lb_build_expr(p, ce->args[1]);
 		if (is_float) {
 			LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
 			res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1158,7 +1154,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_lanes_le:
 	case BuiltinProc_simd_lanes_gt:
 	case BuiltinProc_simd_lanes_ge:
-		arg1 = lb_build_expr(p, ce->args[1]);
 		if (is_float) {
 			LLVMRealPredicate pred = cast(LLVMRealPredicate)0;
 			switch (builtin_id) {
@@ -1193,12 +1188,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 		break;
 
 	case BuiltinProc_simd_extract:
-		arg1 = lb_build_expr(p, ce->args[1]);
 		res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, "");
 		return res;
 	case BuiltinProc_simd_replace:
-		arg1 = lb_build_expr(p, ce->args[1]);
-		arg2 = lb_build_expr(p, ce->args[2]);
 		res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, "");
 		return res;
 
@@ -1283,12 +1275,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 
 	case BuiltinProc_simd_shuffle:
 		{
-			arg1 = lb_build_expr(p, ce->args[1]);
-
 			Type *vt = arg0.type;
 			GB_ASSERT(vt->kind == Type_SimdVector);
 
-
 			i64 indices_count = ce->args.count-2;
 			i64 max_count = vt->SimdVector.count*2;
 			GB_ASSERT(indices_count <= max_count);
@@ -1394,8 +1383,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_add_sat:
 	case BuiltinProc_simd_sub_sat:
 		{
-			arg1 = lb_build_expr(p, ce->args[1]);
-
 			char const *name = nullptr;
 			switch (builtin_id) {
 			case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break;
@@ -1417,9 +1404,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 
 	case BuiltinProc_simd_clamp:
 		{
-			arg1 = lb_build_expr(p, ce->args[1]);
-			arg2 = lb_build_expr(p, ce->args[2]);
-
 			LLVMValueRef v = arg0.value;
 			LLVMValueRef min = arg1.value;
 			LLVMValueRef max = arg2.value;
@@ -2737,6 +2721,47 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			return res;
 		}
 
+
+	case BuiltinProc_x86_cpuid:
+		{
+			Type *param_types[2] = {t_u32, t_u32};
+			Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None);
+			LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+			LLVMValueRef the_asm = llvm_get_inline_asm(
+				func_type,
+				str_lit("cpuid"),
+				str_lit("={ax},={bx},={cx},={dx},{ax},{cx}"),
+				true
+			);
+			GB_ASSERT(the_asm != nullptr);
+
+			LLVMValueRef args[2] = {};
+			args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
+			args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
+			lbValue res = {};
+			res.type = tv.type;
+			res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
+			return res;
+		}
+	case BuiltinProc_x86_xgetbv:
+		{
+			Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None);
+			LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+			LLVMValueRef the_asm = llvm_get_inline_asm(
+				func_type,
+				str_lit("xgetbv"),
+				str_lit("={ax},={dx},{cx}"),
+				true
+			);
+			GB_ASSERT(the_asm != nullptr);
+
+			LLVMValueRef args[1] = {};
+			args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
+			lbValue res = {};
+			res.type = tv.type;
+			res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
+			return res;
+		}
 	}
 
 	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));