Rust: Enseñar a rustc a hacer llamadas de cola

Creado en 27 ene. 2011  ·  18Comentarios  ·  Fuente: rust-lang/rust

Rustc aún no sabe cómo hacer llamadas de cola ('be' en lugar de 'ret'). No debería ser demasiado difícil enseñarle cómo hacerlo.

A-LLVM

Comentario más útil

Tenemos planes para implementar eventualmente el TCO garantizado, si es posible. Incluso reservamos una palabra clave para ello, "convertirse". Consulte el repositorio de RFC.

El 3 de agosto de 2016, 19:46 -0400, Antoine PLASKOWSKI [email protected] , escribió:

Soy noticia en rust y estoy muy triste. Intento una función recursiva de cola y apilé el desbordamiento tan rápido. peor cuando compilo en versión el cambio de comportamiento. Él simplemente no llama a mi función. Un amigo me dijo que el LLVM se optimiza a un valor indefinido (¿LLVM sabe que es una recursión de cola infinita?!?).

fn rec(i: i32) { rec(i + 1) } fn main() { println!("{}", rec(0)); }

Estoy de acuerdo con Boiethios, un lenguaje con características funcionales que no implementa tail call miss something.

Escribo en C y C ++, no garantizan la llamada de cola, pero de hecho lo manejan muy bien. No me impedirá aprender a oxidar, ahora que sé que no lo maneja codificaré sin él, así que no es un gran problema.

Pero creo que es una característica muy buena para un lenguaje moderno.


