En la teoría del compilador , la eliminación de código muerto ( DCE , eliminación de código muerto , eliminación de código muerto o tira de código muerto ) es una optimización del compilador para eliminar el código muerto (código que no afecta los resultados del programa). Eliminar dicho código tiene varios beneficios: reduce el tamaño del programa , una consideración importante en algunos contextos, reduce el uso de recursos como el número de bytes a transferir [1] y permite que el programa en ejecución evite ejecutar operaciones irrelevantes , lo que reduce su tiempo de ejecución . También puede permitir mayores optimizaciones al simplificar la estructura del programa. El código inactivo incluye código que nunca se puede ejecutar ( código inalcanzable ) y código que solo afecta a variables inactivas (escritas en ellas, pero nunca más leídas), es decir, irrelevantes para el programa.
Considere el siguiente ejemplo escrito en C.
int foo ( vacío ) { int a = 24 ; int b = 25 ; /* Asignación a variable muerta */ int c ; c = un * 4 ; devolver c ; segundo = 24 ; /* Código inalcanzable */ return 0 ; }
Un análisis simple de los usos de los valores mostraría que el valor de b
después de la primera asignación no se usa dentro de foo
. Además, b
se declara como una variable local en el interior foo
, por lo que su valor no se puede utilizar en el exterior foo
. Por lo tanto, la variable b
está muerta y un optimizador puede recuperar su espacio de almacenamiento y eliminar su inicialización.
Además, debido a que la primera declaración de retorno se ejecuta incondicionalmente y no hay ninguna etiqueta después de ella a la que pueda llegar un "goto", ninguna ruta de ejecución factible llega a la segunda asignación a b
. Por lo tanto, la asignación es inalcanzable y puede eliminarse. Si el procedimiento tuviera un flujo de control más complejo , como una etiqueta después de la declaración de devolución y goto
otra parte del procedimiento, entonces podría existir una ruta de ejecución factible para la asignación a b
.
Además, aunque algunos cálculos se realizan en la función, sus valores no se almacenan en ubicaciones accesibles fuera del alcance de esta función. Además, dado que la función devuelve un valor estático (96), se puede simplificar al valor que devuelve (esta simplificación se llama plegado constante ).
La mayoría de los compiladores avanzados tienen opciones para activar la eliminación de código muerto, a veces en distintos niveles. Es posible que un nivel inferior solo elimine instrucciones que no se pueden ejecutar. Es posible que un nivel superior tampoco reserve espacio para variables no utilizadas. Un nivel aún más alto podría determinar instrucciones o funciones que no sirven para nada y eliminarlas.
Un uso común de la eliminación de códigos muertos es como alternativa a la inclusión de código opcional a través de un preprocesador . Considere el siguiente código.
int principal ( vacío ) { int a = 5 ; int b = 6 ; intc ; _ c = a * ( segundo / 2 ); if ( 0 ) { /* DEPURACIÓN */ printf ( "%d \n " , c ); } devolver c ; }
Debido a que la expresión 0 siempre se evaluará como false , el código dentro de la instrucción if nunca podrá ejecutarse y la eliminación del código muerto lo eliminaría por completo del programa optimizado. Esta técnica es común en la depuración para activar opcionalmente bloques de código; El uso de un optimizador con eliminación de código muerto elimina la necesidad de utilizar un preprocesador para realizar la misma tarea.
En la práctica, gran parte del código muerto que encuentra un optimizador se crea mediante otras transformaciones en el optimizador. Por ejemplo, las técnicas clásicas para reducir la fuerza del operador insertan nuevos cálculos en el código y desactivan los cálculos más antiguos y costosos. [2] La eliminación posterior del código muerto elimina esos cálculos y completa el efecto (sin complicar el algoritmo de reducción de fuerza).
Históricamente, la eliminación de códigos inactivos se realizaba utilizando información derivada del análisis de flujo de datos . [3] Un algoritmo basado en el formulario estático de asignación única (SSA) aparece en el artículo original de la revista sobre el formulario SSA de Ron Cytron et al. [4] Robert Shillingsburg (también conocido como Shillner) mejoró el algoritmo y desarrolló un algoritmo complementario para eliminar operaciones de flujo de control inútiles. [5]
El código muerto normalmente se considera muerto incondicionalmente . Por lo tanto, es razonable intentar eliminar el código inactivo mediante la eliminación del código inactivo en el momento de la compilación .
Sin embargo, en la práctica también es común que las secciones de código representen código inactivo o inalcanzable solo bajo ciertas condiciones , que pueden no conocerse en el momento de la compilación o el ensamblaje. Dichas condiciones pueden ser impuestas por diferentes entornos de ejecución (por ejemplo, diferentes versiones de un sistema operativo, o diferentes conjuntos y combinaciones de controladores o servicios cargados en un entorno de destino particular), que pueden requerir diferentes conjuntos de casos especiales en el código, pero al menos al mismo tiempo se convierte en código condicionalmente muerto para los demás casos. [6] [7] Además, el software (por ejemplo, un controlador o un servicio residente) puede configurarse para incluir o excluir ciertas funciones según las preferencias del usuario, lo que hace que las partes de código no utilizadas sean inútiles en un escenario particular. [6] [7] Si bien se puede desarrollar software modular para cargar bibliotecas dinámicamente solo bajo demanda, en la mayoría de los casos, no es posible cargar solo las rutinas relevantes de una biblioteca en particular, e incluso si esto fuera compatible, una rutina puede todavía incluye secciones de código que pueden considerarse código muerto en un escenario determinado, pero que ya no se pueden descartar en el momento de la compilación.
Las técnicas utilizadas para detectar dinámicamente la demanda, identificar y resolver dependencias, eliminar dicho código inactivo condicionalmente y recombinar el código restante durante la carga o el tiempo de ejecución se denominan eliminación dinámica de código inactivo [6] [7] [8] [9] [10 ] [11] [12] [13] [14] [15] [16] [17] [18] o eliminación dinámica de instrucciones muertas . [19]
La mayoría de los lenguajes de programación, compiladores y sistemas operativos ofrecen poco o nada más soporte que la carga dinámica de bibliotecas y los enlaces tardíos , por lo que el software que utiliza la eliminación dinámica de código muerto es muy raro junto con lenguajes compilados de antemano o escritos en lenguaje ensamblador . [8] [12] [9] Sin embargo, las implementaciones de lenguaje que realizan una compilación justo a tiempo pueden optimizar dinámicamente la eliminación del código muerto. [18] [20] [21]
Aunque con un enfoque bastante diferente, a veces también se utilizan enfoques similares para la actualización dinámica de software y la aplicación de parches en caliente .
[…] cualquiera de las […] opciones se puede excluir "permanentemente" en el momento de la instalación (también guardará la memoria para los extractos de código correspondientes debido a nuestra
Eliminación dinámica de códigos muertos
), o se puede habilitar o deshabilitar en cualquier momento posterior. a través de funciones API en caso de que alguien quiera evitar que un usuario pueda reiniciar la máquina.
[…] estamos considerando agregar más llamadas de vaciado de caché sincrónicas […] Debido a nuestro método de eliminación dinámica de código muerto, esto no causaría ningún tipo de hinchazón cuando no sea necesario en una configuración de destino particular, ya que una llamada de vaciado de caché particular se incluiría en La imagen de tiempo de ejecución de FreeKEYB solo si el caché de disco correspondiente también está cargado o si los interruptores de línea de comando le dijeron a FreeKEYB que cargara el soporte correspondiente.
[…] FreeKEYB crea la imagen en tiempo de ejecución del controlador en el momento de la inicialización dependiendo del tipo de máquina en la que se está cargando, el tipo de teclado, diseño, país y página de códigos utilizados, el tipo de mouse y adaptador(es) de video instalados, el otros controladores cargados en ese sistema, el sistema operativo y los métodos de carga y reubicación utilizados, las características individuales incluidas y las opciones de configuración especificadas en la línea de comando.
Debido a la gran cantidad de opciones y opciones admitidas en la línea de comandos […] (alrededor de cincuenta opciones […] con múltiples configuraciones posibles) existe una gran cantidad de combinaciones de funciones con incontables dependencias […] lo que resulta en […] un número infinito de [ …] diferentes imágenes de destino.
La técnica de eliminación dinámica de códigos muertos de FreeKEYB logra resolver […] estas […] dependencias y […] eliminar códigos y datos muertos […] no se limita a […] incluir o excluir un número algo limitado de módulos o subrutinas completas y arreglar algunas tablas de despacho como en la programación TSR clásica, pero […] funciona […] a […] nivel de bytes […] capaz de eliminar […] instrucciones individuales en medio de rutinas más grandes […] distribuidas por todo el código para manejar un caso particular o admitir una característica específica […] se utilizan herramientas especiales para analizar el código […] y crear […] tablas de reparación […] automatizadas […] utilizando definiciones condicionales […] para declarar los diversos casos […] no sólo es opcional en el momento del ensamblaje sino también en el momento de la inicialización […] sin la […] sobrecarga de tener al menos una cantidad de código inactivo en la imagen en tiempo de ejecución […] para realizar un seguimiento de todas las dependencias entre […] estos condicionales, construyen y reubican dinámicamente la imagen en tiempo de ejecución, arreglan todas las referencias entre estas partes binarias pequeñas, cambiantes y en movimiento […] aún permitiendo usar el pequeño modelo de estilo .COM/.SYS […] […] está listo en el momento de la inicialización […] API para importar y exportar estructuras de objetos entre FreeKEYB y la aplicación que llama […] para cambiar su tamaño y moverlas internamente de forma transparente […] en tiempo de ejecución […]
[…] Brandneue[s] Feature, der
dynamischen Dead-Code-Elimination
, die die jeweils notwendigen Bestandteile des Treibers erst zum Installationszeitpunkt zusammenbastelt und reloziert, so daß keine ungenutzten Code- oder Datenbereiche mehr residente bleiben (zB wenn jemand ein bestimmtes FreeKEYB- Característica no benötigt).
[…](NB. Esto representa la primera implementación conocida de eliminación dinámica granular de código muerto a nivel de bytes para software ensamblado o compilado con anticipación ).
[…] una […] característica única […] que llamamos
eliminación dinámica de códigos muertos
, por lo que puede en el momento de la instalación […] especificar qué componentes del controlador desea y cuáles no.
Esto llega hasta un punto de modularización cargable dinámica y vinculación tardía que no he visto en DOS hasta ahora.
Si no le gusta el protector de pantalla, las macros, la calculadora o la compatibilidad con el mouse, o <casi cualquier otra cosa>, puede especificar esto en la línea de comando y FreeKEYB, teniendo en cuenta todas las dependencias entre las rutinas, lo hará por completo. elimine todos los fragmentos de código, que tratan con esa característica y no son necesarios para proporcionar la funcionalidad solicitada, antes de que el controlador reubique la imagen en la ubicación de destino y se convierta en residente.
Eliminar algunas funciones más pequeñas solo ahorra un par de bytes, pero excluir componentes más complejos puede ahorrarle medio Kb y más.
También puede especificar el tamaño de las áreas de datos […]
[…] el cargador optimizará dinámicamente las áreas de datos residentes y las secciones de código a nivel de bytes para eliminar cualquier redundancia del controlador dependiendo de la configuración local y del hardware/software/controlador determinados.
[…]
[…] Dado que FreeKEYB utiliza nuestra
eliminación dinámica de código muerto
para optimizar su imagen de memoria para el entorno de destino en el momento de la carga, ciertamente me gustaría agregar soporte especial en FreeKEYB para
GEOS
, que podría controlarse mediante una opción de línea de comando, de modo que El código adicional solo se carga cuando también se usa GEOS.
[…]
[…] Estaría dispuesto a agregar ganchos especiales en FreeKEYB, nuestro controlador de teclado DOS muy avanzado, si demostrara mejorar la usabilidad bajo
GEOS
[…] Debido a nuestra nueva y sofisticada tecnología
Dynamic Dead Code Elimination
que elimina a nivel de bytes cualquier código fragmentos no utilizados en un determinado controlador, usuario o configuración del sistema y entorno de hardware cuando el instalador del controlador crea y reubica la imagen de carga de sí mismo, esto no tendría ningún impacto en la memoria para los usuarios que no son de GEOS, por lo que no hay mucho de qué preocuparse (huella de memoria, etc.) como en los controladores DOS codificados tradicionalmente.
[…]
[…]
La evaluación diferida
es básicamente
una eliminación dinámica de código muerto
.
[…](NB. Posiblemente el primer uso público del término eliminación dinámica de código muerto , aunque solo conceptualmente y con un enfoque en la evaluación diferida en lenguajes funcionales ).