瀏覽代碼

Merge remote-tracking branch 'live/master' into spall_v3

Colin Davidson 1 周之前
父節點
當前提交
e869b9351b

+ 2 - 0
base/intrinsics/intrinsics.odin

@@ -32,6 +32,7 @@ trap       :: proc() -> ! ---
 alloca             :: proc(size, align: int) -> [^]u8 ---
 cpu_relax          :: proc() ---
 read_cycle_counter :: proc() -> i64 ---
+read_cycle_counter_frequency :: proc() -> i64 ---
 
 count_ones           :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
 count_zeros          :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
@@ -362,6 +363,7 @@ x86_cpuid  :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
 x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
 
 
+
 // Darwin targets only
 objc_object   :: struct{}
 objc_selector :: struct{}

+ 333 - 12
core/sys/darwin/mach_darwin.odin

@@ -5,15 +5,34 @@ foreign import mach "system:System"
 import "core:c"
 import "base:intrinsics"
 
+mach_port_t :: distinct c.uint
+task_t :: mach_port_t
+
+semaphore_t :: distinct u64
+
 kern_return_t :: distinct c.int
+thread_act_t   :: distinct u64
+thread_state_t :: distinct ^u32
+thread_list_t  :: [^]thread_act_t
+vm_region_recurse_info_t :: distinct ^i32
+task_info_t :: distinct ^i32
+
+MACH_PORT_NULL :: 0
+MACH_PORT_DEAD :: ~mach_port_t(0)
+
+MACH_MSG_PORT_DESCRIPTOR :: 0
+
+X86_THREAD_STATE32 :: 1
+X86_THREAD_STATE64 :: 4
+ARM_THREAD_STATE64 :: 6
+
+mach_msg_option_t :: distinct i32
+name_t :: distinct cstring
 
-mach_port_t :: distinct c.uint
 vm_map_t :: mach_port_t
 mem_entry_name_port_t :: mach_port_t
 ipc_space_t :: mach_port_t
 thread_t :: mach_port_t
-task_t :: mach_port_t
-semaphore_t :: mach_port_t
 
 vm_size_t :: distinct c.uintptr_t
 
@@ -29,11 +48,279 @@ vm_inherit_t :: distinct c.uint
 
 mach_port_name_t :: distinct c.uint
 
+mach_port_right_t :: distinct c.uint
+
 sync_policy_t :: distinct c.int
 
