1use super::*;
4use crate::abi;
5use crate::resolve;
6use core::ffi::c_void;
7use crate::mangle::{self, swift_type};
8
9pub type Persist<T> = std::sync::Arc<dyn Fn(&T) + Send + Sync>;
13
14pub struct BoundState<T> {
30 value: std::sync::Arc<std::sync::Mutex<T>>,
31 persist: Option<Persist<T>>,
37}
38
39impl<T: Clone> BoundState<T> {
40 pub fn new(initial: T) -> Self {
41 Self {
42 value: std::sync::Arc::new(std::sync::Mutex::new(initial)),
43 persist: None,
44 }
45 }
46
47 pub fn get(&self) -> T {
48 self.value.lock().unwrap().clone()
49 }
50
51 pub fn set(&self, v: T) {
52 if let Some(write) = &self.persist {
53 write(&v);
54 }
55 *self.value.lock().unwrap() = v;
56 }
57
58 pub fn is_persistent(&self) -> bool {
60 self.persist.is_some()
61 }
62}
63
64impl<T: crate::defaults::Persisted> BoundState<T> {
65 pub fn persistent(key: &str, default: T) -> Self {
77 let initial = <T as crate::defaults::Persisted>::load(key, default);
78 let owned = key.to_owned();
79 Self {
80 value: std::sync::Arc::new(std::sync::Mutex::new(initial)),
81 persist: Some(std::sync::Arc::new(move |v: &T| {
82 <T as crate::defaults::Persisted>::store(&owned, v);
83 })),
84 }
85 }
86}
87
88impl<T> Clone for BoundState<T> {
89 fn clone(&self) -> Self {
90 Self { value: self.value.clone(), persist: self.persist.clone() }
91 }
92}
93
94pub(crate) struct BindingHooks {
96 get: Box<dyn Fn(*mut u8)>,
97 set: Box<dyn Fn(*const u8)>,
98}
99
100pub(crate) unsafe extern "C" fn binding_get_body(out: *mut u8, payload: *mut c_void) {
101 let hooks = unsafe { &*(payload as *const BindingHooks) };
102 (hooks.get)(out);
103}
104
105pub(crate) unsafe extern "C" fn binding_set_body(value: *const u8, payload: *mut c_void) {
106 let hooks = unsafe { &*(payload as *const BindingHooks) };
107 (hooks.set)(value);
108 if let Some(f) = ON_WRITE.get() {
113 f();
114 }
115}
116
117static ON_WRITE: std::sync::OnceLock<Box<dyn Fn() + Send + Sync>> =
119 std::sync::OnceLock::new();
120
121pub fn on_write(f: impl Fn() + Send + Sync + 'static) {
131 let _ = ON_WRITE.set(Box::new(f));
132}
133
134pub(crate) unsafe fn binding_get_set(
139 value_meta: *const c_void,
140 binding_mangled: &[u8],
141 get: Box<dyn Fn(*mut u8)>,
142 set: Box<dyn Fn(*const u8)>,
143) -> Vec<u8> {
144 unsafe {
145 let hooks = Box::leak(Box::new(BindingHooks { get, set })) as *mut BindingHooks;
146 let get_ctx = Box::leak(Box::new(abi::ClosureCtx::new(
147 binding_get_body,
148 hooks as *mut c_void,
149 ))) as *mut abi::ClosureCtx;
150 let set_ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
151 binding_set_body,
152 hooks as *mut c_void,
153 ))) as *mut abi::ValueClosureCtx;
154
155 let binding_meta = abi::resolve_type_by_mangled_name(binding_mangled);
156 assert!(!binding_meta.is_null(), "Binding metadata");
157 let n = abi::type_stride(binding_meta).max(abi::type_size(binding_meta));
158 let mut out = vec![0u8; n];
159 core::arch::asm!("blr {f}", f = in(reg) resolve::binding_get_set(),
163 in("x0") abi::swift_closure_thunk as *const c_void,
164 in("x1") get_ctx,
165 in("x2") abi::value_closure_thunk as *const c_void,
166 in("x3") set_ctx,
167 in("x4") value_meta,
168 in("x8") out.as_mut_ptr(),
169 lateout("x5") _, lateout("x6") _, lateout("x7") _,
170 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
171 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
172 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
173 clobber_abi("C"),
174 );
175 out
176 }
177}
178
179pub fn toggle_bound(label: &str, state: &BoundState<bool>) -> ViewHandle {
182 unsafe {
183 let g = state.clone();
184 let sset = state.clone();
185 let binding = binding_get_set(
186 resolve::bool_meta(),
187 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
188 Box::new(move |out| *out = u8::from(g.get())),
189 Box::new(move |v| sset.set(*v != 0)),
190 );
191 toggle_with_binding(label, &binding)
192 }
193}
194
195pub fn toggle_app_storage(label: &str, stored: &crate::state::StoredView<bool>) -> ViewHandle {
203 unsafe { toggle_with_binding(label, &stored.binding()) }
204}
205
206pub fn slider_app_storage(
208 stored: &crate::state::StoredView<f64>,
209 lower: f64,
210 upper: f64,
211) -> ViewHandle {
212 unsafe {
213 let mut binding = stored.binding();
214 super::slider_with_binding(&mut binding, lower, upper)
215 }
216}
217
218pub fn stepper_app_storage(
221 label: &str,
222 stored: &crate::state::StoredView<f64>,
223 lower: f64,
224 upper: f64,
225 step: f64,
226) -> ViewHandle {
227 unsafe {
228 let mut binding = stored.binding();
229 super::stepper_with_binding(label, &mut binding, lower, upper, step)
230 }
231}
232
233pub(crate) unsafe fn scalar_binding<T, const N: usize>(
243 state: &BoundState<T>,
244 meta: *const c_void,
245 mangled: &[u8],
246 to_bytes: fn(&T) -> [u8; N],
247 from_bytes: fn([u8; N]) -> T,
248) -> Vec<u8>
249where
250 T: Clone + Send + 'static,
251{
252 let g = state.clone();
253 let s = state.clone();
254 unsafe {
255 binding_get_set(
256 meta,
257 mangled,
258 Box::new(move |out| {
259 let v = to_bytes(&g.get());
260 core::ptr::copy_nonoverlapping(v.as_ptr(), out, N);
261 }),
262 Box::new(move |v| {
263 let mut b = [0u8; N];
264 core::ptr::copy_nonoverlapping(v, b.as_mut_ptr(), N);
265 s.set(from_bytes(b));
266 }),
267 )
268 }
269}
270
271pub(crate) unsafe fn bool_binding_of(state: &BoundState<bool>) -> Vec<u8> {
276 unsafe {
277 scalar_binding::<bool, 1>(
278 state,
279 resolve::bool_meta(),
280 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
281 |v| [u8::from(*v)],
282 |b| b[0] != 0,
283 )
284 }
285}
286
287pub(crate) unsafe fn f64_binding_of(state: &BoundState<f64>) -> Vec<u8> {
292 unsafe {
293 scalar_binding::<f64, 8>(
294 state,
295 resolve::double_meta(),
296 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DOUBLE]).as_bytes(),
297 |v| v.to_le_bytes(),
298 f64::from_le_bytes,
299 )
300 }
301}
302
303pub(crate) unsafe fn i64_binding_of(state: &BoundState<i64>) -> Vec<u8> {
308 unsafe {
309 scalar_binding::<i64, 8>(
310 state,
311 resolve::int_meta(),
312 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::INT]).as_bytes(),
313 |v| v.to_le_bytes(),
314 i64::from_le_bytes,
315 )
316 }
317}
318
319pub fn slider_bound(state: &BoundState<f64>, lower: f64, upper: f64) -> ViewHandle {
326 unsafe {
327 let mut binding = f64_binding_of(state);
328 super::slider_with_binding(&mut binding, lower, upper)
329 }
330}
331
332pub fn slider_step_bound(
337 state: &BoundState<f64>,
338 lower: f64,
339 upper: f64,
340 step: f64,
341) -> ViewHandle {
342 unsafe {
343 let mut binding = f64_binding_of(state);
344 super::slider_step_with_binding(&mut binding, lower, upper, step)
345 }
346}
347
348pub fn stepper_bound(
353 label: &str,
354 state: &BoundState<f64>,
355 lower: f64,
356 upper: f64,
357 step: f64,
358) -> ViewHandle {
359 unsafe {
360 let mut binding = f64_binding_of(state);
361 super::stepper_with_binding(label, &mut binding, lower, upper, step)
362 }
363}
364
365pub(crate) unsafe fn toggle_with_binding(label: &str, binding: &[u8]) -> ViewHandle {
367 unsafe {
368 let ss = abi::swift_string(label);
369 let s0 = u64::from_le_bytes(ss[..8].try_into().unwrap());
370 let s1 = u64::from_le_bytes(ss[8..].try_into().unwrap());
371 let (k0, k1, k2, k3) = localized_string_key(s0, s1);
372
373 let b0 = u64::from_le_bytes(binding[0..8].try_into().unwrap());
374 let b1 = u64::from_le_bytes(binding[8..16].try_into().unwrap());
375 let b2 = binding[16] as u64;
376
377 let (meta, wt, size) = result_type(&TOGGLE_T);
378 let mut result = vec![0u8; size];
379 core::arch::asm!("blr {f}", f = in(reg) resolve::toggle_init(),
380 in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
381 in("x4") b0, in("x5") b1, in("x6") b2,
382 in("x8") result.as_mut_ptr(),
383 in("x20") meta,
384 lateout("x7") _,
385 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
386 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
387 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
388 );
389 wrap_owned(result, meta, wt)
390 }
391}
392
393pub unsafe fn binding_write(binding: &mut [u8], value: &mut [u8], mangled: &[u8]) {
405 unsafe {
406 let meta = abi::resolve_type_by_mangled_name(mangled);
407 assert!(!meta.is_null(), "binding type metadata");
408 core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
409 in("x0") value.as_mut_ptr(),
410 in("x1") meta,
411 in("x20") binding.as_mut_ptr(),
412 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
413 lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
414 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
415 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
416 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
417 );
418 }
419}
420
421pub fn binding_write_bool(binding: &mut [u8], value: bool) {
422 unsafe {
423 let mut v = [u8::from(value)];
424 let meta = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL]).as_bytes());
425 core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
426 in("x0") v.as_mut_ptr(),
427 in("x1") meta,
428 in("x20") binding.as_mut_ptr(),
429 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
430 lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
431 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
432 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
433 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
434 );
435 }
436}
437
438pub fn text_binding(state: &BoundState<String>) -> Vec<u8> {
448 let g = state.clone();
449 let s = state.clone();
450 unsafe {
451 binding_get_set(
452 resolve::string_meta(),
453 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::STRING]).as_bytes(),
454 Box::new(move |out| {
455 let bytes = abi::swift_string(&g.get());
456 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out, 16);
457 }),
458 Box::new(move |v| {
459 let mut bytes = [0u8; 16];
460 core::ptr::copy_nonoverlapping(v, bytes.as_mut_ptr(), 16);
461 s.set(abi::read_swift_string(bytes));
462 }),
463 )
464 }
465}
466
467pub fn f64_binding(state: &BoundState<f64>) -> Vec<u8> {
469 let g = state.clone();
470 let s = state.clone();
471 unsafe {
472 binding_get_set(
473 resolve::double_meta(),
474 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DOUBLE]).as_bytes(),
475 Box::new(move |out| {
476 let v = g.get();
477 core::ptr::copy_nonoverlapping(v.to_le_bytes().as_ptr(), out, 8);
478 }),
479 Box::new(move |v| {
480 let mut b = [0u8; 8];
481 core::ptr::copy_nonoverlapping(v, b.as_mut_ptr(), 8);
482 s.set(f64::from_le_bytes(b));
483 }),
484 )
485 }
486}
487
488pub fn optional_i64_binding(state: &BoundState<Option<i64>>) -> Vec<u8> {
524 let g = state.clone();
525 let s = state.clone();
526 unsafe {
527 let payload = resolve::int_meta();
528 let opt = abi::resolve_type_by_mangled_name(b"SiSg");
529 assert!(!opt.is_null(), "Optional<Int> metadata");
530 let width = abi::type_bytes(opt);
531 binding_get_set(
532 opt,
533 mangle::bound(&swift_type!(SwiftUI.Binding), &[&mangle::optional(mangle::INT)])
534 .as_bytes(),
535 Box::new(move |out| {
536 let slot = core::slice::from_raw_parts_mut(out, width);
537 slot.fill(0);
538 match g.get() {
539 Some(v) => {
540 slot[..8].copy_from_slice(&v.to_le_bytes());
541 abi::store_optional_tag(slot, payload, true);
542 }
543 None => abi::store_optional_tag(slot, payload, false),
544 }
545 }),
546 Box::new(move |v| {
547 let slot = core::slice::from_raw_parts(v, width);
548 let present = abi::read_optional_tag(slot, payload);
549 s.set(present.then(|| {
550 let mut b = [0u8; 8];
551 b.copy_from_slice(&slot[..8]);
552 i64::from_le_bytes(b)
553 }));
554 }),
555 )
556 }
557}
558
559pub fn i64_binding(state: &BoundState<i64>) -> Vec<u8> {
560 let g = state.clone();
561 let s = state.clone();
562 unsafe {
563 binding_get_set(
564 resolve::int_meta(),
565 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::INT]).as_bytes(),
566 Box::new(move |out| {
567 let v = g.get();
568 core::ptr::copy_nonoverlapping(v.to_le_bytes().as_ptr(), out, 8);
569 }),
570 Box::new(move |v| {
571 let mut b = [0u8; 8];
572 core::ptr::copy_nonoverlapping(v, b.as_mut_ptr(), 8);
573 s.set(i64::from_le_bytes(b));
574 }),
575 )
576 }
577}
578
579pub fn bool_binding(state: &BoundState<bool>) -> Vec<u8> {
580 let g = state.clone();
581 let s = state.clone();
582 unsafe {
583 binding_get_set(
584 resolve::bool_meta(),
585 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
586 Box::new(move |out| *out = u8::from(g.get())),
587 Box::new(move |v| s.set(*v != 0)),
588 )
589 }
590}
591
592swift_cases! {
593 pub enum HorizontalEdge {
595 Leading = 0,
596 Trailing = 1,
597 }
598}
599
600unsafe fn presenting_modifier(
606 func: *const c_void,
607 oret: *const c_void,
608 view: &ViewHandle,
609 before: &[u64],
610 state: &BoundState<bool>,
611 content: &ViewHandle,
612) -> ViewHandle {
613 unsafe {
614 let mut b = bool_binding(state);
615 let w = binding_words(&mut b);
616 let mut ints = before.to_vec();
617 ints.extend_from_slice(&w);
618 ints.extend_from_slice(&view_words(content));
619 generic1_modifier_ints(
620 func,
621 oret,
622 view,
623 content.meta,
624 content.wt,
625 &ints,
626 [0.0; 4],
627 )
628 }
629}
630
631pub fn navigation_destination(
633 view: &ViewHandle,
634 state: &BoundState<bool>,
635 destination: &ViewHandle,
636) -> ViewHandle {
637 unsafe {
638 presenting_modifier(
639 resolve::navigation_destination_fn(),
640 resolve::navigation_destination_oret(),
641 view,
642 &[],
643 state,
644 destination,
645 )
646 }
647}
648
649pub fn inspector(
651 view: &ViewHandle,
652 state: &BoundState<bool>,
653 content: &ViewHandle,
654) -> ViewHandle {
655 unsafe {
656 presenting_modifier(
657 resolve::inspector_fn(),
658 resolve::inspector_oret(),
659 view,
660 &[],
661 state,
662 content,
663 )
664 }
665}
666
667pub fn swipe_actions(
673 view: &ViewHandle,
674 edge: HorizontalEdge,
675 allows_full_swipe: bool,
676 content: &ViewHandle,
677) -> ViewHandle {
678 unsafe {
679 let e = abi::enum_argument(abi::metadata0(resolve::horizontal_edge_ma()), edge.tag());
680 let payload = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL]).as_bytes());
681 let opt = abi::resolve_type_by_mangled_name(mangle::optional(&mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL])).as_bytes());
682 let mut nil = vec![0u8; abi::type_bytes(opt).max(24)];
683 abi::store_optional_tag(&mut nil, payload, false);
684 let mut ints = vec![e, u64::from(allows_full_swipe)];
685 ints.extend_from_slice(&binding_words(&mut nil));
686 ints.extend_from_slice(&view_words(content));
687 generic1_modifier_ints(
688 resolve::swipe_actions_fn(),
689 resolve::swipe_actions_oret(),
690 view,
691 content.meta,
692 content.wt,
693 &ints,
694 [0.0; 4],
695 )
696 }
697}
698
699pub fn popover_anchored(
710 view: &ViewHandle,
711 state: &BoundState<bool>,
712 anchor: (f64, f64),
713 arrow_edge: Edge,
714 content: &ViewHandle,
715) -> ViewHandle {
716 unsafe {
717 let mut b = bool_binding(state);
718 let w = binding_words(&mut b);
719 let anchor_meta = abi::metadata0(resolve::popover_anchor_ma());
720 let mut buf = vec![0u8; abi::type_bytes(anchor_meta)];
721 buf[..8].copy_from_slice(&anchor.0.to_le_bytes());
722 buf[8..16].copy_from_slice(&anchor.1.to_le_bytes());
723 abi::inject_enum_tag(&mut buf, anchor_meta, 1);
724 let edge = abi::enum_argument(abi::metadata0(resolve::edge_ma()), arrow_edge as u32);
727 let mut ints = w.to_vec();
728 ints.push(buf.as_ptr() as u64);
729 ints.push(edge);
730 ints.extend_from_slice(&view_words(content));
731 generic1_modifier_ints(
732 resolve::popover_fn(),
733 resolve::popover_oret(),
734 view,
735 content.meta,
736 content.wt,
737 &ints,
738 [0.0; 4],
739 )
740 }
741}
742
743#[derive(Clone, Copy, Debug, PartialEq, Eq)]
748pub enum Edge {
749 Top = 0,
750 Leading = 1,
751 Bottom = 2,
752 Trailing = 3,
753}
754
755
756pub fn toggle_labelled(label: &ViewHandle, state: &BoundState<bool>) -> ViewHandle {
764 unsafe {
765 let binding = bool_binding_of(state);
766 let b0 = u64::from_le_bytes(binding[0..8].try_into().expect("binding 0"));
767 let b1 = u64::from_le_bytes(binding[8..16].try_into().expect("binding 1"));
768 let b2 = binding[16] as u64;
769
770 let (known_meta, known_wt, known_size) = super::result_type(&super::TOGGLE_ANY_T);
772 let typed = super::substitute_args(known_meta, 2, &[(0, label.meta)], &[(1, label.wt)]);
773 let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
774 let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
775 let (label_meta, label_wt) = match typed {
776 Some(_) => (label.meta, label.wt),
777 None => (resolve::anyview_meta(), resolve::anyview_wt()),
778 };
779 let kind = if typed.is_some() { super::Content::Concrete } else { super::Content::Erased };
780
781 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
784 super::emit_tuple,
785 super::Child::leak(label, kind),
786 ))) as *mut abi::ClosureCtx;
787
788 let mut result = vec![0u8; size];
789 core::arch::asm!("blr {f}", f = in(reg) resolve::toggle_label_init(),
797 in("x0") b0, in("x1") b1, in("x2") b2,
798 in("x3") abi::swift_closure_thunk as *const c_void,
799 in("x4") ctx,
800 in("x5") label_meta,
801 in("x6") label_wt,
802 in("x8") result.as_mut_ptr(),
803 in("x20") meta,
804 lateout("x7") _,
805 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
806 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
807 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
808 );
809 wrap_owned(result, meta, wt)
810 }
811}
812
813
814pub fn toggle_sources(label: &str, states: &[&BoundState<bool>]) -> ViewHandle {
832 assert!(!states.is_empty(), "a Toggle over sources needs at least one");
833 unsafe {
834 let ss = abi::swift_string(label);
835 let s0 = u64::from_le_bytes(ss[..8].try_into().expect("label lo"));
836 let s1 = u64::from_le_bytes(ss[8..].try_into().expect("label hi"));
837 let (k0, k1, k2, k3) = localized_string_key(s0, s1);
838
839 let element = abi::resolve_type_by_mangled_name(b"7SwiftUI7BindingVySbG");
840 assert!(!element.is_null(), "Binding<Bool> metadata");
841 let stride = abi::type_bytes(element);
842 let mut bytes = vec![0u8; stride * states.len()];
843 for (i, state) in states.iter().enumerate() {
844 let b = bool_binding_of(state);
845 bytes[i * stride..i * stride + b.len()].copy_from_slice(&b);
846 }
847 let array = abi::swift_array(element, states.len(), &bytes);
848
849 let array_meta = abi::resolve_type_by_mangled_name(b"Say7SwiftUI7BindingVySbGG");
850 assert!(!array_meta.is_null(), "Array<Binding<Bool>> metadata");
851 let rac = super::conformance_wt(array_meta, resolve::random_access_proto());
852 assert!(!rac.is_null(), "Array<Binding<Bool>>: RandomAccessCollection");
853
854 let (meta, wt, size) = super::result_type(&super::TOGGLE_T);
855 let mut result = vec![0u8; size];
856 core::arch::asm!("blr {f}", f = in(reg) resolve::toggle_sources_init(),
865 in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
866 in("x4") &array as *const u64,
867 in("x5") array_meta,
868 in("x6") rac,
869 in("x8") result.as_mut_ptr(),
870 in("x20") meta,
871 lateout("x7") _,
872 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
873 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
874 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
875 );
876 wrap_owned(result, meta, wt)
877 }
878}