Eliminación de código muerto

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Optimización de compilador para eliminar código que no afecta los resultados del programa

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 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, 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.

Ejemplos

Considere el siguiente ejemplo escrito en C.

int Foo()vacío) {} int a = 24; int b = 25; * Asignación a la variable muerta */ int c; c = a * 4; retorno c; b = 24; /* Código no aplicable */ retorno 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 dentro de foo, por lo que su valor no se puede utilizar fuera de 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 devolución se ejecuta incondicionalmente y no hay ninguna etiqueta después de la cual se pueda indicar "goto" podría alcanzar, ninguna ruta de ejecución factible llega a la segunda asignación a b. Por lo tanto, la asignación es inalcanzable y se puede eliminar. Si el procedimiento tuviera un flujo de control más complejo, como una etiqueta después de la declaración de devolución y un goto en 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 una 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; int c; c = a * ()b / 2); si ()0) {} /* DEBUG */ printf()"n", c); } retorno c;}

Debido a que la expresión 0 siempre se evaluará como falsa, el código dentro de la declaració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 es creado por 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. 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 del flujo de datos. 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. Robert Shillingsburg (también conocido como Shillner) mejoró el algoritmo y desarrolló un algoritmo complementario para eliminar operaciones de flujo de control inútiles.

Eliminación dinámica de códigos muertos

El código inactivo normalmente se considera inactivo 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 sólo 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. 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. 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 aún puede incluir secciones de código que pueden considerarse código muerto en un escenario determinado, pero ya no se puede 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 o eliminación dinámica de instrucciones muertas.

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 con anticipación o escritos en lenguaje ensamblador. Sin embargo, las implementaciones de lenguaje que realizan una compilación justo a tiempo pueden optimizar dinámicamente la eliminación del código inactivo.

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.

Contenido relacionado

Disulfuro de molibdeno

disulfuro de molibdeno es un compuesto inorgánico compuesto de molibdeno y azufre. Su fórmula química es...

AMD K6-III

El K6-III era una línea de microprocesadores x86 fabricada por AMD que se lanzó el 22 de febrero de 1999. El lanzamiento consistió en 400 y modelos de 450...

Cristian bohr

Christian Harald Lauritz Peter Emil Bohr fue un médico danés, padre del físico y premio Nobel Niels Bohr, así como del matemático y futbolista Harald...
Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save