diff --git a/src/host/readback.rs b/src/host/readback.rs
index 72126391..9d755b4b 100644
--- a/src/host/readback.rs
+++ b/src/host/readback.rs
@@ -26,6 +26,7 @@ impl Host {
     net
   }
 }
+
 /// See [`Host::readback`].
 struct ReadbackState<'a> {
   host: &'a Host,
diff --git a/src/main.rs b/src/main.rs
index f5f7b9e2..6edcb866 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -353,7 +353,7 @@ fn reduce_exprs(host: &Host, exprs: &[Net], opts: &RuntimeOpts) {
   for expr in exprs {
     let mut net = DynNet::new(&heap, opts.lazy_mode);
     dispatch_dyn_net!(&mut net => {
-      host.encode_net(net, Trg::port(run::Port::new_var(net.root.addr())), expr);
+      host.encode_net(net, Trg::port(net.root.as_var()), expr);
       let start_time = Instant::now();
       if opts.single_core {
         net.normal();
diff --git a/src/run.rs b/src/run.rs
index ae0790cd..ad0a1f4c 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -62,6 +62,7 @@ mod net;
 mod node;
 mod parallel;
 mod port;
+mod tag;
 mod wire;
 
 pub use addr::*;
@@ -72,6 +73,7 @@ pub use linker::*;
 pub use net::*;
 pub use node::*;
 pub use port::*;
+pub use tag::*;
 pub use wire::*;
 
 pub type Lab = u16;
diff --git a/src/run/addr.rs b/src/run/addr.rs
index c27eac8d..f19515f2 100644
--- a/src/run/addr.rs
+++ b/src/run/addr.rs
@@ -37,17 +37,21 @@ impl Addr {
 
   const HALF_MASK: usize = 0b1000;
 
-  /// Given an address to one word of a two-word allocation, returns the address
-  /// of the first word of that allocation.
+  /// TODO
   #[inline(always)]
-  pub(super) fn left_half(&self) -> Self {
-    Addr(self.0 & !Addr::HALF_MASK)
+  pub(super) fn floor(&self, align: Align) -> Self {
+    Addr(self.0 & (usize::MAX << align.tag_bits()))
   }
 
-  /// Given an address to one word of a two-word allocation, returns the address
-  /// of the other word of that allocation.
+  /// TODO
   #[inline(always)]
-  pub fn other_half(&self) -> Self {
-    Addr(self.0 ^ Addr::HALF_MASK)
+  pub(super) fn other(&self, align: Align) -> Self {
+    Addr(self.0 ^ (1 << align.tag_bits()))
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub(super) fn offset(&self, words: usize) -> Self {
+    Addr(self.0 + (words << 3))
   }
 }
diff --git a/src/run/allocator.rs b/src/run/allocator.rs
index 308207a2..95139a53 100644
--- a/src/run/allocator.rs
+++ b/src/run/allocator.rs
@@ -1,27 +1,17 @@
 use super::*;
 
-/// The memory behind a two-word allocation.
-///
-/// This must be aligned to 16 bytes so that the left word's address always ends
-/// with `0b0000` and the right word's address always ends with `0b1000`.
-#[repr(C)]
-#[repr(align(16))]
-#[derive(Default)]
-pub(super) struct Node(pub AtomicU64, pub AtomicU64);
-
 /// The memory buffer backing a [`Net`].
-#[repr(align(16))]
-pub struct Heap(pub(super) [Node]);
+#[repr(align(64))]
+pub struct Heap(pub(super) [AtomicU64]);
 
 impl Heap {
   #[inline]
   /// Allocate a new heap with a given size in words.
   pub fn new_words(words: usize) -> Box<Self> {
-    let nodes = words / 2;
     unsafe {
       Box::from_raw(core::ptr::slice_from_raw_parts_mut(
-        alloc::alloc(Layout::array::<Node>(nodes).unwrap()) as *mut _,
-        nodes,
+        alloc::alloc(Layout::array::<AtomicU64>(words).unwrap().align_to(64).unwrap()) as *mut _,
+        words,
       ) as *mut _)
     }
   }
@@ -37,66 +27,101 @@ pub struct Allocator<'h> {
   pub(super) tracer: Tracer,
   pub(super) heap: &'h Heap,
   pub(super) next: usize,
-  pub(super) head: Addr,
+  pub(super) heads: [Addr; 4],
 }
 
 deref!({<'h>} Allocator<'h> => self.tracer: Tracer);
 
+impl Align {
+  #[inline(always)]
+  const fn free(self) -> u64 {
+    (1 << self as u64) << 60
+  }
+}
+
+/// Sentinel values used to indicate free memory.
+impl Port {
+  pub(super) const FREE_1: Port = Port(Align1.free());
+  pub(super) const FREE_2: Port = Port(Align2.free());
+  pub(super) const FREE_4: Port = Port(Align4.free());
+  pub(super) const FREE_8: Port = Port(Align8.free());
+}
+
 impl<'h> Allocator<'h> {
   pub fn new(heap: &'h Heap) -> Self {
-    Allocator { tracer: Tracer::default(), heap, next: 0, head: Addr::NULL }
+    Allocator { tracer: Tracer::default(), heap, next: 0, heads: [Addr::NULL; 4] }
+  }
+
+  fn head(&mut self, align: Align) -> &mut Addr {
+    unsafe { self.heads.get_unchecked_mut(align as usize) }
   }
 
-  /// Frees one word of a two-word allocation.
+  fn push_addr(head: &mut Addr, addr: Addr) {
+    addr.val().store(head.0 as u64, Relaxed);
+    *head = addr;
+  }
+
+  /// Frees one word of an allocation of size `alloc_align`.
   #[inline(always)]
-  pub fn half_free(&mut self, addr: Addr) {
-    trace!(self.tracer, addr);
-    const FREE: u64 = Port::FREE.0;
+  pub fn free_word(&mut self, mut addr: Addr, alloc_align: Align) {
     if cfg!(feature = "_fuzz") {
       if cfg!(not(feature = "_fuzz_no_free")) {
-        assert_ne!(addr.val().swap(FREE, Relaxed), FREE, "double free");
+        let free = Port::FREE_1.0;
+        assert_ne!(addr.val().swap(free, Relaxed), free, "double free");
       }
-    } else {
-      addr.val().store(FREE, Relaxed);
-      if addr.other_half().val().load(Relaxed) == FREE {
-        trace!(self.tracer, "other free");
-        let addr = addr.left_half();
-        if addr.val().compare_exchange(FREE, self.head.0 as u64, Relaxed, Relaxed).is_ok() {
-          let old_head = &self.head;
-          let new_head = addr;
-          trace!(self.tracer, "appended", old_head, new_head);
-          self.head = new_head;
-        } else {
-          trace!(self.tracer, "too slow");
-        };
+      return;
+    }
+    let mut align = Align1;
+    if align == alloc_align {
+      return Self::push_addr(self.head(align), addr);
+    }
+    addr.val().store(align.free(), Relaxed);
+    while align != alloc_align {
+      if addr.other(align).val().load(Relaxed) != align.free() {
+        return;
+      }
+      trace!(self.tracer, "other free");
+      let next_align = unsafe { align.next().unwrap_unchecked() };
+      addr = addr.floor(next_align);
+      let next_value = if next_align == alloc_align { self.head(alloc_align).0 as u64 } else { next_align.free() };
+      if addr.val().compare_exchange(align.free(), next_value, Relaxed, Relaxed).is_err() {
+        return trace!(self.tracer, "too slow");
       }
+      trace!(self.tracer, "success");
+      if next_align == alloc_align {
+        let old_head = next_value;
+        let new_head = addr;
+        trace!(self.tracer, "appended", old_head, new_head);
+        return *self.head(align) = addr;
+      }
+      align = next_align;
     }
   }
 
-  /// Allocates a two-word node.
+  /// Allocates a node, with a size specified by `align`.
   #[inline(never)]
-  pub fn alloc(&mut self) -> Addr {
-    trace!(self.tracer, self.head);
-    let addr = if self.head != Addr::NULL {
-      let addr = self.head.clone();
-      let next = Addr(self.head.val().load(Relaxed) as usize);
-      trace!(self.tracer, next);
-      self.head = next;
+  pub fn alloc(&mut self, align: Align) -> Addr {
+    let head = self.head(align);
+    let addr = if *head != Addr::NULL {
+      let addr = *head;
+      let next = Addr(head.val().load(Relaxed) as usize);
+      *head = next;
       addr
     } else {
       let index = self.next;
-      self.next += 1;
-      Addr(&self.heap.0.get(index).expect("OOM").0 as *const _ as _)
+      self.next += 8;
+      Addr(&self.heap.0.get(index).expect("OOM") as *const _ as _)
     };
-    trace!(self.tracer, addr, self.head);
-    addr.val().store(Port::LOCK.0, Relaxed);
-    addr.other_half().val().store(Port::LOCK.0, Relaxed);
+    trace!(self, addr);
+    for i in 0 .. align.width() {
+      addr.offset(i as usize).val().store(Port::LOCK.0, Relaxed);
+    }
     addr
   }
 
   #[inline(always)]
   pub(crate) fn free_wire(&mut self, wire: Wire) {
-    self.half_free(wire.addr());
+    self.free_word(wire.addr(), wire.alloc_align());
   }
 
   /// If `trg` is a wire, frees the backing memory.
diff --git a/src/run/def.rs b/src/run/def.rs
index 0c947def..cf9d8473 100644
--- a/src/run/def.rs
+++ b/src/run/def.rs
@@ -186,7 +186,7 @@ impl<'a, M: Mode> Net<'a, M> {
 
     let def = port.addr().def();
 
-    if trg.tag() == Ctr && !def.labs.has(trg.lab()) {
+    if trg.is_ctr_ish() && !def.labs.has(trg.lab()) {
       return self.comm02(port, trg);
     }
 
diff --git a/src/run/instruction.rs b/src/run/instruction.rs
index 7aaf3787..feacbf33 100644
--- a/src/run/instruction.rs
+++ b/src/run/instruction.rs
@@ -112,14 +112,14 @@ impl<'a, M: Mode> Net<'a, M> {
   #[inline(always)]
   pub(crate) fn do_ctr(&mut self, lab: Lab, trg: Trg) -> (Trg, Trg) {
     let port = trg.target();
-    if !M::LAZY && port.tag() == Ctr && port.lab() == lab {
-      trace!(self.tracer, "fast");
+    if !M::LAZY && port.is(Tag::Ctr) && port.lab() == lab {
+      trace!(self, "fast");
       self.free_trg(trg);
       let node = port.consume_node();
       self.rwts.anni += 1;
       (Trg::wire(node.p1), Trg::wire(node.p2))
     // TODO: fast copy?
-    } else if false && !M::LAZY && port.tag() == Num || port.tag() == Ref && lab >= port.lab() {
+    } else if false && !M::LAZY && port.is(Tag::Num) || port.is(Tag::Ref) && lab >= port.lab() {
       self.rwts.comm += 1;
       (Trg::port(port.clone()), Trg::port(port))
     } else {
@@ -170,7 +170,7 @@ impl<'a, M: Mode> Net<'a, M> {
   #[inline(always)]
   pub(crate) fn do_mat(&mut self, trg: Trg) -> (Trg, Trg) {
     let port = trg.target();
-    if !M::LAZY && port.tag() == Num {
+    if !M::LAZY && port.is(Tag::Num) {
       self.rwts.oper += 1;
       self.free_trg(trg);
       let num = port.num();
@@ -198,13 +198,13 @@ impl<'a, M: Mode> Net<'a, M> {
 
   #[inline(always)]
   pub(crate) fn do_wires(&mut self) -> (Trg, Trg, Trg, Trg) {
-    let a = self.alloc();
-    let b = a.other_half();
+    let a = self.alloc(Align2);
+    let b = a.offset(1);
     (
-      Trg::port(Port::new_var(a.clone())),
-      Trg::wire(Wire::new(a)),
-      Trg::port(Port::new_var(b.clone())),
-      Trg::wire(Wire::new(b)),
+      Trg::port(Port::new_var(Align2, a.clone())),
+      Trg::wire(Wire::new(Align2, a)),
+      Trg::port(Port::new_var(Align2, b.clone())),
+      Trg::wire(Wire::new(Align2, b)),
     )
   }
 
@@ -213,7 +213,7 @@ impl<'a, M: Mode> Net<'a, M> {
   #[allow(unused)] // TODO: emit this instruction
   pub(crate) fn do_mat_con_con(&mut self, trg: Trg, out: Trg) -> (Trg, Trg, Trg) {
     let port = trg.target();
-    if !M::LAZY && trg.target().tag() == Num {
+    if !M::LAZY && trg.target().is(Tag::Num) {
       self.rwts.oper += 1;
       self.free_trg(trg);
       let num = port.num();
@@ -241,7 +241,7 @@ impl<'a, M: Mode> Net<'a, M> {
   #[allow(unused)] // TODO: emit this instruction
   pub(crate) fn do_mat_con(&mut self, trg: Trg, out: Trg) -> (Trg, Trg) {
     let port = trg.target();
-    if trg.target().tag() == Num {
+    if trg.target().is(Tag::Num) {
       self.rwts.oper += 1;
       self.free_trg(trg);
       let num = port.num();
diff --git a/src/run/interact.rs b/src/run/interact.rs
index 80c5e3bb..1590bddd 100644
--- a/src/run/interact.rs
+++ b/src/run/interact.rs
@@ -4,42 +4,47 @@ impl<'a, M: Mode> Net<'a, M> {
   /// Performs an interaction between two connected principal ports.
   #[inline(always)]
   pub fn interact(&mut self, a: Port, b: Port) {
-    self.tracer.sync();
-    trace!(self.tracer, a, b);
+    self.sync();
+    trace!(self, a, b);
+    use Tag::*;
     match (a.tag(), b.tag()) {
       // not actually an active pair
       (Var | Red, _) | (_, Var | Red) => unreachable!(),
       // nil-nil
       (Ref, Ref | Num) if !a.is_skippable() => self.call(a, b),
       (Ref | Num, Ref) if !b.is_skippable() => self.call(b, a),
-      (Num | Ref, Num | Ref) => self.rwts.eras += 1,
+      (Num | Ref | AdtZ, Num | Ref | AdtZ) => self.rwts.eras += 1,
       // comm 2/2
-      (Ctr, Mat) if a.lab() != 0 => self.comm22(a, b),
-      (Mat, Ctr) if b.lab() != 0 => self.comm22(a, b),
-      (Ctr, Op) | (Op, Ctr) => self.comm22(a, b),
-      (Ctr, Ctr) if a.lab() != b.lab() => self.comm22(a, b),
+      (Mat | Op, CtrN!() | AdtN!()) => self.comm2N(a, b),
+      (CtrN!() | AdtN!(), Mat | Op) => self.comm2N(b, a),
       // anni
-      (Mat, Mat) | (Op, Op) | (Ctr, Ctr) => self.anni2(a, b),
+      (CtrN!(), CtrN!()) => self.anniNM(a, b),
+      (Mat, Mat) | (Op, Op) => self.anni2(a, b),
       // comm 2/0
-      (Ref, Ctr) if b.lab() >= a.lab() => self.comm02(a, b),
-      (Ctr, Ref) if a.lab() >= b.lab() => self.comm02(b, a),
-      (Num, Ctr) => self.comm02(a, b),
-      (Ctr, Num) => self.comm02(b, a),
-      (Ref, _) if a == Port::ERA => self.comm02(a, b),
-      (_, Ref) if b == Port::ERA => self.comm02(b, a),
+      (Ref, CtrN!() | AdtN!()) if b.lab() >= a.lab() => self.comm0N(a, b),
+      (CtrN!() | AdtN!(), Ref) if a.lab() >= b.lab() => self.comm0N(b, a),
+      (Num, CtrN!() | AdtN!()) => self.comm0N(a, b),
+      (CtrN!() | AdtN!(), Num) => self.comm0N(b, a),
+      (AdtZ, Op | Mat) => self.comm02(a, b),
+      (Op | Mat, AdtZ) => self.comm02(a, b),
+      // TODO
+      (AdtN!() | AdtZ, CtrN!()) => self.adt_ctr(a, b),
+      (CtrN!(), AdtN!() | AdtZ) => self.adt_ctr(b, a),
+      (AdtN!() | AdtZ, AdtN!() | AdtZ) => todo!(),
       // deref
-      (Ref, _) => self.call(a, b),
-      (_, Ref) => self.call(b, a),
+      (Ref, Mat | Op) if a == Port::ERA => self.comm02(a, b),
+      (Mat | Op, Ref) if b == Port::ERA => self.comm02(b, a),
+      (Ref, CtrN!() | AdtN!()) if a == Port::ERA => self.comm0N(a, b),
+      (CtrN!() | AdtN!(), Ref) if b == Port::ERA => self.comm0N(b, a),
+      (Ref, CtrN!() | AdtN!() | Mat | Op) => self.call(a, b),
+      (CtrN!() | AdtN!() | Mat | Op, Ref) => self.call(b, a),
       // native ops
-      (Op, Num) => self.op_num(a, b),
-      (Num, Op) => self.op_num(b, a),
+      (Op, Num) => self.opr_num(a, b),
+      (Num, Op) => self.opr_num(b, a),
       (Mat, Num) => self.mat_num(a, b),
       (Num, Mat) => self.mat_num(b, a),
       // todo: what should the semantics of these be?
-      (Mat, Ctr) // b.lab() == 0
-      | (Ctr, Mat) // a.lab() == 0
-      | (Op, Mat)
-      | (Mat, Op) => unimplemented!("{:?}-{:?}", a.tag(), b.tag()),
+      (Op, Mat) | (Mat, Op) => unimplemented!("{:?}-{:?}", a.tag(), b.tag()),
     }
   }
 
@@ -72,7 +77,7 @@ impl<'a, M: Mode> Net<'a, M> {
   /// ```
   #[inline(never)]
   pub fn anni2(&mut self, a: Port, b: Port) {
-    trace!(self.tracer, a, b);
+    trace!(self, a, b);
     self.rwts.anni += 1;
     let a = a.consume_node();
     let b = b.consume_node();
@@ -119,7 +124,7 @@ impl<'a, M: Mode> Net<'a, M> {
   /// ```
   #[inline(never)]
   pub fn comm22(&mut self, a: Port, b: Port) {
-    trace!(self.tracer, a, b);
+    trace!(self, a, b);
     self.rwts.comm += 1;
 
     let a = a.consume_node();
@@ -130,13 +135,13 @@ impl<'a, M: Mode> Net<'a, M> {
     let B1 = self.create_node(b.tag, b.lab);
     let B2 = self.create_node(b.tag, b.lab);
 
-    trace!(self.tracer, A1.p0, A2.p0, B1.p0, B2.p0);
+    trace!(self, A1.p0, A2.p0, B1.p0, B2.p0);
     self.link_port_port(A1.p1, B1.p1);
     self.link_port_port(A1.p2, B2.p1);
     self.link_port_port(A2.p1, B1.p2);
     self.link_port_port(A2.p2, B2.p2);
 
-    trace!(self.tracer);
+    trace!(self);
     self.link_wire_port(a.p1, B1.p0);
     self.link_wire_port(a.p2, B2.p0);
     self.link_wire_port(b.p1, A1.p0);
@@ -165,7 +170,7 @@ impl<'a, M: Mode> Net<'a, M> {
   /// ```
   #[inline(never)]
   pub fn comm02(&mut self, a: Port, b: Port) {
-    trace!(self.tracer, a, b);
+    trace!(self, a, b);
     self.rwts.comm += 1;
     let b = b.consume_node();
     self.link_wire_port(b.p1, a.clone());
@@ -204,20 +209,20 @@ impl<'a, M: Mode> Net<'a, M> {
   /// ```
   #[inline(never)]
   pub fn mat_num(&mut self, a: Port, b: Port) {
-    trace!(self.tracer, a, b);
+    trace!(self, a, b);
     self.rwts.oper += 1;
     let a = a.consume_node();
     let b = b.num();
     if b == 0 {
       let x = self.create_node(Ctr, 0);
-      trace!(self.tracer, x.p0);
+      trace!(self, x.p0);
       self.link_port_port(x.p2, Port::ERA);
       self.link_wire_port(a.p2, x.p1);
       self.link_wire_port(a.p1, x.p0);
     } else {
-      let x = self.create_node(Ctr, 0);
-      let y = self.create_node(Ctr, 0);
-      trace!(self.tracer, x.p0, y.p0);
+      let x = self.create_node(Tag::Ctr, 0);
+      let y = self.create_node(Tag::Ctr, 0);
+      trace!(self, x.p0, y.p0);
       self.link_port_port(x.p1, Port::ERA);
       self.link_port_port(x.p2, y.p0);
       self.link_port_port(y.p1, Port::new_num(b - 1));
@@ -253,21 +258,37 @@ impl<'a, M: Mode> Net<'a, M> {
   /// ```
   #[inline(never)]
   pub fn op_num(&mut self, a: Port, b: Port) {
-    trace!(self.tracer, a, b);
+    trace!(self, a, b);
     let a = a.consume_node();
     let op = unsafe { Op::from_unchecked(a.lab) };
     let a1 = a.p1.load_target();
-    if a1.tag() == Num {
+    if a1.is(Tag::Num) {
       self.rwts.oper += 1;
       let out = op.op(b.num(), a1.num());
       self.link_wire_port(a.p2, Port::new_num(out));
     } else {
       let op = op.swap();
-      let x = self.create_node(Op, op as u16);
-      trace!(self.tracer, x.p0);
+      let x = self.create_node(Tag::Op, op as u16);
+      trace!(self, x.p0);
       self.link_port_port(x.p1, b);
       self.link_wire_port(a.p2, x.p2);
       self.link_wire_port(a.p1, x.p0);
     }
   }
+
+  fn comm2N(&mut self, a: Port, b: Port) {
+    todo!()
+  }
+
+  fn anniNM(&mut self, a: Port, b: Port) {
+    todo!()
+  }
+
+  fn comm0N(&mut self, a: Port, b: Port) {
+    todo!()
+  }
+
+  fn adt_ctr(&mut self, b: Port, a: Port) {
+    todo!()
+  }
 }
diff --git a/src/run/net.rs b/src/run/net.rs
index 5ecbed83..da04bb76 100644
--- a/src/run/net.rs
+++ b/src/run/net.rs
@@ -16,8 +16,8 @@ deref!({<'a, M: Mode>} Net<'a, M> => self.linker: Linker<'a, M>);
 impl<'h, M: Mode> Net<'h, M> {
   /// Creates an empty net with a given heap.
   pub fn new(heap: &'h Heap) -> Self {
-    let mut net = Net::new_with_root(heap, Wire(std::ptr::null()));
-    net.root = Wire::new(net.alloc());
+    let mut net = Net::new_with_root(heap, Wire(0));
+    net.root = Wire::new(Align2, net.alloc(Align2));
     net
   }
 
@@ -55,13 +55,13 @@ impl<'a, M: Mode> Net<'a, M> {
     let mut path: Vec<Port> = vec![];
 
     loop {
-      trace!(self.tracer, prev);
+      trace!(self, prev);
       // Load ptrs
       let next = self.get_target_full(prev.clone());
-      trace!(self.tracer, next);
+      trace!(self, next);
 
       // If next is root, stop.
-      if next == Port::new_var(root.addr()) || next == Port::new_var(self.root.addr()) {
+      if next == root.as_var() || next == self.root.as_var() {
         break;
       }
 
@@ -73,7 +73,7 @@ impl<'a, M: Mode> Net<'a, M> {
           prev = path.pop().unwrap();
           continue;
         // Otherwise, if it is a ref, expand it.
-        } else if next.tag() == Ref && next != Port::ERA {
+        } else if next.is(Tag::Ref) && next != Port::ERA {
           self.call(next, prev.clone());
           continue;
         // Otherwise, we're done.
@@ -83,7 +83,7 @@ impl<'a, M: Mode> Net<'a, M> {
       }
 
       // If next is an aux port, pass through.
-      let main = self.get_header(next.addr().left_half());
+      let main = self.get_header(next.addr().floor(next.alloc_align()));
       path.push(prev);
       prev = main.this.clone();
     }
@@ -93,15 +93,16 @@ impl<'a, M: Mode> Net<'a, M> {
 
   pub fn normal_from(&mut self, root: Wire) {
     assert!(M::LAZY);
-    let mut visit = vec![Port::new_var(root.addr())];
+    let mut visit = vec![root.as_var()];
     while let Some(prev) = visit.pop() {
-      trace!(self.tracer, "visit", prev);
+      trace!(self, "visit", prev);
       //println!("normal {} | {}", prev.view(), self.rewrites());
       let next = self.weak_normal(prev, root.clone());
-      trace!(self.tracer, "got", next);
+      trace!(self, "got", next);
       if next.is_full_node() {
-        visit.push(Port::new_var(next.addr()));
-        visit.push(Port::new_var(next.addr().other_half()));
+        todo!();
+        // visit.push(Port::new_var( next.addr()));
+        // visit.push(Port::new_var( next.addr().other_half()));
       }
     }
   }
diff --git a/src/run/node.rs b/src/run/node.rs
index 60b27e2e..822b96d6 100644
--- a/src/run/node.rs
+++ b/src/run/node.rs
@@ -19,8 +19,8 @@ impl Port {
     TraverseNode {
       tag: self.tag(),
       lab: self.lab(),
-      p1: Wire::new(self.addr()),
-      p2: Wire::new(self.addr().other_half()),
+      p1: Wire::new(self.align(), self.addr()),
+      p2: Wire::new(self.align(), self.addr().other(Align1)),
     }
   }
 }
@@ -34,30 +34,29 @@ pub struct CreatedNode {
 impl<'a, M: Mode> Net<'a, M> {
   #[inline(always)]
   pub fn create_node(&mut self, tag: Tag, lab: Lab) -> CreatedNode {
-    let addr = self.alloc();
+    assert_eq!(tag.width(), 2);
+    let addr = self.alloc(tag.align());
     CreatedNode {
       p0: Port::new(tag, lab, addr.clone()),
-      p1: Port::new_var(addr.clone()),
-      p2: Port::new_var(addr.other_half()),
+      p1: Port::new_var(Align2, addr),
+      p2: Port::new_var(Align2, addr.other(Align1)),
     }
   }
 
   /// Creates a wire an aux port pair.
   #[inline(always)]
   pub fn create_wire(&mut self) -> (Wire, Port) {
-    let addr = self.alloc();
-    self.half_free(addr.other_half());
-    (Wire::new(addr.clone()), Port::new_var(addr))
+    let addr = self.alloc(Align2);
+    (Wire::new(Align1, addr), Port::new_var(Align1, addr))
   }
 
   /// Creates a wire pointing to a given port; sometimes necessary to avoid
   /// deadlock.
   #[inline(always)]
   pub fn create_wire_to(&mut self, port: Port) -> Wire {
-    let addr = self.alloc();
-    self.half_free(addr.other_half());
-    let wire = Wire::new(addr);
-    self.link_port_port(port, Port::new_var(wire.addr()));
+    let addr = self.alloc(Align1);
+    let wire = Wire::new(Align1, addr);
+    self.link_port_port(port, wire.as_var());
     wire
   }
 }
diff --git a/src/run/parallel.rs b/src/run/parallel.rs
index 1fc97fde..2695c786 100644
--- a/src/run/parallel.rs
+++ b/src/run/parallel.rs
@@ -10,7 +10,7 @@ impl<'h, M: Mode> Net<'h, M> {
       let area = unsafe { std::mem::transmute(&self.heap.0[heap_start .. heap_start + heap_size]) };
       let mut net = Net::new_with_root(area, self.root.clone());
       net.next = self.next.saturating_sub(heap_start);
-      net.head = if tid == 0 { net.head } else { Addr::NULL };
+      net.heads = if tid == 0 { net.heads } else { [Addr::NULL; 4] };
       net.tid = tid;
       net.tids = tids;
       net.tracer.set_tid(tid);
diff --git a/src/run/port.rs b/src/run/port.rs
index 34e6da95..e7376b9a 100644
--- a/src/run/port.rs
+++ b/src/run/port.rs
@@ -5,6 +5,8 @@ use super::*;
 /// The type of a port is determined by its *tag*, which is stored in the bottom
 /// three bits.
 ///
+/// TODO: update
+///
 /// All tags other than [`Num`] divide the bits of the port as follows:
 /// - the top 16 bits are the *label*, accessible with [`Port::lab`]
 /// - the middle 45 bits are the non-alignment bits of the *address*, an
@@ -18,86 +20,23 @@ use super::*;
 #[must_use]
 pub struct Port(pub u64);
 
-bi_enum! {
-  #[repr(u8)]
-  #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-  pub enum Tag {
-    /// `Red` ports represent redirects, which are an implementation detail of
-    /// the atomic linking algorithm, and don't have a precise analogue in
-    /// interaction nets.
-    ///
-    /// These ports are never directly held, but rather replace the backlinks of
-    /// some var ports. They are used to resolve inter-thread conflicts, and
-    /// thus will never appear when single-threaded.
-    ///
-    /// See the documentation for the linking algorithm for more.
-    Red = 0,
-    /// A `Var` port represents an auxiliary port in the net.
-    ///
-    /// The address of this port represents the wire leaving this port,
-    /// accessible with `Port::wire`.
-    ///
-    /// The label of this port is currently unused and always 0.
-    Var = 1,
-    /// A `Ref` port represents the principal port of a nilary reference node.
-    ///
-    /// The address of this port is a pointer to the corresponding [`Def`].
-    ///
-    /// The label of this port is always equivalent to `def.labs.min_safe`, and
-    /// is used as an optimization for the ref commutation interaction.
-    ///
-    /// Eraser nodes are represented by a null-pointer `Ref`, available as the
-    /// constant [`Port::ERA`].
-    Ref = 2,
-    /// A `Num` port represents the principal port of a U60 node.
-    ///
-    /// The top 60 bits of the port are the value of this node, and are
-    /// accessible with [`Port::num`].
-    ///
-    /// The 4th bit from the bottom is currently unused in this port.
-    Num = 3,
-    /// An `Op` port represents the principal port of an Op node.
-    ///
-    /// The label of this port is the corresponding operation, which can be
-    /// accessed with [`Port::op`].
-    ///
-    /// The address of this port is the address of a two-word allocation,
-    /// storing the targets of the wires connected to the two auxiliary ports of
-    /// this node.
-    Op = 4,
-    /// A `Mat` port represents the principal port of a Mat node.
-    ///
-    /// The address of this port is the address of a two-word allocation,
-    /// storing the targets of the wires connected to the two auxiliary ports of
-    /// the node.
-    ///
-    /// The label of this port is currently unused and always 0.
-    Mat = 6,
-    /// A `Ctr` port represents the principal port of an binary interaction
-    /// combinator node.
-    ///
-    /// The label of this port is the label of the combinator; two combinators
-    /// annihilate if they have the same label, or commute otherwise.
-    ///
-    /// The address of this port is the address of a two-word allocation,
-    /// storing the targets of the wires connected to the two auxiliary ports of
-    /// the node.
-    Ctr = 7,
-  }
-}
-
 impl fmt::Debug for Port {
   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     write!(f, "{:016x?} ", self.0)?;
     match *self {
       Port::ERA => write!(f, "[ERA]"),
-      Port::FREE => write!(f, "[FREE]"),
+      Port::FREE_1 => write!(f, "[FREE 1]"),
+      Port::FREE_2 => write!(f, "[FREE 2]"),
+      Port::FREE_4 => write!(f, "[FREE 4]"),
+      Port::FREE_8 => write!(f, "[FREE 8]"),
       Port::GONE => write!(f, "[GONE]"),
       Port::LOCK => write!(f, "[LOCK]"),
       _ => match self.tag() {
-        Num => write!(f, "[Num {}]", self.num()),
-        Var | Red | Mat => write!(f, "[{:?} {:?}]", self.tag(), self.addr()),
-        Op | Ctr | Ref => write!(f, "[{:?} {:?} {:?}]", self.tag(), self.lab(), self.addr()),
+        Tag::Num => write!(f, "[Num {}]", self.num()),
+        Tag::Var | Tag::Red | Tag::Mat => write!(f, "[{:?} {:?}]", self.tag(), self.addr()),
+        Tag::Op | CtrN!() | AdtN!() | Tag::AdtZ | Tag::Ref => {
+          write!(f, "[{:?} {:?} {:?}]", self.tag(), self.lab(), self.addr())
+        }
       },
     }
   }
@@ -105,10 +44,7 @@ impl fmt::Debug for Port {
 
 impl Port {
   /// The principal port of an eraser node.
-  pub const ERA: Port = Port(Ref as _);
-  /// A sentinel value used to indicate free memory; see the allocator for more
-  /// details.
-  pub const FREE: Port = Port(0x8000_0000_0000_0000);
+  pub const ERA: Port = Port(Tag::Ref as _);
   /// A sentinel value used to lock a wire; see the linking algorithm for more
   /// details.
   pub const LOCK: Port = Port(0xFFFF_FFFF_FFFF_FFF0);
@@ -124,30 +60,62 @@ impl Port {
 
   /// Creates a new [`Var`] port with a given addr.
   #[inline(always)]
-  pub fn new_var(addr: Addr) -> Self {
-    Port::new(Var, 0, addr)
+  pub fn new_var(alloc_align: Align, addr: Addr) -> Self {
+    Port::new(Tag::Var, alloc_align as u16, addr)
+  }
+
+  /// Creates a new [`Red`] port with a given addr.
+  #[inline(always)]
+  pub fn new_red(alloc_align: Align, addr: Addr) -> Self {
+    Port::new(Tag::Red, alloc_align as u16, addr)
   }
 
   /// Creates a new [`Num`] port with a given 60-bit numeric value.
   #[inline(always)]
   pub const fn new_num(val: u64) -> Self {
-    Port((val << 4) | (Num as u64))
+    Port((val << 4) | (Tag::Num as u64))
   }
 
   /// Creates a new [`Ref`] port corresponding to a given definition.
   #[inline(always)]
   pub fn new_ref(def: &Def) -> Port {
-    Port::new(Ref, def.labs.min_safe, Addr(def as *const _ as _))
+    Port::new(Tag::Ref, def.labs.min_safe, Addr(def as *const _ as _))
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub fn new_adtz(variant_count: u8, variant_index: u8) -> Self {
+    Port(Tag::AdtZ as u64 | ((variant_count as u64) << 16) | ((variant_index as u64) << 8))
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub fn variant_count(&self) -> u8 {
+    (self.0 >> 16) as u8
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub fn variant_index(&self) -> u8 {
+    (self.0 >> 8) as u8
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub fn align(&self) -> Align {
+    unsafe { Align::from_unchecked((self.0 & 0b11) as u8) }
   }
 
   /// Accesses the tag of this port; this is valid for all ports.
   #[inline(always)]
   pub fn tag(&self) -> Tag {
-    unsafe { Tag::from_unchecked((self.0 & 0x7) as u8) }
+    unsafe { Tag::from_unchecked((self.0 & (1 << self.align().tag_bits()) - 1) as u8) }
   }
 
+  /// TODO
   #[inline(always)]
   pub fn is(&self, tag: Tag) -> bool {
+    // TODO: optimize
     self.tag() == tag
   }
 
@@ -160,11 +128,11 @@ impl Port {
   /// Accesses the addr of this port; this is valid for all non-`Num` ports.
   #[inline(always)]
   pub const fn addr(&self) -> Addr {
+    // todo
     Addr((self.0 & 0x0000_FFFF_FFFF_FFF8) as usize as _)
   }
 
-  /// Accesses the operation of this port; this is valid for [`Op1`] and [`Op2`]
-  /// ports.
+  /// Accesses the operation of this port; this is valid for [`Opr`] ports.
   #[inline(always)]
   pub fn op(&self) -> Op {
     unsafe { Op::from_unchecked(self.lab()) }
@@ -180,12 +148,12 @@ impl Port {
   /// non-sentinel [`Red`] ports.
   #[inline(always)]
   pub fn wire(&self) -> Wire {
-    Wire::new(self.addr())
+    Wire::new(self.alloc_align(), self.addr())
   }
 
   #[inline(always)]
   pub fn is_principal(&self) -> bool {
-    self.tag() >= Ref
+    self.align() != Align1
   }
 
   /// Given a principal port, returns whether this principal port may be part of
@@ -193,22 +161,37 @@ impl Port {
   /// need to be added to the redex list.
   #[inline(always)]
   pub fn is_skippable(&self) -> bool {
-    self.tag() == Num || self.tag() == Ref && self.lab() != u16::MAX
+    self.is(Tag::AdtZ) || self.is(Tag::Num) || self.is(Tag::Ref) && self.lab() != u16::MAX
   }
 
   /// Converts a [`Var`] port into a [`Red`] port with the same address.
   #[inline(always)]
   pub(super) fn redirect(&self) -> Port {
-    Port::new(Red, 0, self.addr())
+    Port(self.0 ^ (Tag::Red as u64 ^ Tag::Var as u64))
   }
 
   /// Converts a [`Red`] port into a [`Var`] port with the same address.
   #[inline(always)]
   pub(super) fn unredirect(&self) -> Port {
-    Port::new(Var, 0, self.addr())
+    self.redirect()
   }
 
   pub(super) fn is_full_node(&self) -> bool {
-    self.tag() > Num
+    match self.tag() {
+      Tag::Op | Tag::Mat | CtrN!() | AdtN!() => true,
+      Tag::Red | Tag::Var | Tag::Num | Tag::Ref | Tag::AdtZ => false,
+    }
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub(super) fn alloc_align(&self) -> Align {
+    unsafe { Align::from_unchecked(self.lab() as u8) }
+  }
+
+  /// TODO
+  #[inline(always)]
+  pub(super) fn is_ctr_ish(&self) -> bool {
+    (self.0 * 0b111) > 0b100
   }
 }
diff --git a/src/run/tag.rs b/src/run/tag.rs
new file mode 100644
index 00000000..3042baff
--- /dev/null
+++ b/src/run/tag.rs
@@ -0,0 +1,123 @@
+use super::*;
+
+bi_enum! {
+  #[repr(u8)]
+  #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+  pub enum Tag {
+    //-- Align1 --\\
+    Red  =    0b0_00,
+    Var  =    0b1_00,
+
+    //-- Align2 --\\
+    Ctr2 =   0b01_01,
+    Adt2 =   0b11_01,
+    Num  =   0b00_01,
+    Op   =   0b10_01,
+
+    //-- Align4 --\\
+    Ctr3 =  0b001_10,
+    Ctr4 =  0b101_10,
+    Adt3 =  0b011_10,
+    Adt4 =  0b111_10,
+    Ref  =  0b000_10,
+    Mat  =  0b010_10,
+    AdtZ =  0b100_10,
+    //   =  0b110_10,
+
+    //-- Align8 --\\
+    Ctr5 = 0b0001_11,
+    Ctr6 = 0b0101_11,
+    Ctr7 = 0b1001_11,
+    Ctr8 = 0b1101_11,
+    Adt5 = 0b0011_11,
+    Adt6 = 0b0111_11,
+    Adt7 = 0b1011_11,
+    Adt8 = 0b1111_11,
+    //   = 0b0000_11,
+    //   = 0b0010_11,
+    //   = 0b0100_11,
+    //   = 0b0110_11,
+    //   = 0b1000_11,
+    //   = 0b1010_11,
+    //   = 0b1100_11,
+    //   = 0b1110_11,
+  }
+}
+
+impl Tag {
+  #[inline(always)]
+  pub(super) fn align(self) -> Align {
+    unsafe { Align::from_unchecked(self as u8 & 0b11) }
+  }
+  #[inline]
+  pub(super) fn width(self) -> u8 {
+    match self {
+      Tag::Num | Tag::Ref | Tag::AdtZ => 0,
+      Tag::Red | Tag::Var => 1,
+      Tag::Op | Tag::Mat => 2,
+      CtrN!() | AdtN!() => (1 << (self.align() as u8 - 1)) + 1 + (self as u8 >> 4),
+    }
+  }
+  #[inline]
+  pub(super) fn arity(self) -> u8 {
+    match self {
+      AdtN!() => self.width() - 1,
+      _ => self.width(),
+    }
+  }
+}
+
+macro_rules! CtrN {
+  () => {
+    Tag::Ctr2 | Tag::Ctr3 | Tag::Ctr4 | Tag::Ctr5 | Tag::Ctr6 | Tag::Ctr7 | Tag::Ctr8
+  };
+}
+
+macro_rules! AdtN {
+  () => {
+    Tag::Adt2 | Tag::Adt3 | Tag::Adt4 | Tag::Adt5 | Tag::Adt6 | Tag::Adt7 | Tag::Adt8
+  };
+}
+
+pub(crate) use AdtN;
+pub(crate) use CtrN;
+
+bi_enum! {
+  #[repr(u8)]
+  #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+  pub(crate) enum Align {
+    Align1 = 0b00,
+    Align2 = 0b01,
+    Align4 = 0b10,
+    Align8 = 0b11,
+  }
+}
+
+pub(crate) use Align::*;
+
+impl Align {
+  #[inline(always)]
+  pub(super) fn tag_bits(self) -> u8 {
+    self as u8 + 3
+  }
+  #[inline(always)]
+  pub(super) fn width(self) -> u8 {
+    1 << self as u8
+  }
+  #[inline(always)]
+  pub(super) fn next(self) -> Option<Self> {
+    match self {
+      Align1 => Some(Align2),
+      Align2 => Some(Align4),
+      Align4 => Some(Align8),
+      Align8 => None,
+    }
+  }
+}
+
+#[test]
+fn test_tag_width() {
+  use Tag::*;
+  assert_eq!([Ctr2, Ctr3, Ctr4, Ctr5, Ctr6, Ctr7, Ctr8].map(Tag::width), [2, 3, 4, 5, 6, 7, 8]);
+  assert_eq!([Adt2, Adt3, Adt4, Adt5, Adt6, Adt7, Adt8].map(Tag::width), [2, 3, 4, 5, 6, 7, 8]);
+}
diff --git a/src/run/wire.rs b/src/run/wire.rs
index 7a79cd94..d27ee5ad 100644
--- a/src/run/wire.rs
+++ b/src/run/wire.rs
@@ -13,7 +13,7 @@ use super::*;
 /// Changes to the target are handled by the linker.
 #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 #[must_use]
-pub struct Wire(pub *const AtomicU64);
+pub struct Wire(pub u64);
 
 impl fmt::Debug for Wire {
   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -27,28 +27,33 @@ unsafe impl Sync for Wire {}
 impl Wire {
   #[inline(always)]
   pub fn addr(&self) -> Addr {
-    Addr(self.0 as _)
+    Addr((self.0 & 0x0000_FFFF_FFFF_FFFF) as usize)
   }
 
   #[inline(always)]
-  pub fn new(addr: Addr) -> Wire {
-    Wire(addr.0 as _)
+  pub fn alloc_align(&self) -> Align {
+    unsafe { Align::from_unchecked((self.0 >> 48) as u8) }
+  }
+
+  #[inline(always)]
+  pub fn new(alloc_align: Align, addr: Addr) -> Wire {
+    Wire(((alloc_align as u64) << 48) | (addr.0 as u64))
   }
 
   #[inline(always)]
   fn target<'a>(&self) -> &'a AtomicU64 {
     if cfg!(feature = "_fuzz") {
-      assert_ne!(self.0 as usize, 0xfffffffffff0u64 as usize);
-      assert_ne!(self.0 as usize, 0);
+      assert_ne!(self.addr().0, 0xfffffffffff0u64 as usize);
+      assert_ne!(self.0, 0);
     }
-    unsafe { &*self.0 }
+    unsafe { &*(self.addr().0 as *const _) }
   }
 
   #[inline(always)]
   pub fn load_target(&self) -> Port {
     let port = Port(self.target().load(Relaxed));
     if cfg!(feature = "_fuzz") {
-      assert_ne!(port, Port::FREE);
+      assert_ne!(port, Port::FREE_1);
     }
     port
   }
@@ -67,18 +72,23 @@ impl Wire {
   pub fn swap_target(&self, value: Port) -> Port {
     let port = Port(self.target().swap(value.0, Relaxed));
     if cfg!(feature = "_fuzz") {
-      assert_ne!(port, Port::FREE);
+      assert_ne!(port, Port::FREE_1);
     }
     port
   }
 
+  #[inline(always)]
+  pub fn as_var(&self) -> Port {
+    Port(self.0 | Tag::Var as u64)
+  }
+
   // Takes a pointer's target.
   #[inline(always)]
   pub fn lock_target(&self) -> Port {
     loop {
       let got = self.swap_target(Port::LOCK);
       if cfg!(feature = "_fuzz") {
-        assert_ne!(got, Port::FREE);
+        assert_ne!(got, Port::FREE_1);
       }
       if got != Port::LOCK {
         return got;
@@ -86,9 +96,4 @@ impl Wire {
       spin_loop();
     }
   }
-
-  #[inline(always)]
-  pub(super) fn as_var(&self) -> Port {
-    Port::new_var(self.addr())
-  }
 }
diff --git a/src/trace.rs b/src/trace.rs
index 7bb961d1..b7d22d95 100644
--- a/src/trace.rs
+++ b/src/trace.rs
@@ -64,7 +64,7 @@
 #![cfg_attr(not(feature = "trace"), allow(unused))]
 
 use std::{
-  cell::UnsafeCell,
+  cell::{Cell, UnsafeCell},
   fmt::{self, Debug, Formatter, Write},
   sync::{
     atomic::{AtomicBool, AtomicU64, Ordering},
@@ -84,10 +84,10 @@ pub struct Tracer(());
 #[cfg(not(feature = "trace"))]
 impl Tracer {
   #[inline(always)]
-  pub fn sync(&mut self) {}
+  pub fn sync(&self) {}
   #[inline(always)]
   #[doc(hidden)]
-  pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, _: A) {}
+  pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, _: A) {}
   #[inline(always)]
   pub fn set_tid(&self, _: usize) {}
 }
@@ -124,12 +124,12 @@ pub struct Tracer(TraceWriter);
 #[cfg(feature = "trace")]
 impl Tracer {
   #[inline(always)]
-  pub fn sync(&mut self) {
+  pub fn sync(&self) {
     self.0.sync()
   }
   #[inline(always)]
   #[doc(hidden)]
-  pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, args: A) {
+  pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, args: A) {
     self.0.trace::<S, A>(args)
   }
   #[inline(always)]
@@ -183,7 +183,7 @@ static ACTIVE_TRACERS: Mutex<Vec<Box<TraceLock>>> = Mutex::new(Vec::new());
 
 struct TraceWriter {
   lock: &'static TraceLock,
-  nonce: u64,
+  nonce: Cell<u64>,
 }
 
 unsafe impl Send for TraceWriter {}
@@ -197,13 +197,13 @@ impl Default for TraceWriter {
     let lock = unsafe { &*(&*boxed as *const _) };
     let mut active_tracers = ACTIVE_TRACERS.lock().unwrap();
     active_tracers.push(boxed);
-    TraceWriter { lock, nonce: TRACE_NONCE.fetch_add(1, Ordering::Relaxed) }
+    TraceWriter { lock, nonce: Cell::new(TRACE_NONCE.fetch_add(1, Ordering::Relaxed)) }
   }
 }
 
 impl TraceWriter {
-  fn sync(&mut self) {
-    self.nonce = TRACE_NONCE.fetch_add(1, Ordering::Relaxed);
+  fn sync(&self) {
+    self.nonce.set(TRACE_NONCE.fetch_add(1, Ordering::Relaxed));
   }
   fn acquire(&self, cb: impl FnOnce(&mut TraceData)) {
     while self.lock.locked.compare_exchange_weak(false, true, Ordering::Relaxed, Ordering::Relaxed).is_err() {
@@ -212,13 +212,13 @@ impl TraceWriter {
     cb(unsafe { &mut *self.lock.data.get() });
     self.lock.locked.store(false, Ordering::Release);
   }
-  fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, args: A) {
+  fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, args: A) {
     if cfg!(feature = "_fuzz") {
       self.sync();
     }
     let meta: &'static _ = &TraceMetadata { source: S::SOURCE, arg_fmts: A::FMTS };
     self.acquire(|data| {
-      let nonce = self.nonce;
+      let nonce = self.nonce.get();
       for arg in args.to_words().rev() {
         data.write_word(arg);
       }