+mach_msg_port_descriptor_t :: struct {
+	name: mach_port_t,
+	_: u32,
+	using _: bit_field u32 {
+		_: u32 | 16,
+		disposition: u32 | 8,
+		type: u32 | 8,
+	},
+}
+
+Task_Port_Type :: enum u32 {
+	Kernel   = 1,
+	Host,
+	Name,
+	Bootstrap,
+	Seatbelt = 7,
+	Access   = 9,
+}
+
+Bootstrap_Error :: enum u32 {
+	Success,
+	Not_Privileged  = 1100,
+	Name_In_Use     = 1101,
+	Unknown_Service = 1102,
+	Service_Active  = 1103,
+	Bad_Count       = 1104,
+	No_Memory       = 1105,
+	No_Children     = 1106,
+}
+
+Msg_Type :: enum u32 {
+	Unstructured = 0,
+	Bit          = 0,
+	Boolean      = 0,
+	Integer_16   = 1,
+	Integer_32   = 2,
+	Char         = 8,
+	Byte         = 9,
+	Integer_8    = 9,
+	Real         = 10,
+	Integer_64   = 11,
+	String       = 12,
+	String_C     = 12,
+
+	Port_Name      = 15,
+
+	Move_Receive   = 16,
+	Port_Receive   = 16,
+	Move_Send      = 17,
+	Port_Send      = 17,
+	Move_Send_Once = 18,
+	Port_Send_Once = 18,
+	Copy_Send      = 19,
+	Make_Send      = 20,
+	Make_Send_Once = 21,
+}
+
+Msg_Header_Bits :: enum u32 {
+	Zero                   = 0,
+	Remote_Mask         = 0xff,
+	Local_Mask        = 0xff00,
+	Migrated      = 0x08000000,
+	Unused        = 0x07ff0000,
+	Complex_Data  = 0x10000000,
+	Complex_Ports = 0x20000000,
+	Circular      = 0x40000000,
+	Complex       = 0x80000000,
+}
+
+mach_msg_type_t :: struct {
+	using _: bit_field u32 {
+		name:       u32 | 8,
+		size:       u32 | 8,
+		number:     u32 | 12,
+		inline:     u32 | 1,
+		longform:   u32 | 1,
+		deallocate: u32 | 1,
+		unused:     u32 | 1,
+	},
+}
+
+mach_msg_header_t :: struct {
+	msgh_bits: u32,
+	msgh_size: u32,
+	msgh_remote_port: mach_port_t,
+	msgh_local_port: mach_port_t,
+	msgh_voucher_port: u32,
+	msgh_id: i32,
+}
+
+mach_msg_body_t :: struct {
+	msgh_descriptor_count: u32,
+}
+
+mach_msg_trailer_t :: struct {
+	msgh_trailer_type: u32,
+	msgh_trailer_size: u32,
+}
+
+x86_thread_state32_t :: struct {
+	eax: u32,
+	ebx: u32,
+	ecx: u32,
+	edx: u32,
+	edi: u32,
+	esi: u32,
+	ebp: u32,
+	esp: u32,
+	ss:  u32,
+	eflags: u32,
+	eip: u32,
+	cs:  u32,
+	ds:  u32,
+	es:  u32,
+	fs:  u32,
+	gs:  u32,
+}
+X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32)
+
+x86_thread_state64_t :: struct #packed {
+	rax: u64,
+	rbx: u64,
+	rcx: u64,
+	rdx: u64,
+	rdi: u64,
+	rsi: u64,
+	rbp: u64,
+	rsp: u64,
+	r8:  u64,
+	r9:  u64,
+	r10: u64,
+	r11: u64,
+	r12: u64,
+	r13: u64,
+	r14: u64,
+	r15: u64,
+	rip: u64,
+	rflags: u64,
+	cs:  u64,
+	fs: u64,
+	gs: u64,
+}
+X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32)
+
+arm_thread_state64_t :: struct #packed {
+	x: [29]u64,
+	fp: u64,
+	lr: u64,
+	sp: u64,
+	pc: u64,
+	cpsr: u32,
+	pad:  u32,
+}
+ARM_THREAD_STATE64_COUNT :: size_of(arm_thread_state64_t) / size_of(u32)
+
+THREAD_IDENTIFIER_INFO :: 4
+thread_identifier_info :: struct {
+	thread_id: u64,
+	thread_handler: u64,
+	dispatch_qaddr: u64,
+}
+THREAD_IDENTIFIER_INFO_COUNT :: size_of(thread_identifier_info) / size_of(u32)
+
+vm_region_submap_info_64 :: struct {
+	protection:               u32,
+	max_protection:           u32,
+	inheritance:              u32,
+	offset:                   u64,
+	user_tag:                 u32,
+	pages_residept:           u32,
+	pages_shared_now_private: u32,
+	pages_swapped_out:        u32,
+	pages_dirtied:            u32,
+	ref_count:                u32,
+	shadow_depth:             u16,
+	external_pager:           u8,
+	share_mode:               u8,
+	is_submap:                b32,
+	behavior:                 i32,
+	object_id:                u32,
+	user_wired_count:         u16,
+	pages_reusable:           u32,
+}
+VM_REGION_SUBMAP_INFO_COUNT_64 :: size_of(vm_region_submap_info_64) / size_of(u32)
+
+TASK_DYLD_INFO :: 17
+task_dyld_info :: struct {
+	all_image_info_addr: u64,
+	all_image_info_size: u64,
+	all_image_info_format: i32,
+}
+TASK_DYLD_INFO_COUNT :: size_of(task_dyld_info) / size_of(u32)
+
+dyld_image_info :: struct {
+	image_load_addr: u64,
+	image_file_path: cstring,
+	image_file_mod_date: u64,
+}
+
+dyld_uuid_info :: struct {
+	image_load_addr: u64,
+	image_uuid:   [16]u8,
+}
+
+dyld_all_image_infos :: struct {
+	version:                             u32,
+	info_array_count:                    u32,
+	info_array:             		  rawptr,
+	notification:                     rawptr,
+	process_detached_from_shared_region: b32,
+	libSystem_initialized:               b32,
+	dyld_image_load_addr:                u64,
+	jit_info:                         rawptr,
+	dyld_version:                    cstring,
+	error_message:                   cstring,
+	termination_flags:                   u64,
+	core_symbolication_shm_page:      rawptr,
+	system_order_flag:                   u64,
+	uuid_array_count:                    u64,
+	uuid_array:                       rawptr,
+	dyld_all_image_infos_addr:           u64,
+	initial_image_count:                 u64,
+	error_kind:                          u64,
+	error_client_of_dylib_path:      cstring,
+	error_target_dylib_path:         cstring,
+	error_symbol:                    cstring,
+	shared_cache_slide:                  u64,
+	shared_cache_uuid:                [16]u8,
+	shared_cache_base_addr:              u64,
+	info_array_change_timestamp:         u64,
+	dyld_path:                       cstring,
+	notify_ports:             [8]mach_port_t,
+	reserved:                         [7]u64,
+	shared_cache_fsid:                   u64,
+	shared_cache_fsobjid:                u64,
+	compact_dyld_image_info_addr:        u64,
+	compact_dyld_image_info_size:        u64,
+	platform:                            u32,
+	aot_info_count:                      u32,
+	aot_info_array:                   rawptr,
+	aot_info_array_change_timestamp:     u64,
+	aot_shared_cache_base_address:       u64,
+	aot_shared_cache_uuid:            [16]u8,
+}
+
+
 @(default_calling_convention="c")
 foreign mach {
-	mach_task_self :: proc() -> mach_port_t ---
+	mach_task_self     :: proc() -> mach_port_t ---
+	mach_msg           :: proc(header: rawptr, option: Msg_Option_Flags, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> Kern_Return ---
+	mach_msg_send      :: proc(header: rawptr) -> Kern_Return ---
+	mach_vm_allocate   :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> Kern_Return ---
+	mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> Kern_Return ---
+	mach_vm_remap      :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: VM_Inherit) -> Kern_Return ---
+	mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> Kern_Return ---
+	vm_page_size:  u64
+	vm_page_mask:  u64
+	vm_page_shift: i32
+
+	mach_port_allocate   :: proc(task: task_t, right: Port_Right, name: rawptr) -> Kern_Return ---
+	mach_port_deallocate :: proc(task: task_t, name: u32) -> Kern_Return ---
+	mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> Kern_Return ---
+
+	task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> Kern_Return ---
+	task_suspend   :: proc(task: task_t) -> Kern_Return ---
+	task_resume    :: proc(task: task_t) -> Kern_Return ---
+	task_threads   :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> Kern_Return ---
+	task_info      :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> Kern_Return ---
+	task_terminate :: proc(task: task_t) -> Kern_Return ---
 
 	semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return ---
 	semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return ---
@@ -44,9 +331,11 @@ foreign mach {
 
 	semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
 
-	vm_allocate :: proc (target_task : vm_map_t, address: ^vm_address_t, size: vm_size_t, flags: VM_Flags) -> Kern_Return ---
+	thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> Kern_Return ---
+	thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> Kern_Return ---
 
-	vm_deallocate :: proc(target_task: vm_map_t, address: vm_address_t, size: vm_size_t) -> Kern_Return ---
+	bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> Kern_Return ---
+	bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> Kern_Return ---
 
 	vm_map :: proc(
 		target_task:    vm_map_t,
@@ -70,14 +359,9 @@ foreign mach {
 		object_handle: ^mem_entry_name_port_t,
 		parent_entry:  mem_entry_name_port_t,
 	) -> Kern_Return ---
+}
 
-	mach_port_deallocate :: proc(
-		task: ipc_space_t,
-		name: mach_port_name_t,
-	) -> Kern_Return ---
 
-	vm_page_size: vm_size_t
-}
 
 Kern_Return :: enum kern_return_t {
 	Success,
@@ -500,6 +784,39 @@ VM_PROT_NONE    :: VM_Prot_Flags{}
 VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
 VM_PROT_ALL     :: VM_Prot_Flags{.Read, .Write, .Execute}
 
+/*
+ * Mach msg options, defined as bits within the mach_msg_option_t type
+ */
+
+Msg_Option :: enum mach_msg_option_t {
+	Send_Msg,
+	Receive_Msg,
+
+	Send_Timeout      = LOG2(0x10),
+	Send_Notify       = LOG2(0x20),
+	Send_Interrupt    = LOG2(0x40),
+	Send_Cancel       = LOG2(0x80),
+	Receive_Timeout   = LOG2(0x100),
+	Receive_Notify    = LOG2(0x200),
+	Receive_Interrupt = LOG2(0x400),
+	Receive_Large     = LOG2(0x800),
+	Send_Always       = LOG2(0x10000),
+}
+
+Msg_Option_Flags :: distinct bit_set[Msg_Option; mach_msg_option_t]
+
+/*
+ *  Enumeration of valid values for mach_port_right_t
+ */
+
+Port_Right :: enum mach_port_right_t {
+	Send,
+	Receive,
+	Send_Once,
+	Port_Set,
+	Dead_Name,
+}
+
 /*
  *	Enumeration of valid values for vm_inherit_t.
  */
@@ -522,3 +839,7 @@ Sync_Policy :: enum sync_policy_t {
 
 	Lifo = Fifo | Reversed,
 }
+
+mach_vm_trunc_page :: proc(v: u64) -> u64 {
+	return v & ~vm_page_mask
+}

+ 20 - 0
core/sys/posix/spawn.odin

@@ -0,0 +1,20 @@
+package posix
+
+when ODIN_OS == .Darwin {
+	foreign import lib "system:System.framework"
+} else {
+	foreign import lib "system:c"
+}
+
+foreign lib {
+	/*
+	Creates a child process from a provided filepath
+	spawnp searches directories on the path for the file
+
+	Returns: 0 on success, with the child pid returned in the pid argument, or error values on failure.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html ]]
+	*/
+	posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno ---
+	posix_spawnp :: proc(pid: ^pid_t, file: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno ---
+}

+ 2 - 0
core/time/perf.odin

@@ -104,6 +104,8 @@ TSC at a fixed frequency, independent of ACPI state, and CPU frequency.
 has_invariant_tsc :: proc "contextless" () -> bool {
 	when ODIN_ARCH == .amd64 {
 		return x86_has_invariant_tsc()
+	} else when ODIN_ARCH == .arm64 {
+		return true
 	}
 
 	return false

+ 8 - 1
core/time/tsc_darwin.odin

@@ -1,10 +1,17 @@
 #+private
 package time
 
+import "base:intrinsics"
 import "core:sys/unix"
 
 _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) {
-	unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return
+	if ODIN_ARCH == .amd64 {
+		unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return
+	} else if ODIN_ARCH == .arm64 {
+		freq = u64(intrinsics.read_cycle_counter_frequency())
+	} else {
+		return
+	}
 	ok = true
 	return
 }

+ 31 - 25
core/time/tsc_linux.odin

@@ -2,32 +2,38 @@
 #+build linux
 package time
 
-import linux "core:sys/linux"
+import "base:intrinsics"
+@(require) import linux "core:sys/linux"
 
 _get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
-	// Get the file descriptor for the perf mapping
-	perf_attr := linux.Perf_Event_Attr{}
-	perf_attr.size = size_of(perf_attr)
-	perf_attr.type = .HARDWARE
-	perf_attr.config.hw = .INSTRUCTIONS
-	perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV}
-	fd, perf_errno := linux.perf_event_open(&perf_attr, linux.Pid(0), -1, linux.Fd(-1), {})
-	if perf_errno != nil {
-		return 0, false
+	when ODIN_ARCH == .arm64 {
+		frequency := u64(intrinsics.read_cycle_counter_frequency())
+		return frequency, true
+	} else {
+		// Get the file descriptor for the perf mapping
+		perf_attr := linux.Perf_Event_Attr{}
+		perf_attr.size = size_of(perf_attr)
+		perf_attr.type = .HARDWARE
+		perf_attr.config.hw = .INSTRUCTIONS
+		perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV}
+		fd, perf_errno := linux.perf_event_open(&perf_attr, linux.Pid(0), -1, linux.Fd(-1), {})
+		if perf_errno != nil {
+			return 0, false
+		}
+		defer linux.close(fd)
+		// Map it into the memory
+		page_size : uint = 4096
+		addr, mmap_errno := linux.mmap(0, page_size, {.READ}, {.SHARED}, fd)
+		if mmap_errno != nil {
+			return 0, false
+		}
+		defer linux.munmap(addr, page_size)
+		// Get the frequency from the mapped page
+		event_page := cast(^linux.Perf_Event_Mmap_Page) addr
+		if .User_Time not_in event_page.cap.flags {
+			return 0, false
+		}
+		frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult))
+		return frequency, true
 	}
