Una bifurcación , salto o transferencia es una instrucción en un programa informático que puede hacer que un ordenador comience a ejecutar una secuencia de instrucciones diferente y, por lo tanto, se desvíe de su comportamiento predeterminado de ejecutar las instrucciones en orden. [ a ] La bifurcación (o ramificación , ramificado ) también puede referirse al acto de cambiar la ejecución a una secuencia de instrucciones diferente como resultado de ejecutar una instrucción de bifurcación. Las instrucciones de bifurcación se utilizan para implementar el flujo de control en bucles y condicionales de programas (es decir, ejecutar una secuencia particular de instrucciones solo si se cumplen ciertas condiciones).
Una instrucción de salto puede ser incondicional , lo que siempre resulta en un salto, o condicional , lo que puede o no provocar un salto dependiendo de alguna condición. Además, según cómo especifique la dirección de la nueva secuencia de instrucciones (la dirección de destino), una instrucción de salto se clasifica generalmente como directa , indirecta o relativa , lo que significa que la instrucción contiene la dirección de destino, o especifica dónde se encuentra la dirección de destino (por ejemplo, un registro o una ubicación de memoria), o especifica la diferencia entre la dirección actual y la de destino.
Implementación
Las instrucciones de salto pueden modificar el contenido del contador de programa (PC) de la CPU (o puntero de instrucción en los microprocesadores Intel). El contador de programa almacena la dirección de memoria de la siguiente instrucción de máquina que se debe buscar y ejecutar. Por lo tanto, si se ejecuta un salto, la CPU ejecuta código desde una nueva dirección de memoria, modificando la lógica del programa según el algoritmo planificado por el programador.
Un tipo de bifurcación a nivel de máquina es la instrucción de salto . Estas pueden o no resultar en la carga o modificación del contador de programa (PC) con un valor nuevo y diferente al que normalmente tendría (incrementándose más allá de la instrucción actual para apuntar a la siguiente). Los saltos suelen tener formas incondicionales y condicionales , donde estas últimas pueden ejecutarse o no (el PC se modifica o no) dependiendo de alguna condición.
El segundo tipo de bifurcación a nivel de máquina es la instrucción de llamada, que se utiliza para implementar subrutinas . Al igual que las instrucciones de salto, las llamadas pueden modificar o no el contador de programa (PC) según códigos de condición; sin embargo, además, se guarda una dirección de retorno en un lugar seguro de la memoria (generalmente en una estructura de datos residente en memoria llamada pila ). Al finalizar la subrutina, esta dirección de retorno se restaura en el PC, y así la ejecución del programa se reanuda con la instrucción que sigue a la instrucción de llamada.
El tercer tipo de bifurcación a nivel de máquina es la instrucción de retorno . Esta instrucción extrae una dirección de retorno de la pila y la carga en el registro PC, devolviendo así el control a la rutina que la llamó. Las instrucciones de retorno también pueden ejecutarse condicionalmente. Esta descripción se refiere a la práctica habitual; sin embargo, el programador de la máquina tiene considerables facultades para manipular la dirección de retorno en la pila y, por lo tanto, redirigir la ejecución del programa de diversas maneras.
Dependiendo del procesador, las instrucciones de salto y llamada pueden modificar el contenido del registro PC de diferentes maneras. Se puede cargar una dirección absoluta, o bien, se puede sumar o restar algún valor (o desplazamiento) al contenido actual del PC, lo que hace que la dirección de destino sea relativa a la posición actual en el programa. El origen del valor de desplazamiento puede variar, por ejemplo, un valor inmediato integrado en la instrucción, el contenido de un registro del procesador o una ubicación de memoria, o el contenido de alguna ubicación sumado a un valor de índice.
El término rama también se puede usar para referirse a programas en lenguajes de programación de alto nivel . En estas ramas, generalmente adoptan la forma de sentencias condicionales de diversas formas que encapsulan la secuencia de instrucciones que se ejecutará si se cumplen las condiciones. Las instrucciones de rama incondicional, como GOTO, se utilizan para saltar incondicionalmente a una secuencia de instrucciones diferente. Si el algoritmo requiere una rama condicional, la llamada a la subrutina GOTO (o GOSUB) va precedida de una sentencia IF-THEN que especifica la(s) condición(es). Todos los lenguajes de alto nivel admiten algoritmos que pueden reutilizar código como un bucle , una estructura de control que repite una secuencia de instrucciones hasta que se cumple alguna condición que provoca la terminación del bucle. Los bucles también califican como instrucciones de rama. A nivel de máquina, los bucles se implementan como saltos condicionales ordinarios que redirigen la ejecución a código repetitivo.
En las CPU con registros de bandera , una instrucción anterior establece una condición en dicho registro. Esta instrucción puede ser aritmética o lógica , y suele estar cerca de la bifurcación, aunque no necesariamente sea la instrucción inmediatamente anterior. La condición almacenada se utiliza posteriormente en una bifurcación, como por ejemplo `jump if overflow-flag set` . Esta información temporal suele almacenarse en un registro de bandera, pero también puede ubicarse en otro lugar. El diseño de un registro de bandera es sencillo en ordenadores más lentos y simples. En ordenadores rápidos, un registro de bandera puede suponer un cuello de botella en la velocidad, ya que las instrucciones que podrían operar en paralelo (en varias unidades de ejecución ) necesitan establecer los bits de la bandera en una secuencia específica.
También existen máquinas (o instrucciones específicas) donde la condición puede ser verificada por la propia instrucción de salto, como por ejemplo `bifurcar <etiqueta> si el registro X es negativo` . En diseños de computadoras sencillos, las bifurcaciones de comparación ejecutan más operaciones aritméticas y pueden consumir más energía que las bifurcaciones de registro de indicadores. En diseños de computadoras rápidas, las bifurcaciones de comparación pueden ser más rápidas que las bifurcaciones de registro de indicadores, ya que pueden acceder a los registros con mayor paralelismo, utilizando los mismos mecanismos de la CPU que un cálculo.
Algunas arquitecturas de CPU orientadas a palabras tempranas [ b ] implementan operaciones condicionales de "saltar las siguientes instrucciones", saltando a NSI, NSI+1 o NSI+2 (PC+1, PC+2, PC+3) según los resultados de la comparación. Algunas arquitecturas de CPU simples, que aún se encuentran en microcontroladores, pueden no implementar un salto condicional, sino solo operaciones de salto y salto incondicional. Un salto o llamada condicional se implementa, por lo tanto, como un salto condicional de una instrucción de salto o llamada incondicional.
Ejemplos
Dependiendo de la arquitectura del ordenador , el mnemónico en lenguaje ensamblador para una instrucción de salto suele ser una forma abreviada de la palabra "jump" o " branch" , a menudo acompañada de otras letras informativas (o un parámetro adicional) que representan la condición. En ocasiones, también se incluyen otros detalles, como el rango del salto (el tamaño del desplazamiento) o un modo de direccionamiento especial que debe utilizarse para localizar el desplazamiento efectivo real.
Esta tabla enumera las instrucciones de salto o bifurcación a nivel de máquina que se encuentran en varias arquitecturas conocidas:
* x86, PDP-11, VAX y algunos otros, establecen el indicador de acarreo para indicar préstamo y lo borran para indicar ausencia de préstamo . ARM, 6502 , PIC y algunos otros hacen lo contrario para las operaciones sustractivas. Esta función invertida del indicador de acarreo para ciertas instrucciones está marcada con ( * ), es decir, préstamo = no acarreo en algunas partes de la tabla, pero si no se indica lo contrario, préstamo ≡ acarreo. Sin embargo, la mayoría de las arquitecturas manejan el acarreo en las operaciones aditivas de la misma manera.
Problemas de rendimiento con las instrucciones de bifurcación
Para lograr un alto rendimiento, los procesadores modernos utilizan arquitectura segmentada (pipeline ). Esta arquitectura consta de varias partes, cada una de las cuales procesa parcialmente una instrucción, envía sus resultados a la siguiente etapa de la segmentación y comienza a trabajar en la siguiente instrucción del programa. Este diseño requiere que las instrucciones se ejecuten en una secuencia fija e invariable. Las instrucciones de bifurcación condicional hacen imposible conocer esta secuencia. Por lo tanto, las bifurcaciones condicionales pueden causar bloqueos que obligan a reiniciar la segmentación en otra parte del programa.
Mejorar el rendimiento reduciendo los atascos en las ramas.
Varias técnicas mejoran la velocidad al reducir las paradas provocadas por bifurcaciones condicionales.
Sugerencias para la predicción de ramificaciones
Históricamente, la predicción de bifurcaciones se basaba en estadísticas y utilizaba el resultado para optimizar el código. Un programador compilaba una versión de prueba del programa y la ejecutaba con datos de prueba. El código de prueba contabilizaba cómo se tomaban realmente las bifurcaciones. El compilador utilizaba las estadísticas del código de prueba para optimizar las bifurcaciones del código publicado. La optimización aseguraba que la dirección de bifurcación más rápida (se tomara o no) fuera siempre la ruta de flujo de control más frecuente. Para permitir esto, las CPU deben diseñarse con (o al menos tener) una temporización de bifurcación predecible. Algunas CPU tienen conjuntos de instrucciones (como Power ISA ) diseñados con "sugerencias de bifurcación" para que el compilador pueda indicar a la CPU cómo debe tomarse cada bifurcación.
El problema con la predicción de ramas de software es que requiere un proceso de desarrollo de software complejo.
Predictores de ramificación de hardware
Para ejecutar cualquier software, los predictores de bifurcación por hardware trasladaron las estadísticas a la electrónica. Los predictores de bifurcación son partes del procesador que adivinan el resultado de una bifurcación condicional. Luego, la lógica del procesador se basa en la suposición y comienza a ejecutar el flujo de instrucciones esperado. Un ejemplo de un esquema simple de predicción de bifurcación por hardware consiste en asumir que todas las bifurcaciones hacia atrás (es decir, hacia un contador de programa menor) se toman (porque forman parte de un bucle) y todas las bifurcaciones hacia adelante (hacia un contador de programa mayor) no se toman (porque salen de un bucle). Se desarrollan y validan estadísticamente mejores predictores de bifurcación ejecutándolos en simulación con diversos programas de prueba. Los buenos predictores suelen tener en cuenta los resultados de ejecuciones anteriores de una bifurcación. Las computadoras más rápidas y costosas pueden entonces funcionar más rápido invirtiendo en una electrónica de predicción de bifurcación más avanzada. En una CPU con predicción de bifurcación por hardware, las sugerencias de bifurcación permiten que la predicción de bifurcación supuestamente superior del compilador anule la predicción de bifurcación más simple del hardware.
Código sin ramificaciones
Algunas lógicas pueden escribirse sin bifurcaciones o con menos bifurcaciones. A menudo es posible usar operaciones bit a bit , movimientos condicionales u otras predicaciones en lugar de bifurcaciones. [ 2 ] [ 3 ] De hecho, el código sin bifurcaciones es imprescindible para la criptografía debido a los ataques de temporización . [ 4 ]
Ranura de retardo
Otra técnica es la ranura de retardo de bifurcación . En este enfoque, siempre se ejecuta al menos una instrucción después de una bifurcación, con algunas excepciones como la instrucción de bifurcación probable/improbable de la arquitectura MIPS . Por lo tanto, la computadora puede usar esta instrucción para realizar trabajo útil independientemente de si su pipeline se bloquea o no. Este enfoque fue históricamente popular en las computadoras RISC . En una familia de CPU compatibles, complica las CPU multiciclo (sin pipeline), las CPU más rápidas con pipelines más largos de lo esperado y las CPU superescalares (que pueden ejecutar instrucciones fuera de orden).
Véase también
Notas
- ↑ Al menos conceptualmente; véase ejecución fuera de orden .
- ↑ Por ejemplo, compare el acumulador con el almacenamiento (CAS) y compare el acumulador lógico con el almacenamiento (LAS) en el IBM 7094 [ 1 ]
Referencias
- ↑ "Instrucciones de control" (PDF) . Principios de funcionamiento del IBM 7094 (PDF) . Biblioteca de referencia de sistemas. pág. 53. A22-6703-4 . Consultado el 22 de septiembre de 2025 .
- ↑ Knuth, Donald (2008). El arte de la programación informática . Vol. 4, Prefascículo 1A (Revisión 6 ed.). pp. 48– 49.
- ↑ "Evitando ramificaciones" . Wiki de programación de ajedrez .
- ↑ "Criptografía de tiempo constante" . BearSSL .
Enlaces externos
- Documentación gratuita para IA-32 y x86-64 , proporcionada por Intel.
- Preguntas frecuentes sobre el PDP-11
- El conjunto de instrucciones ARM
- Flujo de control
- Procesamiento de instrucciones
- Código máquina