From 54b37573c90d14bb8307982ebfc014849efa04ee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 Aug 2021 13:17:06 +0100 Subject: [PATCH] Add `intrinsics.syscall` (for Linux and Darwin only) --- src/check_builtin.cpp | 121 ++++++++++++++++++++++++---------- src/checker_builtin_procs.hpp | 5 ++ src/llvm_backend_proc.cpp | 117 ++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 34 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index acbb303c1..05a5fceda 100644 --- a/src/check_builtin.cpp +++ b/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: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 57b5d7eb9..dc9d139f7 100644 --- a/src/checker_builtin_procs.hpp +++ b/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}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index cca1ff196..ffbb532f0 100644 --- a/src/llvm_backend_proc.cpp +++ b/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));