-	defer linux.close(fd)
-	// Map it into the memory
-	page_size : uint = 4096
-	addr, mmap_errno := linux.mmap(0, page_size, {.READ}, {.SHARED}, fd)
-	if mmap_errno != nil {
-		return 0, false
-	}
-	defer linux.munmap(addr, page_size)
-	// Get the frequency from the mapped page
-	event_page := cast(^linux.Perf_Event_Mmap_Page) addr
-	if .User_Time not_in event_page.cap.flags {
-		return 0, false
-	}
-	frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult))
-	return frequency, true
 }

+ 7 - 0
src/build_settings.cpp

@@ -1887,6 +1887,13 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
 			str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
 	} else if (is_arch_wasm()) {
 		gbString link_flags = gb_string_make(heap_allocator(), " ");
+
+		// NOTE(laytan): Put the stack first in the memory,
+		// causing a stack overflow to error immediately instead of corrupting globals.
+		link_flags = gb_string_appendc(link_flags, "--stack-first ");
+		// NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB.
+		link_flags = gb_string_appendc(link_flags, "-z stack-size=1048576 ");
+
 		// link_flags = gb_string_appendc(link_flags, "--export-all ");
 		// link_flags = gb_string_appendc(link_flags, "--export-table ");
 		// if (bc->metrics.arch == TargetArch_wasm64) {

+ 9 - 0
src/check_builtin.cpp

@@ -4713,6 +4713,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		}
 		break;
 
