This commit is contained in:
Jakub Doka 2024-10-28 23:38:57 +01:00
parent be828b8c54
commit 7ef1adf7e2
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
5 changed files with 373 additions and 95 deletions

47
f.txt Normal file
View file

@ -0,0 +1,47 @@
start: 0
46-c65535: +: [0, 38, 45] [47]
45-c65535: &: [0, 43, 44] [46]
44-c65535: cint: #255 [0] [45]
43-c65535: load: [0, 42, 23] [45]
42-c65535: +: [0, 22, 18] [43]
39-c65535: stre: [0, 0, 36, 37] [47]
39-c65535: stre: [0, 0, 36, 37] [47]
37-c65535: stre: [0, 35, 36, 3] [38, 39]
36-c65535: stck: [0, 3] [37, 38, 39]
35-c65535: load: [0, 24, 34] [37]
34-c65535: stre: [0, 31, 33, 30] [35, 47]
33-c65535: +: [0, 24, 32] [34]
32-c65535: cint: #16 [0] [33]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 29, 26] [34]
29-c65535: +: [0, 24, 28] [30]
28-c65535: cint: #8 [0] [29]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 24, 3] [30]
25-c65535: cint: #1 [0] [26]
24-c65535: stck: [0, 3] [26, 29, 33, 35]
23-c65535: stre: [0, 21, 22, 3] [43, 47]
22-c65535: stck: [0, 3] [23, 42]
21-c65535: load: [0, 6, 20] [23]
20-c65535: stre: [0, 17, 19, 14] [21, 47]
19-c65535: +: [0, 6, 18] [20]
18-c65535: cint: #3 [0] [19, 42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 13, 11] [20]
13-c65535: +: [0, 6, 12] [14]
12-c65535: cint: #2 [0] [13]
11-c65535: stre: [0, 7, 10, 8] [14]
10-c65535: +: [0, 6, 9] [11]
9-c65535: cint: #1 [0] [10]
8-c65535: stre: [0, 7, 6, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
6-c65535: stck: [0, 3] [8, 10, 13, 19, 21]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [6, 8, 22, 23, 24, 26, 36, 37, 47]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 37] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 23, 34, 39] [1]

38
f1.txt Normal file
View file

@ -0,0 +1,38 @@
start: 0
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
51-c65535: +: [0, 36, 32] [34]
32-c65535: cint: #16 [0] [51]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 50, 26] [34]
50-c65535: +: [0, 36, 28] [30]
28-c65535: cint: #8 [0] [50]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 36, 3] [30]
25-c65535: cint: #1 [0] [26, 46]
39-c65535: stre: [0, 0, 36, 34] [47]
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
22-c65535: stck: [0, 3] [49, 42, 8, 48]
39-c65535: stre: [0, 0, 36, 34] [47]
20-c65535: stre: [0, 17, 42, 14] [47, 47]
46-c65535: +: [0, 38, 25] [47]
18-c65535: cint: #3 [0] [42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 49, 11] [20]
49-c65535: +: [0, 22, 12] [14]
12-c65535: cint: #2 [0] [49]
11-c65535: stre: [0, 7, 48, 8] [14]
48-c65535: +: [0, 22, 9] [11]
9-c65535: cint: #1 [0] [48]
8-c65535: stre: [0, 7, 22, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
42-c65535: +: [0, 22, 18] [20]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [47, 8, 22, 26, 36]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]

39
f2.txt Normal file
View file

