Selaa lähdekoodia

Add `intrinsics.syscall` (for Linux and Darwin only)

gingerBill 4 vuotta sitten
vanhempi
commit
54b37573c9
3 muutettua tiedostoa jossa 209 lisäystä ja 34 poistoa
  1. 87 34
      src/check_builtin.cpp
  2. 5 0
      src/checker_builtin_procs.hpp
  3. 117 0
      src/llvm_backend_proc.cpp

+ 87 - 34
src/check_builtin.cpp

@@ -199,7 +199,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 	}
 
-	String builtin_name = builtin_procs[id].name;
+	String builtin_name = builtin_procs[id].name;;
 
 
 	if (ce->args.count > 0) {
@@ -2066,11 +2066,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_integer_like(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 			} else if (x.type == t_llvm_bool) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 			}
 
@@ -2089,17 +2089,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_integer_like(x.type) && !is_type_float(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 			} else if (x.type == t_llvm_bool) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 			}
 			i64 sz = type_size_of(x.type);
 			if (sz < 2) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz);
+				error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_name), xts, sz);
 				gb_string_free(xts);
 			}
 
@@ -2126,13 +2126,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			convert_to_typed(c, &x, y.type);
 			if (is_type_untyped(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 				return false;
 			}
 			if (!is_type_integer(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 				return false;
 			}
@@ -2141,7 +2141,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				GB_ASSERT(ct->kind == Type_Basic);
 				if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
 					gbString xts = type_to_string(x.type);
-					error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+					error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
 					gb_string_free(xts);
 					return false;
 				}
@@ -2161,7 +2161,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			}
 			if (!is_type_float(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 				return false;
 			}
@@ -2204,19 +2204,19 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_pointer(dst.type)) {
 				gbString str = type_to_string(dst.type);
-				error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (!is_type_pointer(src.type)) {
 				gbString str = type_to_string(src.type);
-				error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (!is_type_integer(len.type)) {
 				gbString str = type_to_string(len.type);
-				error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
@@ -2225,7 +2225,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				i64 n = exact_value_to_i64(len.value);
 				if (n < 0) {
 					gbString str = expr_to_string(len.expr);
-					error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+					error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_name), str);
 					gb_string_free(str);
 				}
 			}
@@ -2251,13 +2251,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_pointer(ptr.type)) {
 				gbString str = type_to_string(ptr.type);
-				error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (!is_type_integer(len.type)) {
 				gbString str = type_to_string(len.type);
-				error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
@@ -2266,7 +2266,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				i64 n = exact_value_to_i64(len.value);
 				if (n < 0) {
 					gbString str = expr_to_string(len.expr);
-					error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+					error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_name), str);
 					gb_string_free(str);
 				}
 			}
@@ -2295,19 +2295,19 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_pointer(ptr.type)) {
 				gbString str = type_to_string(ptr.type);
-				error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (are_types_identical(core_type(ptr.type), t_rawptr)) {
 				gbString str = type_to_string(ptr.type);
-				error(ptr.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (!is_type_integer(offset.type)) {
 				gbString str = type_to_string(offset.type);
-				error(offset.expr, "Expected an integer value for the offset parameter for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(offset.expr, "Expected an integer value for the offset parameter for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
@@ -2338,26 +2338,26 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_pointer(ptr0.type)) {
 				gbString str = type_to_string(ptr0.type);
-				error(ptr0.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr0.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (are_types_identical(core_type(ptr0.type), t_rawptr)) {
 				gbString str = type_to_string(ptr0.type);
-				error(ptr0.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr0.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 
 			if (!is_type_pointer(ptr1.type)) {
 				gbString str = type_to_string(ptr1.type);
-				error(ptr1.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr1.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
 			if (are_types_identical(core_type(ptr1.type), t_rawptr)) {
 				gbString str = type_to_string(ptr1.type);
-				error(ptr1.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
+				error(ptr1.expr, "Expected a dereferenceable pointer value for '%.*s', got %s", LIT(builtin_name), str);
 				gb_string_free(str);
 				return false;
 			}
@@ -2365,7 +2365,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			if (!are_types_identical(ptr0.type, ptr1.type)) {
 				gbString xts = type_to_string(ptr0.type);
 				gbString yts = type_to_string(ptr1.type);
-				error(ptr0.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+				error(ptr0.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_name), xts, yts);
 				gb_string_free(yts);
 				gb_string_free(xts);
 				return false;
@@ -2535,7 +2535,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			if (!are_types_identical(x.type, y.type)) {
 				gbString xts = type_to_string(x.type);
 				gbString yts = type_to_string(y.type);
-				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_name), xts, yts);
 				gb_string_free(yts);
 				gb_string_free(xts);
 				return false;
@@ -2543,7 +2543,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_integer(x.type) || is_type_untyped(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 				return false;
 			}
@@ -2553,17 +2553,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				return false;
 			}
 			if (z.mode != Addressing_Constant || !is_type_integer(z.type)) {
-				error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name));
+				error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_name));
 				return false;
 			}
 			i64 n = exact_value_to_i64(z.value);
 			if (n <= 0) {
-				error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n);
+				error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_name), n);
 				return false;
 			}
 			i64 sz = 8*type_size_of(x.type);
 			if (n > sz) {
-				error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
+				error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_name), n, sz);
 				return false;
 			}
 
@@ -2590,7 +2590,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			if (!are_types_identical(x.type, y.type)) {
 				gbString xts = type_to_string(x.type);
 				gbString yts = type_to_string(y.type);
-				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_name), xts, yts);
 				gb_string_free(yts);
 				gb_string_free(xts);
 				*operand = x; // minimize error propagation
