mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge pull request #3524 from Feoramund/freebsd-amd64-syscall-errno
Add `intrinsics.syscall_bsd`
This commit is contained in:
@@ -73,6 +73,8 @@ expect :: proc(val, expected_val: T) -> T ---
|
|||||||
|
|
||||||
// Linux and Darwin Only
|
// Linux and Darwin Only
|
||||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||||
|
// FreeBSD, NetBSD, et cetera
|
||||||
|
syscall_bsd :: proc(id: uintptr, args: ..uintptr) -> (uintptr, bool) ---
|
||||||
|
|
||||||
|
|
||||||
// Atomics
|
// Atomics
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ get_last_error :: proc "contextless" () -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool {
|
_futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool {
|
||||||
if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0) == -1 {
|
if error, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0); !ok {
|
||||||
switch get_last_error() {
|
switch error {
|
||||||
case EINTR, EAGAIN:
|
case EINTR, EAGAIN:
|
||||||
return true
|
return true
|
||||||
case:
|
case:
|
||||||
@@ -45,11 +45,11 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du
|
|||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{
|
if error, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{
|
||||||
time_sec = cast(uint)(duration / 1e9),
|
time_sec = cast(uint)(duration / 1e9),
|
||||||
time_nsec = cast(uint)(duration % 1e9),
|
time_nsec = cast(uint)(duration % 1e9),
|
||||||
}, 0, 0) == -1 {
|
}, 0, 0); !ok {
|
||||||
switch get_last_error() {
|
switch error {
|
||||||
case EINTR, EAGAIN:
|
case EINTR, EAGAIN:
|
||||||
return true
|
return true
|
||||||
case ETIMEDOUT:
|
case ETIMEDOUT:
|
||||||
@@ -62,13 +62,13 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du
|
|||||||
}
|
}
|
||||||
|
|
||||||
_futex_signal :: proc "contextless" (futex: ^Futex) {
|
_futex_signal :: proc "contextless" (futex: ^Futex) {
|
||||||
if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0) == -1 {
|
if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0); !ok {
|
||||||
_panic("futex_wake_single failure")
|
_panic("futex_wake_single failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_futex_broadcast :: proc "contextless" (futex: ^Futex) {
|
_futex_broadcast :: proc "contextless" (futex: ^Futex) {
|
||||||
if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0) == -1 {
|
if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0); !ok {
|
||||||
_panic("_futex_wake_all failure")
|
_panic("_futex_wake_all failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ version_string_buf: [1024]u8
|
|||||||
init_os_version :: proc () {
|
init_os_version :: proc () {
|
||||||
os_version.platform = .FreeBSD
|
os_version.platform = .FreeBSD
|
||||||
|
|
||||||
kernel_version_buf: [129]u8
|
kernel_version_buf: [1024]u8
|
||||||
|
|
||||||
b := strings.builder_from_bytes(version_string_buf[:])
|
b := strings.builder_from_bytes(version_string_buf[:])
|
||||||
// Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC
|
// Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import "base:intrinsics"
|
|||||||
|
|
||||||
sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) {
|
sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) {
|
||||||
mib := mib
|
mib := mib
|
||||||
result_size := i64(size_of(T))
|
result_size := u64(size_of(T))
|
||||||
|
|
||||||
res := intrinsics.syscall(SYS_sysctl,
|
res: uintptr
|
||||||
|
res, ok = intrinsics.syscall_bsd(SYS_sysctl,
|
||||||
uintptr(raw_data(mib)), uintptr(len(mib)),
|
uintptr(raw_data(mib)), uintptr(len(mib)),
|
||||||
uintptr(val), uintptr(&result_size),
|
uintptr(val), uintptr(&result_size),
|
||||||
uintptr(0), uintptr(0),
|
uintptr(0), uintptr(0),
|
||||||
)
|
)
|
||||||
return res == 0
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// See /usr/include/sys/sysctl.h for details
|
// See /usr/include/sys/sysctl.h for details
|
||||||
|
|||||||
+52
-6
@@ -5115,15 +5115,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|||||||
isize max_arg_count = 32;
|
isize max_arg_count = 32;
|
||||||
|
|
||||||
switch (build_context.metrics.os) {
|
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_darwin:
|
||||||
case TargetOs_linux:
|
case TargetOs_linux:
|
||||||
case TargetOs_essence:
|
case TargetOs_essence:
|
||||||
case TargetOs_freebsd:
|
|
||||||
case TargetOs_openbsd:
|
|
||||||
case TargetOs_haiku:
|
case TargetOs_haiku:
|
||||||
switch (build_context.metrics.arch) {
|
switch (build_context.metrics.arch) {
|
||||||
case TargetArch_i386:
|
case TargetArch_i386:
|
||||||
@@ -5133,6 +5127,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->args.count > max_arg_count) {
|
if (ce->args.count > max_arg_count) {
|
||||||
@@ -5146,6 +5143,55 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BuiltinProc_syscall_bsd:
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
convert_to_typed(c, &x, t_uintptr);
|
||||||
|
if (!is_type_uintptr(x.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_freebsd:
|
||||||
|
case TargetOs_netbsd:
|
||||||
|
case TargetOs_openbsd:
|
||||||
|
switch (build_context.metrics.arch) {
|
||||||
|
case TargetArch_amd64:
|
||||||
|
case TargetArch_arm64:
|
||||||
|
max_arg_count = 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
|
||||||
|
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 = make_optional_ok_type(t_uintptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case BuiltinProc_type_base_type:
|
case BuiltinProc_type_base_type:
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ BuiltinProc__simd_end,
|
|||||||
|
|
||||||
// Platform specific intrinsics
|
// Platform specific intrinsics
|
||||||
BuiltinProc_syscall,
|
BuiltinProc_syscall,
|
||||||
|
BuiltinProc_syscall_bsd,
|
||||||
|
|
||||||
BuiltinProc_x86_cpuid,
|
BuiltinProc_x86_cpuid,
|
||||||
BuiltinProc_x86_xgetbv,
|
BuiltinProc_x86_xgetbv,
|
||||||
@@ -512,7 +513,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
|||||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
|
||||||
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||||
|
{STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||||
{STR_LIT("x86_cpuid"), 2, false, 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("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
|||||||
+134
-44
@@ -2747,26 +2747,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
|||||||
{
|
{
|
||||||
GB_ASSERT(arg_count <= 7);
|
GB_ASSERT(arg_count <= 7);
|
||||||
|
|
||||||
// FreeBSD additionally clobbers r8, r9, r10, but they
|
|
||||||
// can also be used to pass in arguments, so this needs
|
|
||||||
// to be handled in two parts.
|
|
||||||
bool clobber_arg_regs[7] = {
|
|
||||||
false, false, false, false, false, false, false
|
|
||||||
};
|
|
||||||
if (build_context.metrics.os == TargetOs_freebsd) {
|
|
||||||
clobber_arg_regs[4] = true; // r10
|
|
||||||
clobber_arg_regs[5] = true; // r8
|
|
||||||
clobber_arg_regs[6] = true; // r9
|
|
||||||
}
|
|
||||||
|
|
||||||
char asm_string[] = "syscall";
|
char asm_string[] = "syscall";
|
||||||
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
|
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
|
||||||
for (unsigned i = 0; i < arg_count; i++) {
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
if (!clobber_arg_regs[i]) {
|
constraints = gb_string_appendc(constraints, ",{");
|
||||||
constraints = gb_string_appendc(constraints, ",{");
|
|
||||||
} else {
|
|
||||||
constraints = gb_string_appendc(constraints, ",+{");
|
|
||||||
}
|
|
||||||
static char const *regs[] = {
|
static char const *regs[] = {
|
||||||
"rax",
|
"rax",
|
||||||
"rdi",
|
"rdi",
|
||||||
@@ -2790,36 +2774,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
|||||||
// Some but not all system calls will additionally
|
// Some but not all system calls will additionally
|
||||||
// clobber memory.
|
// clobber memory.
|
||||||
//
|
//
|
||||||
// As a fix for CVE-2019-5595, FreeBSD started
|
|
||||||
// clobbering R8, R9, and R10, instead of restoring
|
|
||||||
// them. Additionally unlike Linux, instead of
|
|
||||||
// returning negative errno, positive errno is
|
|
||||||
// returned and CF is set.
|
|
||||||
//
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// * Figure out what Darwin does.
|
// * Figure out what Darwin does.
|
||||||
// * Add some extra handling to propagate CF back
|
|
||||||
// up to the caller on FreeBSD systems so that
|
|
||||||
// the caller knows that the return value is
|
|
||||||
// positive errno.
|
|
||||||
constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
|
constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
|
||||||
if (build_context.metrics.os == TargetOs_freebsd) {
|
|
||||||
// Second half of dealing with FreeBSD's system
|
|
||||||
// call semantics. Explicitly clobber the registers
|
|
||||||
// that were not used to pass in arguments, and
|
|
||||||
// then clobber RFLAGS.
|
|
||||||
if (arg_count < 5) {
|
|
||||||
constraints = gb_string_appendc(constraints, ",~{r10}");
|
|
||||||
}
|
|
||||||
if (arg_count < 6) {
|
|
||||||
constraints = gb_string_appendc(constraints, ",~{r8}");
|
|
||||||
}
|
|
||||||
if (arg_count < 7) {
|
|
||||||
constraints = gb_string_appendc(constraints, ",~{r9}");
|
|
||||||
}
|
|
||||||
constraints = gb_string_appendc(constraints, ",~{cc}");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2927,6 +2884,139 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
|||||||
res.type = t_uintptr;
|
res.type = t_uintptr;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
case BuiltinProc_syscall_bsd:
|
||||||
|
{
|
||||||
|
// This is a BSD-style syscall where errors are indicated by a high
|
||||||
|
// Carry Flag and a positive return value, allowing the kernel to
|
||||||
|
// return any value that fits into a machine word.
|
||||||
|
//
|
||||||
|
// This is unlike Linux, where errors are indicated by a negative
|
||||||
|
// return value, limiting what can be expressed in one result.
|
||||||
|
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 *results = gb_alloc_array(permanent_allocator(), LLVMTypeRef, 2);
|
||||||
|
results[0] = lb_type(p->module, t_uintptr);
|
||||||
|
results[1] = lb_type(p->module, t_bool);
|
||||||
|
LLVMTypeRef llvm_results = LLVMStructTypeInContext(p->module->ctx, results, 2, false);
|
||||||
|
|
||||||
|
LLVMTypeRef func_type = LLVMFunctionType(llvm_results, 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; setnb %cl";
|
||||||
|
|
||||||
|
// Using CL as an output; RCX doesn't need to get clobbered later.
|
||||||
|
gbString constraints = gb_string_make(heap_allocator(), "={rax},={cl}");
|
||||||
|
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, "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(Feoramund): If you're experiencing instability
|
||||||
|
// regarding syscalls during optimized builds, it is
|
||||||
|
// possible that the ABI has changed for your platform,
|
||||||
|
// or I've missed a register clobber.
|
||||||
|
//
|
||||||
|
// Documentation on this topic is sparse, but I was able to
|
||||||
|
// determine what registers were being clobbered by adding
|
||||||
|
// dummy values to them, setting a breakpoint after the
|
||||||
|
// syscall, and checking the state of the registers afterwards.
|
||||||
|
//
|
||||||
|
// Be advised that manually stepping through a debugger may
|
||||||
|
// cause the kernel to not return via sysret, which will
|
||||||
|
// preserve register state that normally would've been
|
||||||
|
// otherwise clobbered.
|
||||||
|
//
|
||||||
|
// It is also possible that some syscalls clobber different registers.
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_freebsd) {
|
||||||
|
// As a fix for CVE-2019-5595, FreeBSD started
|
||||||
|
// clobbering R8, R9, and R10, instead of restoring
|
||||||
|
// them.
|
||||||
|
//
|
||||||
|
// More info here:
|
||||||
|
//
|
||||||
|
// https://www.freebsd.org/security/advisories/FreeBSD-SA-19:01.syscall.asc
|
||||||
|
// https://github.com/freebsd/freebsd-src/blob/098dbd7ff7f3da9dda03802cdb2d8755f816eada/sys/amd64/amd64/exception.S#L605
|
||||||
|
// https://stackoverflow.com/q/66878250
|
||||||
|
constraints = gb_string_appendc(constraints, ",~{r8},~{r9},~{r10}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both FreeBSD and NetBSD might clobber RDX.
|
||||||
|
//
|
||||||
|
// For NetBSD, it was clobbered during a call to sysctl.
|
||||||
|
//
|
||||||
|
// For FreeBSD, it's listed as "return value 2" in their
|
||||||
|
// AMD64 assembly, so there's no guarantee that it will persist.
|
||||||
|
constraints = gb_string_appendc(constraints, ",~{rdx},~{r11},~{cc},~{memory}");
|
||||||
|
|
||||||
|
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TargetArch_arm64:
|
||||||
|
{
|
||||||
|
GB_ASSERT(arg_count <= 7);
|
||||||
|
|
||||||
|
char asm_string[] = "svc #0; cset x8, cc";
|
||||||
|
gbString constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
|
||||||
|
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, "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeBSD clobbered x1 on a call to sysctl.
|
||||||
|
constraints = gb_string_appendc(constraints, ",~{x1},~{cc},~{memory}");
|
||||||
|
|
||||||
|
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Unsupported platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
lbValue res = {};
|
||||||
|
res.value = LLVMBuildCall2(p->builder, func_type, inline_asm, args, arg_count, "");
|
||||||
|
res.type = make_optional_ok_type(t_uintptr, true);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
case BuiltinProc_objc_send:
|
case BuiltinProc_objc_send:
|
||||||
return lb_handle_objc_send(p, expr);
|
return lb_handle_objc_send(p, expr);
|
||||||
|
|||||||
Reference in New Issue
Block a user