@ -0,0 +1,39 @@
start: 0
42-c65535: +: [0, 22, 18] [43, 20]
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
39-c65535: stre: [0, 0, 36, 34] [47]
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
51-c65535: +: [0, 36, 32] [34]
32-c65535: cint: #16 [0] [51]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 50, 26] [34]
50-c65535: +: [0, 36, 28] [30]
28-c65535: cint: #8 [0] [50]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 36, 3] [30]
25-c65535: cint: #1 [0] [26]
39-c65535: stre: [0, 0, 36, 34] [47]
45-c65535: &: [0, 43, 44] [46]
22-c65535: stck: [0, 3] [49, 42, 8, 48]
44-c65535: cint: #255 [0] [45]
20-c65535: stre: [0, 17, 42, 14] [47, 47, 43]
46-c65535: +: [0, 38, 45] [47]
18-c65535: cint: #3 [0] [42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 49, 11] [20]
49-c65535: +: [0, 22, 12] [14]
12-c65535: cint: #2 [0] [49]
11-c65535: stre: [0, 7, 48, 8] [14]
48-c65535: +: [0, 22, 9] [11]
9-c65535: cint: #1 [0] [48]
8-c65535: stre: [0, 7, 22, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
43-c65535: load: [0, 42, 20] [45]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [47, 8, 22, 26, 36]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]

View file