@@ -2599,14 +2599,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_integer_like(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
 				gb_string_free(xts);
 				*operand = x;
 				return true;
 			}
 
 			if (y.mode != Addressing_Constant) {
-				error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name));
+				error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_name));
 			}
 
 			if (x.mode == Addressing_Constant) {
@@ -2620,7 +2620,60 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		}
 		break;
 
-
+		
+	case BuiltinProc_syscall:
+		{
+			convert_to_typed(c, operand, t_uintptr);
+			if (!is_type_uintptr(operand->type)) {
+				gbString t = type_to_string(operand->type);
+				error(operand->expr, "Argument 0 must be of type 'uintptr', got %s", t);
+				gb_string_free(t);
+			}
+			for (isize i = 1; i < ce->args.count; i++) {
+				Operand x = {};
+				check_expr(c, &x, ce->args[i]);
+				if (x.mode != Addressing_Invalid) {
+					convert_to_typed(c, &x, t_uintptr);	
+				}
+				if (!is_type_uintptr(operand->type)) {
+					gbString t = type_to_string(x.type);
+					error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
+					gb_string_free(t);
+				}
+			}
+			
+			isize max_arg_count = 32;
+			
+			switch (build_context.metrics.os) {
+			case TargetOs_windows:
+			case TargetOs_freestanding:
+				error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
+				break;
+			case TargetOs_darwin:
+			case TargetOs_linux:
+			case TargetOs_essence:
+			case TargetOs_freebsd:
+				switch (build_context.metrics.arch) {
+				case TargetArch_386:
+				case TargetArch_amd64:
+				case TargetArch_arm64:
+					max_arg_count = 7;
+					break;
+				}
+				break;
+			}
+			
+			if (ce->args.count > max_arg_count) {
+				error(ast_end_token(call), "'%.*s' has a maximum of %td arguments on this platform (%.*s), got %td", LIT(builtin_name), max_arg_count, LIT(target_os_names[build_context.metrics.os]), ce->args.count);
+			}
+			
+			
+			
+			operand->mode = Addressing_Value;
+			operand->type = t_uintptr;
+			return true;
+		}
+		break;
 
 
 	case BuiltinProc_type_base_type:

+ 5 - 0
src/checker_builtin_procs.hpp