+	case BuiltinProc_read_cycle_counter_frequency:
+		if (build_context.metrics.arch != TargetArch_arm64) {
+			error(call, "'%.*s' is only allowed on arm64 targets", LIT(builtin_name));
+			return false;
+		}
+		operand->mode = Addressing_Value;
+		operand->type = t_i64;
+		break;
+
 	case BuiltinProc_read_cycle_counter:
 		operand->mode = Addressing_Value;
 		operand->type = t_i64;

+ 3 - 0
src/checker_builtin_procs.hpp

@@ -61,6 +61,7 @@ enum BuiltinProcId {
 	BuiltinProc_trap,
 	BuiltinProc_debug_trap,
 	BuiltinProc_read_cycle_counter,
+	BuiltinProc_read_cycle_counter_frequency,
 
 	BuiltinProc_count_ones,
 	BuiltinProc_count_zeros,
@@ -225,6 +226,7 @@ BuiltinProc__simd_end,
 	BuiltinProc_x86_cpuid,
 	BuiltinProc_x86_xgetbv,
 
+
 	// Constant type tests
 
 BuiltinProc__type_begin,
@@ -421,6 +423,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("trap"),               0, false, Expr_Expr, BuiltinProcPkg_intrinsics, /*diverging*/true},
 	{STR_LIT("debug_trap"),         0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
 	{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("count_ones"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("count_zeros"),          1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 18 - 1
src/llvm_backend_proc.cpp

@@ -1073,6 +1073,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
 
 	lbValue result = {};
 
+	isize ignored_args = 0;
 	auto processed_args = array_make<lbValue>(permanent_allocator(), 0, args.count);
 
 	{
@@ -1095,6 +1096,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
 			lbArgType *arg = &ft->args[param_index];
 			if (arg->kind == lbArg_Ignore) {
 				param_index += 1;
+				ignored_args += 1;
 				continue;
 			}
 
@@ -1203,7 +1205,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
 			auto tuple_fix_values = slice_make<lbValue>(permanent_allocator(), ret_count);
 			auto tuple_geps = slice_make<lbValue>(permanent_allocator(), ret_count);
 
-			isize offset = ft->original_arg_count;
+			isize offset = ft->original_arg_count - ignored_args;
 			for (isize j = 0; j < ret_count-1; j++) {
 				lbValue ret_arg_ptr = processed_args[offset + j];
 				lbValue ret_arg = lb_emit_load(p, ret_arg_ptr);
@@ -2809,6 +2811,21 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 			}
 			return res;
 		}
+	case BuiltinProc_read_cycle_counter_frequency:
+		{
+			lbValue res = {};
+			res.type = tv.type;
+
+			if (build_context.metrics.arch == TargetArch_arm64) {
+				LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+				bool has_side_effects = false;
+				LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects);
+				GB_ASSERT(the_asm != nullptr);
+				res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+			}
+
+			return res;
+		}
 
 	case BuiltinProc_count_trailing_zeros:
 		return lb_emit_count_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type);