Estás recibiendo esto porque estás suscrito a este hilo.
Responda a este correo electrónico directamente, véalo en GitHub (https://github.com/rust-lang/rust/issues/217#issuecomment-237409642) o silencie el hilo (https://github.com/notifications/unsubscribe -auth/AABsipoedHrbnKDekmzCr-dl8M6g-Gojks5qcShKgaJpZM4AC-q_).

Todos 18 comentarios

Según Graydon, tenemos que pensar un poco más en llamar convenciones antes de implementar esto. LLVM requiere la convención fastcc para implementar llamadas de cola, pero no hace exactamente lo que queremos:

graydon: brson: a juzgar por los comentarios en llvm-land, podemos estar en problemas. es posible que podamos compensar lo que fastcc está haciendo hoy, pero se le permite cambiar de opinión mañana.
graydon: Pensé fastcc == x86_fastcall, pero estaba equivocado.
graydon: esas son convenciones de llamadas diferentes. fastcc es "lo que se siente llvm esta semana"
graydon: necesitamos cambiar a x86_fastcall y luego, a más largo plazo, escribir nuestra propia convención de llamadas.

Actualmente, esto se implementa a medias debido a algunas complicaciones en la forma en que LLVM trata las llamadas de cola. Rustc analiza las expresiones 'be' y las traduce como pares call+ret, lo cual es suficiente para que podamos 'trabajar' en casos simples, no muy profundos. Podría ser suficiente para arrancar.

Aparentemente, solo hay un CC (fastcc) que admite llamadas de seguimiento garantizadas y, para adaptarnos a él, debemos cambiar nuestras suposiciones de ABI en varios lugares (se convierte en restauración de llamadas cuando habilita el indicador -tailcallopt). Entonces, incluso si anotamos nuestros pares call+ret con 'tail', según sea necesario, no podemos decirle a LLVM que comience a realizar esa optimización todavía.

Resultó que no necesitaba más implementación de la que se muestra aquí para el alojamiento propio. Punting al próximo hito.

¿Alguien siente que en realidad vamos a hacer esto nunca más?

No parece que vaya a suceder.

¿Cuál es la situación con LLVM y llamadas de cola? Si se puede hacer que funcionen de manera confiable sin algunas cosas invasivas como declarar que la persona que llama recibe una llamada final, podríamos definir un conjunto limitado de cosas que se pueden pasar a llamadas posteriores (argumentos propios de la persona que llama, escalares) y error cuando algo se pasa lo demás. Sin embargo, tales llamadas de cola pueden no ser muy útiles en la práctica.

Solo funcionan con un ABI de destinatario de la llamada que es subóptimo en casos que no son de cola. Entonces, en cierto sentido, sí, requieren que se declare que el destinatario de la llamada será llamado de cola.

Podríamos implementar esto, por ejemplo, analizando una caja y cuando encontremos una función que se llama tail, ya sea compilándola por separado bajo el ABI de llamada amigable a cola, o compilando envoltorios que cambian ABI en la entrada, o algo así. O, alternativamente, podemos cambiar _todas_ las funciones en todas partes para usar el ABI compatible con las llamadas posteriores. No estoy seguro de si actualmente estamos haciendo eso. Estuvimos un tiempo, pero es posible que hayamos parado. Es el ABI de "llamada rápida" de LLVM, que no creo que estemos usando más.

En cualquier caso, es un poco de un lío.

Tenemos optimización de llamadas entre hermanos, ahora. ¿Planeamos intentar hacer más?

No va a suceder, excepto en la medida en que se aborda en el n.° 2216. La solución para #2216 debe ser lo suficientemente amplia como para admitir la codificación de máquina de estado general.

Esto continúa surgiendo en la conversación y hemos reservado be nuevamente. Reapertura.

Creo que el comentario de Graydon en la lista de correo:

https://mail.mozilla.org/pipermail/rust-dev/2013-April/003557.html

pone un clavo en este ataúd. Reproducido aquí:


El 04/10/2013 a las 5:43 a. m., Artella Coding escribió:

Hola, ¿el óxido optimiza la cola de llamadas? La razón por la que pregunto es que
la siguiente implementación recursiva de llamada de cola da como resultado una pila
Desbordamiento. Gracias.

No, y muy probablemente no lo hará. Tenemos un error de larga data en esto:

https://github.com/mozilla/rust/issues/217

así como una página wiki y varios hilos de listas de correo:

https://github.com/mozilla/rust/wiki/Bikeshed-tailcall
https://mail.mozilla.org/pipermail/rust-dev/2011-agosto/000689.html
https://mail.mozilla.org/pipermail/rust-dev/2012-January/001280.html
...

El resumen de todo esto es:

  • Todos sabemos que las llamadas de cola son una característica virtuosa del lenguaje.
    No se requiere una mayor elaboración de sus virtudes. Muchos de nosotros
    tienen antecedentes de lisp y ML y me gustaría bastante. Sus
    la ausencia es angustia y tristeza, no se llega a ella a la ligera.
  • Tail pide "jugar mal" con destrucción determinista. Incluso
    caída determinista de ~ cajas. por no decir que no son
    se pueden componer, pero las opciones para componerlos son incómodas para la interfaz de usuario,
    penaliza el rendimiento, complica la semántica o todo lo anterior.
  • Las llamadas de cola también "juegan mal" con suposiciones en las herramientas C, que incluyen
    ABI de plataforma y enlaces dinámicos.
  • Las llamadas de cola requieren una convención de llamadas que es un golpe de rendimiento
    en relación con la convención C.
  • Encontramos que la mayoría de los casos de _recursión_ de cola se convierten razonablemente bien en
    bucles, y la mayoría de los casos de llamadas de cola no recursivas codifican el estado
    máquinas que se convierten razonablemente bien en bucles envueltos
    enumeraciones Ninguno de estos es _bastante_ tan bonito como el
    variantes que usan llamadas de seguimiento, pero funcionan y son "igual de rápidas"*,
    así como idiomática para programadores de C y C++ (que son nuestros
    público principal).

Lamento decir todo esto, y es con gran pesar en mi corazón, pero
intentó y no encontró una manera de hacer las compensaciones asociadas con ellos
resumen a un argumento para la inclusión en el óxido.

-Graydon

  • En cuanto a la velocidad, una máquina de estado de conmutación en un bucle es un envío indirecto, por lo que
    probablemente se ejecutará más lento que una máquina de estado codificada por llamadas de cola de
    Estado a estado; por otro lado, si la forma de obtener este rendimiento
    "atrás" es activar las llamadas de seguimiento en todas partes, estaríamos intercambiando uno aislado
    caso de rendimiento subóptimo para un impuesto de rendimiento subóptimo de todos los programas. Nosotros
    no encuentro aceptable este intercambio.

Tal vez valga la pena señalar que Haskell comúnmente necesita una transformación de argumento estático para insertar, fusionar, etc. http://stackoverflow.com/a/9660027/667457

Rust podría respaldar la transformación de argumento estático al decir que las funciones definidas dentro de otra función deberían ser elegibles para las optimizaciones de llamada final. O simplemente advierta si las llamadas posteriores entre funciones anidadas dentro de otra función fallaron en la optimización del hermano. No estoy seguro de la situación actual.

Es triste que no se implemente la recursividad de la cola. Entonces tengo una pregunta: ¿por qué crear una sintaxis de lenguaje que parezca una sintaxis de lenguaje funcional si carece de una característica esencial de funcional?

Soy noticia en rust y estoy muy triste. Intento una función recursiva de cola y apilé el desbordamiento tan rápido. peor cuando compilo en versión el cambio de comportamiento. Él simplemente no llama a mi función. Un amigo me dijo que LLVM se optimiza a un valor indefinido (¿LLVM sabe que es una recursión de cola infinita?!?).

fn rec(i: i32) {
  rec(i + 1)
}

fn main() {
  println!("{}", rec(0));
}

Estoy de acuerdo con Boiethios, un lenguaje con características funcionales que no implementa tail call miss something.

Escribo en C y C ++, no garantizan la llamada final, pero de hecho lo manejan muy bien. No me impedirá aprender a oxidar, ahora sé que la oxidación no lo maneja. Codificaré sin que no sea un gran problema.

Pero creo que es una característica muy buena para un lenguaje moderno.

Darse cuenta de

fn rec(i: i32) {
  println!("Hello");
  rec(i + 1)
}

desbordamiento de pila también en modo de liberación de carga

Tenemos planes para implementar eventualmente el TCO garantizado, si es posible. Incluso reservamos una palabra clave para ello, "convertirse". Consulte el repositorio de RFC.

El 3 de agosto de 2016, 19:46 -0400, Antoine PLASKOWSKI [email protected] , escribió:

Soy noticia en rust y estoy muy triste. Intento una función recursiva de cola y apilé el desbordamiento tan rápido. peor cuando compilo en versión el cambio de comportamiento. Él simplemente no llama a mi función. Un amigo me dijo que el LLVM se optimiza a un valor indefinido (¿LLVM sabe que es una recursión de cola infinita?!?).

fn rec(i: i32) { rec(i + 1) } fn main() { println!("{}", rec(0)); }

Estoy de acuerdo con Boiethios, un lenguaje con características funcionales que no implementa tail call miss something.

Escribo en C y C ++, no garantizan la llamada de cola, pero de hecho lo manejan muy bien. No me impedirá aprender a oxidar, ahora que sé que no lo maneja codificaré sin él, así que no es un gran problema.

Pero creo que es una característica muy buena para un lenguaje moderno.


Estás recibiendo esto porque estás suscrito a este hilo.
Responda a este correo electrónico directamente, véalo en GitHub (https://github.com/rust-lang/rust/issues/217#issuecomment-237409642) o silencie el hilo (https://github.com/notifications/unsubscribe -auth/AABsipoedHrbnKDekmzCr-dl8M6g-Gojks5qcShKgaJpZM4AC-q_).

Solo una pregunta: es hipotéticamente posible realizar análisis de gráficos de llamadas y transformar llamadas de cola en bucles, al menos dentro de una sola caja, pero es computacionalmente costoso en tiempo de compilación y bastante complicado de codificar. ¿Hubo alguna discusión sobre esta posibilidad? Esa seguiría siendo una opción ahora, sin tener en cuenta la elección de la convención de llamadas.

El enfoque de lbstanza es requerir la anotación de funciones para que sean elegibles para TCO (defn+ en lugar de defn). En rust, eso mitigaría en gran medida el tiempo de compilación adicional de la sugerencia de timthelion, siempre y cuando no esté usando llamadas de cola literalmente en todas partes jajaja.

¿Fue útil esta página
0 / 5 - 0 calificaciones