为什么功能中的本地阵列似乎可以防止TCO

Why local arrays in functions seem to prevent TCO?

本文关键字:TCO 阵列 功能 为什么      更新时间:2023-10-16

看起来函数中有一个本地数组会阻止我检查过的所有编译器对其进行尾调用优化:

int foo(int*);
int tco_test() {
    // int arr[5]={1, 2, 3, 4, 5}; // <-- variant 1
    // int* arr = new int[5]; // <-- variant 2
    int x = foo(arr);
    return x > 0 ? tco_test() : x;
}

variant 1处于活动状态时,最终会有一个对tco_test()的真正调用(gcc之前试图进行一些展开,但最终仍会调用该函数(。Variant 2实现了预期的TCO。

本地数组中是否存在无法优化尾部调用的内容?

如果编译器执行TCO,那么所有外部foo(arr)调用都将接收到相同的指针。这是一个可见的语义变化,因此不再是纯粹的优化。

有问题的局部变量是一个数组,这可能是转移注意力;重要的是它通过指针对外部的可见性。

考虑一下这个程序:

#include <stdio.h>
int *valptr[7], **curptr = valptr, **endptr = valptr + 7;
void reset(void)
{
  curptr = valptr;
}
int record(int *ptr)
{
  if (curptr >= endptr)
    return 1;
  *curptr++ = ptr;
  return 0;
}
int tally(void)
{
  int **pp;
  int count = 0;
  for (pp = valptr; pp < curptr; pp++)
    count += **pp;
  return count;
}
int tail_function(int x)
{
  return record(&x) ? tally() : tail_function(x + 1);
}
int main(void)
{
  printf("tail_function(0) = %dn", tail_function(0));
  return 0;
}

tail_function通过尾部调用进行递归时,record函数会记录局部变量x的不同实例的地址。当它的空间用完时,它返回1,从而触发tail_function调用tally并返回。CCD_ 11扫描记录的存储器位置并将它们的值相加。

如果tally受制于TCO,则只有一个x的实例。实际上,它是这样的:

int tail_function(int x)
{
tail:
  if (record(&x))
    return tally();
  x = x + 1;
  goto tail;
}

因此,现在,record一遍又一遍地记录相同的位置,导致tally计算的值不正确,而不是预期的21

recordtally的逻辑取决于每次激活作用域时实际实例化的x,并且作用域的外部激活具有持续到内部激活终止的寿命。该要求排除了tail_function在恒定空间中的递归;它必须分配单独的CCD_ 21实例。