@@ -146,6 +146,9 @@ enum BuiltinProcId {
 	BuiltinProc_fixed_point_div_sat,
 
 	BuiltinProc_expect,
+	
+	// Platform specific intrinsics
+	BuiltinProc_syscall,
 
 	// Constant type tests
 
@@ -379,6 +382,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	
+	{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

+ 117 - 0
src/llvm_backend_proc.cpp

@@ -1830,6 +1830,123 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			res.type = t;
 			return lb_emit_conv(p, res, t);
 		}
+		
+	case BuiltinProc_syscall:
+		{
+			unsigned arg_count = cast(unsigned)ce->args.count;
+			LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count);
+			for_array(i, ce->args) {
+				lbValue arg = lb_build_expr(p, ce->args[i]);
+				arg = lb_emit_conv(p, arg, t_uintptr);
+				args[i] = arg.value;
+			}
+			
+			LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr);
+			LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count);
+			for (unsigned i = 0; i < arg_count; i++) {
+				llvm_arg_types[i] = llvm_uintptr;
+			}
+			
+			LLVMTypeRef func_type = LLVMFunctionType(llvm_uintptr, llvm_arg_types, arg_count, false);
+			
+			LLVMValueRef inline_asm = nullptr;
+			
+			switch (build_context.metrics.arch) {
+			case TargetArch_amd64:
+				{
+					GB_ASSERT(arg_count <= 7);
+					
+					char asm_string[] = "syscall";
+					gbString constraints = gb_string_make(heap_allocator(), "={rax}");
+					for (unsigned i = 0; i < arg_count; i++) {
+						constraints = gb_string_appendc(constraints, ",{");
+						static char const *regs[] = {
+							"rax",
+							"rdi",
+							"rsi",
+							"rdx",
+							"r10",
+							"r8",
+							"r9"
+						};
+						constraints = gb_string_appendc(constraints, regs[i]);
+						constraints = gb_string_appendc(constraints, "}");
+					}
+					size_t asm_string_size = gb_strlen(asm_string);
+					size_t constraints_size = gb_string_length(constraints);
+					
+					inline_asm = LLVMGetInlineAsm(func_type, asm_string, asm_string_size, constraints, constraints_size, true, false, LLVMInlineAsmDialectATT);
+				}
+				break;
+			case TargetArch_386:
+				{
+					GB_ASSERT(arg_count <= 7);
+					
+					char asm_string_default[] = "int $0x80";
+					char *asm_string = asm_string_default;
+					gbString constraints = gb_string_make(heap_allocator(), "={eax}");
+					
+					for (unsigned i = 0; i < gb_min(arg_count, 6); i++) {
+						constraints = gb_string_appendc(constraints, ",{");
+						static char const *regs[] = {
+							"eax",
+							"ebx",
+							"ecx",
+							"edx",
+							"esi",
+							"edi",
+						};
+						constraints = gb_string_appendc(constraints, regs[i]);
+						constraints = gb_string_appendc(constraints, "}");
+					}
+					if (arg_count == 7) {
+						char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp";
+						asm_string = asm_string7;
+						
+						constraints = gb_string_appendc(constraints, ",rm");
+					}
+					
+					size_t asm_string_size = gb_strlen(asm_string);
+					size_t constraints_size = gb_string_length(constraints);
+					
+					inline_asm = LLVMGetInlineAsm(func_type, asm_string, asm_string_size, constraints, constraints_size, true, false, LLVMInlineAsmDialectATT);
+				}
+				break;
+			case TargetArch_arm64:
+				{
+					GB_ASSERT(arg_count <= 7);
+					
+					char asm_string[] = "svc #0";
+					gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+					for (unsigned i = 0; i < arg_count; i++) {
+						constraints = gb_string_appendc(constraints, ",{");
+						static char const *regs[] = {
+							"x8",
+							"x0",
+							"x1",
+							"x2",
+							"x3",
+							"x4",
+							"x5",
+						};
+						constraints = gb_string_appendc(constraints, regs[i]);
+						constraints = gb_string_appendc(constraints, "}");
+					}
+					size_t asm_string_size = gb_strlen(asm_string);
+					size_t constraints_size = gb_string_length(constraints);
+					
+					inline_asm = LLVMGetInlineAsm(func_type, asm_string, asm_string_size, constraints, constraints_size, true, false, LLVMInlineAsmDialectATT);
+				}
+				break;
+			default:
+				GB_PANIC("Unsupported platform");
+			}
+			
+			lbValue res = {};
+			res.value = LLVMBuildCall2(p->builder, func_type, inline_asm, args, arg_count, "");
+			res.type = t_uintptr;
+			return res;
+		}
 	}
 
 	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));