@ -45,7 +45,7 @@ impl crate::ctx_map::CtxEntry for Nid {
type Key<'a> = (Kind, &'a [Nid], ty::Id);
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
ctx[*self as usize].as_ref().unwrap().key()
ctx[*self as usize].as_ref().unwrap_or_else(|(_, t)| panic!("{t:#?}")).key()
}
}
@ -73,6 +73,7 @@ struct Nodes {
visited: BitSet,
free: Nid,
lookup: Lookup,
complete: bool,
}
impl Default for Nodes {
@ -82,6 +83,7 @@ impl Default for Nodes {
free: Nid::MAX,
lookup: Default::default(),
visited: Default::default(),
complete: false,
}
}
}
@ -230,7 +232,7 @@ impl Nodes {
}
fn push_up(&mut self, rpo: &mut Vec<Nid>) {
rpo.clear();
debug_assert!(rpo.is_empty());
self.collect_rpo(VOID, rpo);
for &node in rpo.iter().rev() {
@ -262,6 +264,8 @@ impl Nodes {
&& !matches!(nod.kind, Kind::Arg | Kind::Mem | Kind::Loops))
.collect::<Vec<_>>()
);
rpo.clear();
}
fn better(&mut self, is: Nid, then: Nid) -> bool {
@ -491,6 +495,7 @@ impl Nodes {
self.values.clear();
self.lookup.clear();
self.free = Nid::MAX;
self.complete = false;
}
fn new_node_nop(&mut self, ty: ty::Id, kind: Kind, inps: impl Into<Vc>) -> Nid {
@ -610,13 +615,16 @@ impl Nodes {
fn late_peephole(&mut self, target: Nid) -> Option<Nid> {
if let Some(id) = self.peephole(target) {
self.replace(target, id);
return Some(id);
return None;
}
None
}
fn iter_peeps(&mut self, mut fuel: usize, stack: &mut Vec<Nid>) {
stack.clear();
debug_assert!(!self.complete);
debug_assert!(stack.is_empty());
self.complete = true;
self.iter()
.filter_map(|(id, node)| node.kind.is_peeped().then_some(id))
@ -630,16 +638,42 @@ impl Nodes {
if self.unlock_remove(node) {
continue;
}
let new = self.late_peephole(node);
if let Some(new) = new {
if let Some(new) = self.peephole(node) {
self.replace(node, new);
self.push_adjacent_nodes(new, stack);
}
debug_assert_matches!(
self.iter()
.find(|(i, n)| n.lock_rc != 0 && n.kind.is_peeped() && !stack.contains(i)),
None
);
}
stack.drain(..).for_each(|s| _ = self.unlock_remove(s));
}
fn push_adjacent_nodes(&mut self, of: Nid, stack: &mut Vec<Nid>) {
let prev_len = stack.len();
for &i in self[new].outputs.iter().chain(self[new].inputs.iter()) {
for &i in self[of].outputs.iter().chain(self[of].inputs.iter()) {
if self[i].kind.is_peeped() && self[i].lock_rc == 0 {
stack.push(i);
}
}
stack.iter().skip(prev_len).for_each(|&n| self.lock(n));
}
pub fn aclass_index(&self, mut region: Nid) -> (usize, Nid) {
loop {
region = match self[region].kind {
Kind::BinOp { op: TokenKind::Add | TokenKind::Sub } => self[region].inputs[1],
Kind::Phi => {
debug_assert_eq!(self[region].inputs[2], 0);
self[region].inputs[1]
}
_ => break (self[region].aclass, region),
};
}
}
@ -823,11 +857,42 @@ impl Nodes {
return Some(ctrl);
}
K::Call { .. } | K::Return => {
K::Call { .. } => {
if self[target].inputs[0] == NEVER {
return Some(NEVER);
}
}
K::Return => {
if self[target].inputs[0] == NEVER {
return Some(NEVER);
}
let mut new_inps = Vc::from(&self[target].inputs[..2]);
'a: for &n in self[target].inputs.clone().iter().skip(2) {
if self[n].kind != Kind::Stre || self[n].inputs.len() != 4 {
new_inps.push(n);
continue;
}
let mut cursor = n;
let class = self.aclass_index(self[cursor].inputs[2]);
cursor = self[cursor].inputs[3];
while cursor != MEM {
if self.aclass_index(self[cursor].inputs[2]) != class
|| self[cursor].inputs.len() != 4
{
new_inps.push(n);
continue 'a;
}
cursor = self[cursor].inputs[3];
}
}
if new_inps.as_slice() != self[target].inputs.as_slice() {
return Some(self.new_node_nop(ty::Id::VOID, Kind::Return, new_inps));
}
}
K::Phi => {
let &[ctrl, lhs, rhs] = self[target].inputs.as_slice() else { unreachable!() };
@ -857,18 +922,123 @@ impl Nodes {
}
}
K::Stre => {
if self[target].inputs[1] != VOID
&& self[target].inputs.len() == 4
&& self[self[target].inputs[1]].kind != Kind::Load
&& self[self[target].inputs[3]].kind == Kind::Stre
&& self[self[target].inputs[3]].lock_rc == 0
&& self[self[target].inputs[3]].inputs[2] == self[target].inputs[2]
let &[_, value, region, store, ..] = self[target].inputs.as_slice() else {
unreachable!()
};
'eliminate: {
break 'eliminate;
if self[value].kind != Kind::Load || self[value].outputs.as_slice() != [target]
{
return Some(self.modify_input(
self[target].inputs[3],
1,
self[target].inputs[1],
));
break 'eliminate;
}
let &[_, stack, last_store] = self[value].inputs.as_slice() else {
unreachable!()
};
if self[stack].ty != self[value].ty || self[stack].kind != Kind::Stck {
break 'eliminate;
}
let mut unidentifed = self[stack].outputs.clone();
let load_idx = unidentifed.iter().position(|&n| n == value).unwrap();
unidentifed.swap_remove(load_idx);
let mut saved = Vc::default();
let mut cursor = last_store;
let mut first_store = last_store;
while cursor != MEM && self[cursor].kind == Kind::Stre {
let mut contact_point = cursor;
let mut region = self[cursor].inputs[2];
if let Kind::BinOp { op } = self[region].kind {
debug_assert_matches!(op, TokenKind::Add | TokenKind::Sub);
contact_point = region;
region = self[region].inputs[1]
}
if region != stack {
break;
}
let Some(index) = unidentifed.iter().position(|&n| n == contact_point)
else {
break 'eliminate;
};
unidentifed.remove(index);
saved.push(contact_point);
first_store = cursor;
cursor = *self[cursor].inputs.get(3).unwrap_or(&MEM);
if unidentifed.is_empty() {
break;
}
}
// TODO: this can be an offset already due to previous peeps so handle that
if let &[mcall] = unidentifed.as_slice()
&& matches!(self[mcall].kind, Kind::Call { .. })
&& self[mcall].inputs.last() == Some(&stack)
{
self.modify_input(mcall, self[mcall].inputs.len() - 1, region);
return Some(last_store);
} else {
debug_assert_matches!(
self[last_store].kind,
Kind::Stre | Kind::Mem,
"{:?}",
self[last_store]
);
debug_assert_matches!(
self[first_store].kind,
Kind::Stre | Kind::Mem,
"{:?}",
self[first_store]
);
if !unidentifed.is_empty() {
break 'eliminate;
}
// FIXME: when the loads and stores become parallel we will need to get saved
// differently
let mut prev_store = store;
for mut oper in saved.into_iter().rev() {
let mut region = region;
if let Kind::BinOp { op } = self[oper].kind {
debug_assert_eq!(self[oper].outputs.len(), 1);
debug_assert_eq!(self[self[oper].outputs[0]].kind, Kind::Stre);
region = self.new_node(self[oper].ty, Kind::BinOp { op }, [
VOID,
region,
self[oper].inputs[2],
]);
oper = self[oper].outputs[0];
}
let mut inps = self[oper].inputs.clone();
debug_assert_eq!(inps.len(), 4);
inps[2] = region;
inps[3] = prev_store;
prev_store = self.new_node(self[oper].ty, Kind::Stre, inps);
}
return Some(prev_store);
}
}
if value != VOID
&& self[target].inputs.len() == 4
&& self[value].kind != Kind::Load
&& self[store].kind == Kind::Stre
&& self[store].lock_rc == 0
&& self[store].inputs[2] == region
{
if self[store].inputs[1] == value {
return Some(store);
}
return Some(self.modify_input(store, 1, self[target].inputs[1]));
}
}
K::Load => {
@ -908,24 +1078,25 @@ impl Nodes {
fn replace(&mut self, target: Nid, with: Nid) {
debug_assert_ne!(target, with, "{:?}", self[target]);
let mut back_press = 0;
for i in 0..self[target].outputs.len() {
let out = self[target].outputs[i - back_press];
for out in self[target].outputs.clone() {
let index = self[out].inputs.iter().position(|&p| p == target).unwrap();
self.lock(target);
let prev_len = self[target].outputs.len();
self.modify_input(out, index, with);
back_press += (self[target].outputs.len() != prev_len) as usize;
self.unlock(target);
}
self.remove(target);
}
fn modify_input(&mut self, target: Nid, inp_index: usize, with: Nid) -> Nid {
self.remove_node_lookup(target);
debug_assert_ne!(self[target].inputs[inp_index], with, "{:?}", self[target]);
if self[target].is_not_gvnd() && (self[target].kind != Kind::Phi || with == 0) {
let prev = self[target].inputs[inp_index];
self[target].inputs[inp_index] = with;
self[with].outputs.push(target);
let index = self[prev].outputs.iter().position(|&o| o == target).unwrap();
self[prev].outputs.swap_remove(index);
self.remove(prev);
target
} else {
let prev = self[target].inputs[inp_index];
self[target].inputs[inp_index] = with;
let (entry, hash) = self.lookup.entry(target.key(&self.values), &self.values);
@ -948,6 +1119,7 @@ impl Nodes {
}
}
}
}
#[track_caller]
fn unlock_remove(&mut self, id: Nid) -> bool {
@ -1121,7 +1293,7 @@ impl Nodes {
if !matches!(node.kind, Kind::End | Kind::Mem | Kind::Arg | Kind::Loops)
&& node.outputs.is_empty()
{
log::error!("outputs are empry {id} {:?}", node.kind);
log::error!("outputs are empry {id} {:?}", node);
failed = true;
}
if node.inputs.first() == Some(&NEVER) && id != NEVER {
@ -1275,6 +1447,8 @@ impl Nodes {
&& self[mcall].inputs.last() == Some(&stack)
{
self.modify_input(mcall, self[mcall].inputs.len() - 1, region);
self.replace(dst, last_store);
} else {
debug_assert_matches!(
self[last_store].kind,
@ -1295,6 +1469,7 @@ impl Nodes {
// FIXME: when the loads and stores become parallel we will need to get saved
// differently
let mut prev_store = self[dst].inputs[3];
for mut oper in saved.into_iter().rev() {
let mut region = region;
if let Kind::BinOp { op } = self[oper].kind {
@ -1308,24 +1483,14 @@ impl Nodes {
oper = self[oper].outputs[0];
}
self.modify_input(oper, 2, region);
}
let mut inps = self[oper].inputs.clone();
debug_assert_eq!(inps.len(), 4);
inps[2] = region;
inps[3] = prev_store;
prev_store = self.new_node(self[oper].ty, Kind::Stre, inps);
}
// self[first_store].lock_rc = u16::MAX - 1;
// self[last_store].lock_rc = u16::MAX - 1;
let prev_store = self[dst].inputs[3];
if prev_store != MEM && first_store != MEM {
self.modify_input(first_store, 3, prev_store);
}
self.replace(dst, last_store);
if self.values[stack as usize].is_ok() {
self.lock(stack);
}
if self.values[dst as usize].is_ok() {
self.lock(dst);
self.replace(dst, prev_store);
}
}
}
@ -1426,7 +1591,7 @@ impl Kind {
}
fn is_peeped(&self) -> bool {
!matches!(self, Self::End | Self::Arg)
!matches!(self, Self::End | Self::Arg | Self::Mem | Self::Loops)
}
}
@ -1473,7 +1638,8 @@ impl Node {
fn is_not_gvnd(&self) -> bool {
(self.kind == Kind::Phi && self.inputs[2] == 0)
|| matches!(self.kind, Kind::Arg | Kind::Stck | Kind::End)
|| matches!(self.kind, Kind::Arg | Kind::Stck)
|| self.kind.is_cfg()
}
fn is_mem(&self) -> bool {
@ -1724,11 +1890,13 @@ impl ItemCtx {
self.scope.aclasses.push(AClass::new(&mut self.nodes));
}
fn finalize(&mut self, stack: &mut Vec<Nid>) {
fn finalize(&mut self, stack: &mut Vec<Nid>, _tys: &Types, _files: &[parser::Ast]) {
self.scope.clear(&mut self.nodes);
mem::take(&mut self.ctrl).soft_remove(&mut self.nodes);
self.nodes.eliminate_stack_temporaries();
self.nodes.iter_peeps(1000, stack);
self.nodes.unlock(MEM);
self.nodes.unlock(NEVER);
self.nodes.unlock(LOOPS);
@ -1993,7 +2161,7 @@ impl<'a> Codegen<'a> {
);
debug_assert!(self.ci.nodes[region].kind != Kind::Stre);
let (value_index, value_region) = self.aclass_index(value);
let (value_index, value_region) = self.ci.nodes.aclass_index(value);
if value_index != 0 {
// simply switch the class to the default one
let aclass = &mut self.ci.scope.aclasses[value_index];
@ -2018,7 +2186,7 @@ impl<'a> Codegen<'a> {
self.ci.nodes[value_region].aclass = 0;
}
let (index, _) = self.aclass_index(region);
let (index, _) = self.ci.nodes.aclass_index(region);
let aclass = &mut self.ci.scope.aclasses[index];
self.ci.nodes.load_loop_aclass(index, aclass, &mut self.ci.loops);
let mut vc = Vc::from([VOID, value, region, aclass.last_store.get()]);
@ -2052,7 +2220,7 @@ impl<'a> Codegen<'a> {
self.ty_display(self.ci.nodes[region].ty)
);
debug_assert!(self.ci.nodes[region].kind != Kind::Stre);
let (index, _) = self.aclass_index(region);
let (index, _) = self.ci.nodes.aclass_index(region);
let aclass = &mut self.ci.scope.aclasses[index];
self.ci.nodes.load_loop_aclass(index, aclass, &mut self.ci.loops);
let vc = [VOID, region, aclass.last_store.get()];
@ -2061,21 +2229,6 @@ impl<'a> Codegen<'a> {
load
}
pub fn aclass_index(&mut self, mut region: Nid) -> (usize, Nid) {
loop {
region = match self.ci.nodes[region].kind {
Kind::BinOp { op: TokenKind::Add | TokenKind::Sub } => {
self.ci.nodes[region].inputs[1]
}
Kind::Phi => {
debug_assert_eq!(self.ci.nodes[region].inputs[2], 0);
self.ci.nodes[region].inputs[1]
}
_ => break (self.ci.nodes[region].aclass, region),
};
}
}
pub fn generate(&mut self, entry: FileId) {
self.find_type(0, entry, entry, Err("main"), self.files);
if self.tys.ins.funcs.is_empty() {
@ -2229,7 +2382,7 @@ impl<'a> Codegen<'a> {
}
self.ci.ctrl.set(
self.ci.nodes.new_node(ty::Id::VOID, Kind::Return, inps),
self.ci.nodes.new_node_nop(ty::Id::VOID, Kind::Return, inps),
&mut self.ci.nodes,
);
@ -2567,7 +2720,7 @@ impl<'a> Codegen<'a> {
for arg in args {
let value = self.expr(arg)?;
if let Some(base) = self.tys.base_of(value.ty) {
clobbered_aliases.push(self.aclass_index(value.id).0);
clobbered_aliases.push(self.ci.nodes.aclass_index(value.id).0);
if base.has_pointers(self.tys) {
clobbered_aliases.push(0);
}
@ -2674,7 +2827,7 @@ impl<'a> Codegen<'a> {
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
if let Some(base) = self.tys.base_of(value.ty) {
clobbered_aliases.push(self.aclass_index(value.id).0);
clobbered_aliases.push(self.ci.nodes.aclass_index(value.id).0);
if base.has_pointers(self.tys) {
clobbered_aliases.push(0);
}
@ -3600,7 +3753,7 @@ impl<'a> Codegen<'a> {
self.ci.scope.vars.drain(..).for_each(|v| v.remove_ignore_arg(&mut self.ci.nodes));
self.ci.finalize(&mut self.pool.nid_stack);
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
if self.errors.borrow().len() == prev_err_len {
self.ci.emit_body(self.tys, self.files, sig, self.pool);
@ -3737,7 +3890,7 @@ impl TypeParser for Codegen<'_> {
self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) });
scope = mem::take(&mut self.ci.scope.vars);
self.ci.finalize(&mut self.pool.nid_stack);
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
let res = if self.errors.borrow().len() == prev_err_len {
self.emit_and_eval(file, ret, &mut [])
@ -3781,7 +3934,7 @@ impl TypeParser for Codegen<'_> {
self.expr(&(Expr::Return { pos: expr.pos(), val: Some(expr) }));
self.ci.finalize(&mut self.pool.nid_stack);
self.ci.finalize(&mut self.pool.nid_stack, self.tys, self.files);
let ret = self.ci.ret.expect("for return type to be infered");
if self.errors.borrow().len() == prev_err_len {

View file

@ -809,6 +809,7 @@ impl<'a> Function<'a> {
let mut args = self.nodes[VOID].outputs[ARG_START..].to_owned().into_iter();
while let Some(ty) = typs.next_value(self.tys) {
let arg = args.next().unwrap();
debug_assert_eq!(self.nodes[arg].kind, Kind::Arg);
match parama.next(ty, self.tys) {
None => {}
Some(PLoc::Reg(r, _) | PLoc::WideReg(r, _) | PLoc::Ref(r, _)) => {