flowchart LR
M13["Módulo 13<br/>Representação intermediária<br/>três endereços"]
M14["Módulo 14<br/>Geração de código<br/>máquina de pilha"]
M15["Módulo 15<br/>Otimização e<br/>integração final<br/>FECHAMENTO"]
ENT["Semana seguinte<br/>Entrega final do<br/>Projeto Integrador"]
M13 --> M14 --> M15 --> ENT
subgraph FECHA["O que o módulo 15 fecha"]
F1["Blocos básicos e grafo<br/>de fluxo de controle"]
F2["Transformações locais<br/>com condição de segurança"]
F3["Ponto fixo pela terceira<br/>vez no semestre"]
F4["Sistema completo:<br/>compila, valida, grava<br/>e executa"]
end
M15 --- FECHA
M14 -.->|"invariante de uso único<br/>acusa a transformação incompatível"| F2
F4 -.->|"execução diferencial"| ENT
Módulo 15: Plano de Aula — Otimização e Integração Final
Documento exclusivo do professor. Este é o guia operacional das seis aulas do módulo 15: roteiro por blocos das duas aulas teóricas, plano das quatro aulas de tutoria, entregáveis e riscos antecipados. Não distribua à turma — o gancho da abertura depende de a turma não saber de antemão que o otimizador não remove nada, e as questões de discussão perdem função assim que a resposta circula.
Visão Geral do Módulo
Onde este módulo fica — é o último, e ele fecha duas coisas ao mesmo tempo: o conteúdo e o percurso.
Este módulo tem duas naturezas que competem pelo mesmo espaço, e conduzir mal essa competição é o erro característico do último módulo do semestre. A primeira natureza é instrumental e traz conteúdo novo de verdade: blocos básicos, grafo de fluxo de controle, transformações locais com condição de segurança e a noção de solução por ponto fixo aplicada a fluxo de dados. A segunda é de fechamento: a integração ponta a ponta, a validação do objeto e a retrospectiva do caminho da expressão regular até o programa executável.
A tentação é sacrificar a primeira em nome da segunda, porque a segunda é confortável e a turma está cansada. Resista. Um módulo 15 que vira sessão de encerramento entrega estudantes que sabem nomear quatro otimizações e não sabem enunciar a condição de segurança de nenhuma — e a condição é o conteúdo. Faça o inverso do que o cansaço pede: o conteúdo instrumental ocupa a maior parte das duas aulas teóricas, e a retrospectiva entra no bloco de fechamento, curta e com números do artefato na mão.
Há um segundo traço que distingue este módulo dos catorze anteriores: ele tem dois resultados que contrariam a expectativa, e os dois valem mais que qualquer transformação da lista. O otimizador do projeto de referência remove zero instruções de um programa típico da linguagem, e está certo. E a eliminação de subexpressões comuns, semanticamente correta, não pode ser aplicada a este compilador, porque viola uma precondição da máquina de pilha — quem a acusa é a verificação de uso único escrita no módulo 14, para outro fim. Os dois resultados são o esqueleto do roteiro abaixo; não os trate como curiosidades de rodapé.
Objetivos, Competências e Habilidades
Objetivos de aprendizagem. Introduzir a estrutura sobre a qual a otimização opera e as transformações locais fundamentais. Estabelecer a preservação de semântica como requisito absoluto. Concluir a integração do artefato e conduzir o fechamento conceitual do semestre.
Competências a desenvolver. Capacidade de aplicar transformações a um artefato preservando propriedades que precisam ser mantidas, e de justificar essa preservação. Capacidade de avaliar um sistema completo quanto ao comportamento diante de entrada real, e não apenas de exemplos preparados. Capacidade de comunicar tecnicamente o resultado de um projeto longo.
Habilidades a adquirir. Identificar blocos básicos e construir o grafo de fluxo de controle. Aplicar dobramento de constantes, propagação de cópias, eliminação de subexpressões comuns e eliminação de código morto, justificando a segurança de cada uma. Explicar o papel da análise de fluxo de dados e a noção de solução por ponto fixo. Integrar as fases e diagnosticar falhas ponta a ponta. Apresentar tecnicamente o projeto.
Estrutura das Aulas
Aulas 1 e 2 — Aula Teórica
Roteiro por blocos — a ordem importa, porque o módulo inteiro é a resposta a um número decepcionante mostrado logo na abertura.
Bloco de abertura — o otimizador que não otimiza
Projete o relatório do projeto de referência antes de dizer qualquer palavra sobre otimização, exatamente como ele saiu: um programa típico da linguagem entra com doze instruções, sai com doze, e o contador registra zero dobramentos e zero instruções mortas removidas. Pergunte só isto: o otimizador está quebrado?
Deixe a sala responder e anote as hipóteses; a que sempre aparece é “está quebrado” ou “não foi chamado”. Responda que o otimizador funciona e que o número é o correto, e não explique por quê ainda — diga apenas que ao fim das duas aulas eles vão saber dizer, com a estrutura na mão, por que não havia nada a remover, e que vão saber também por que a otimização continua no programa da disciplina mesmo assim.
Escreva no canto do quadro as três palavras que sustentam as duas aulas — estrutura, condição, preservação — e não as apague; você volta a elas no fechamento.
Bloco seguinte — blocos básicos e o algoritmo dos líderes
Comece pela pergunta que parece ingênua: por que uma otimização precisa de estrutura alguma, se dobrar dois literais é olhar a instrução e trocar? A resposta que abre o bloco é a transformação de remoção: para saber se ninguém lê um resultado é preciso olhar adiante, por todos os caminhos alcançáveis, e isso não é propriedade da instrução, é propriedade da instrução no fluxo de controle.
Defina bloco básico pela maximalidade e insista na palavra: qualquer instrução isolada satisfaz entrada por cima e saída por baixo; o que faz um bloco ser um bloco é ser o maior trecho com essa propriedade. Então dê as três condições de líder — primeira instrução, alvo de desvio, sucessor imediato de desvio — e conduza o exemplo numérico no quadro, com os números do artefato de referência já resolvidos.
Tome a sequência de doze instruções que a tradução produz para uma condição composta. Os líderes são 0, 5 e 10, e a partição resultante é B_0 = [0,5), B_1 = [5,10) e B_2 = [10,12). Faça a verificação de sanidade na frente deles, porque é ela que salva os grupos na tutoria: 5 + 5 + 2 = 12, sem buraco e sem sobreposição, e todo alvo de desvio é início de bloco. Quem aceita uma decomposição sem conferir essa soma constrói transformações locais sobre blocos com desvio no meio, e todas ficam inseguras.
A terceira condição de líder é a que some. Pergunte à turma por que a instrução seguinte a um desvio começa um bloco novo, e espere: a resposta é que o desvio, ao ser tomado, encerra o bloco, de modo que o que vem depois só é alcançado por outro caminho.
Primeira questão de discussão em duplas. “Numa sequência de instruções, a instrução i_7 vem imediatamente depois de um desvio condicional e não é alvo de desvio nenhum. Ela é líder? (a) não, porque ninguém salta para ela; (b) sim, pela terceira condição, mesmo que o desvio possa não ser tomado; (c) só se o desvio for incondicional; (d) depende de i_7 ser ou não um desvio.”
Aplique o procedimento inteiro: voto individual em silêncio, discussão em duplas, segundo voto. A resposta é (b). Quem vota (a) confunde alcançar com saltar — o controle chega ali por passagem natural quando a condição falha, e chega por salto de outro lugar quando não falha; são dois caminhos, e é isso que faz o ponto ser fronteira. Quem vota (c) é o erro mais informativo: percebeu a ideia e a aplicou pela metade. Feche mostrando que sem essa condição B_0 e B_1 do exemplo virariam um bloco só, com um desvio no meio, e que a hipótese de execução sequencial dentro do bloco deixaria de valer.
Bloco seguinte — o grafo de fluxo e a manobra da análise estática
Construa o grafo do mesmo exemplo no quadro, com os blocos empilhados na ordem textual. De B_0 saem duas arestas, para B_1 e para o fim da regra; de B_1, para B_2 e para o fim; de B_2, só para o fim. Verbalize a regra que a turma erra na implementação: os sucessores se calculam exclusivamente a partir da última instrução do bloco, porque só dela o controle pode sair.
Conte o erro de exibição que a primeira versão da tabela do projeto de referência tinha, porque ele ensina mais que o acerto: o fim da regra não é um bloco, não há instrução lá, e a versão inicial simplesmente descartava as arestas que apontavam para ele. O resultado é que um bloco terminado em desvio condicional aparecia com um sucessor só — um condicional com um sucessor contradiz a definição de condicional, e a estrutura escondia justamente a bifurcação que o grafo existe para exibir.
Feche o bloco com a manobra que atravessa o semestre inteiro: perguntas sobre execuções são indecidíveis em geral, perguntas sobre caminhos de um grafo finito são decidíveis, e a análise troca umas pelas outras ao preço de considerar possíveis caminhos que nenhuma execução percorre. Diga em voz alta que essa imprecisão erra sempre para o mesmo lado — deixa de otimizar o que seria seguro e nunca otimiza o que não seria — e amarre com o argumento do módulo 1: perder oportunidade custa desempenho, aplicar transformação insegura custa correção, e as duas moedas não são conversíveis.
Bloco seguinte — a condição é o conteúdo
Este é o bloco central das duas aulas. Escreva no quadro a formulação operacional e não a suavize: uma otimização é uma transformação com condição de aplicabilidade, e a condição é o que a separa de um defeito.
Percorra as quatro transformações sempre no mesmo formato — o que faz, qual é a condição, qual caso de fronteira a quebra. No dobramento de constantes, a condição tem duas partes e a segunda é a esquecida: operandos literais e operação pura. Dê o caso da divisão por zero, que é o mais econômico: substituir t := c_1 / c_2 com c_2 = 0 por um valor troca um programa que falha por um programa que não falha, e falhar é comportamento, não acidente. Registre também o cuidado aritmético — quem dobra é a máquina que compila, quem executa é outra —, mesmo que na nossa linguagem a operação dobrada seja comparação entre inteiros.
Na propagação de cópias, seja honesto sobre o alvo: a nossa tradução nunca emite instrução de cópia, então a transformação não tem o que remover neste compilador. O mecanismo existe e está em uso, porque substituir leituras de um nome por leituras de outro é o que a eliminação de subexpressões faz. Dizer isso vale mais que fingir cobertura completa, e é uma das falas que os grupos repetem depois na apresentação.
Na eliminação de código morto, a conjunção da definição é o ponto: resultado não vivo e ausência de efeito colateral. Guarde o caso da emissão para o bloco de abertura da segunda aula — ele é a dívida que segura a atenção entre uma aula e outra. Anuncie apenas que existe uma transformação plausível que apaga a saída do programa inteiro sem que nenhuma verificação estrutural perceba, e pare aí.
Feche com o condutor e com o exemplo numérico resolvido. Explique por que uma passada de cada transformação é insuficiente: dobrar uma comparação entre constantes deixa sem leitor as duas instruções que carregavam os operandos, que passam a ser mortas, e remover essas pode deixar outras sem leitor. Projete o programa artificial do projeto de referência, rotulando-o como artificial na hora de mostrá-lo, e percorra o resultado: seis instruções antes, quatro depois, um dobramento, duas mortas removidas, ponto fixo em duas iterações. Aponte com o dedo o detalhe que a turma passa direto: o destino do desvio mudou de 6 para 4. Remover instrução renumera tudo o que vem depois, e esquecer de corrigir os alvos produz um objeto bem formado, que executa, e que salta para os lugares errados.
Bloco de abertura da segunda aula — a dívida da emissão
Não recapitule. Retome pela pendência: qual era a transformação plausível que apaga a saída? Projete os dois relatórios lado a lado — com guarda, doze instruções e nenhuma removida; sem guarda, onze instruções, uma removida, e uma emissão no programa antes contra zero depois.
Deixe a turma olhar antes de comentar, e então diga o que interessa: o programa quebrado compila, executa, não acusa erro nenhum e passa em qualquer verificação estrutural. Não há assimetria a detectar, não há erro de tipo, não há inconsistência interna. O único jeito de descobrir é executar e comparar o que saiu. É o argumento mais forte que você tem para a execução diferencial do fim da aula, e ele só funciona nesta ordem: primeiro o defeito indetectável, depois o método que o detecta.
Bloco seguinte — correta e mesmo assim inaplicável
Este é o resultado que vale o módulo, e ele precisa de tempo próprio. Enuncie a eliminação de subexpressões comuns com as três condições — operandos não redefinidos, expressão pura, e as duas ocorrências no mesmo bloco — e mostre por que a terceira é o que separa a versão local da global.
Então apresente o confronto com os números do artefato. No programa típico, três blocos e zero subexpressões eliminadas, porque as repetições do casamento caem em blocos diferentes e a transformação local não as alcança. No programa artificial, dois blocos e uma subexpressão eliminada, oito instruções viram sete — e a verificação de uso único do módulo 14 acusa violação: um temporário passaria a ser lido duas vezes.
Explique a incompatibilidade pela máquina, não pela regra: a eliminação de subexpressões existe para que um resultado sirva a vários leitores; a máquina de destino é de pilha; ler um temporário nela é desempilhá-lo; o segundo leitor encontraria a pilha vazia.
Segunda questão de discussão em duplas. “Uma transformação foi demonstrada semanticamente correta: para toda entrada, o programa transformado produz os mesmos efeitos observáveis, na mesma ordem. Segue-se que ela pode ser incluída no otimizador? (a) sim, correção semântica é exatamente o critério; (b) sim, desde que o ganho compense o custo de compilação; (c) não, ela também precisa respeitar as precondições das fases posteriores e da máquina de destino; (d) não, porque nenhuma transformação pode ser demonstrada correta.”
A resposta é (c). Quem vota (a) está com o critério que o próprio livro-texto usa nas primeiras seções e que este caso corrige — é a votação em que você quer ver a turma mudando de opinião entre o primeiro e o segundo voto. Quem vota (b) trocou aplicabilidade por conveniência, e vale nomear: custo é outra discussão, e vem no bloco seguinte. Feche com o hábito operacional que o caso ensina, e escreva-o no quadro: mantenha as invariantes ligadas. Quem descobriu a incompatibilidade foi uma verificação escrita um módulo antes, para outra finalidade, que continuou rodando — testes verificam o que o autor imaginou, invariantes verificam o que o sistema exige.
Bloco seguinte — vivacidade, ponto fixo e a terceira aparição
Aqui o tratamento é panorâmico por decisão do plano da disciplina, e panorâmico não quer dizer vago: o esquema tem de ficar completo, e é o esquema que se cobra.
Defina variável viva com atenção à quantificação — existe um caminho que lê antes de redefinir, basta um — e mostre que essa é a formulação conservadora de novo. Escreva as equações da vivacidade no quadro,
\mathrm{vivo_{ent}}(i) = \mathrm{usa}(i) \cup \bigl(\mathrm{vivo_{sai}}(i) \setminus \mathrm{def}(i)\bigr), \qquad \mathrm{vivo_{sai}}(i) = \bigcup_{s \in \mathrm{suc}(i)} \mathrm{vivo_{ent}}(s),
e leia a primeira em voz alta, palavra por palavra: está vivo na entrada tudo o que a instrução lê, mais o que estava vivo na saída e ela não redefiniu. Apresente então as duas escolhas que caracterizam qualquer análise da família — direção e operação de encontro — e a tabela curta que se cobra depois: vivacidade para trás com união, definições alcançáveis para a frente com união, expressões disponíveis para a frente com interseção.
Sobre a terminação, dê o teorema de Knaster e Tarski com a hipótese de altura finita e o argumento em três linhas: a sequência a partir do menor elemento é crescente por monotonicidade, uma sequência crescente num reticulado de altura finita estabiliza, e o valor onde estabiliza é ponto fixo por construção. Traduza para o caso concreto — reticulado é o conjunto das partes dos nomes, altura é o número de nomes, transferência é união e subtração de conjunto fixo — e então nomeie o padrão em voz alta: é a terceira vez no semestre. Anuláveis e produtivos no módulo 8; primeiros e seguidores no módulo 10; vivacidade agora. Peça que alguém enuncie a regra geral, e aceite a formulação da turma se estiver correta: quando a definição de um conjunto se refere a si mesma, itera-se até estabilizar.
Mostre o relatório de vivacidade do artefato e faça a leitura que explica a abertura: no máximo dois temporários vivos ao mesmo tempo, cada valor consumido logo depois de produzido, ponto fixo alcançado em duas rodadas. É a mesma quantidade que a coloração do grafo de interferência apontou no módulo 14, calculada por outro caminho — e medir a mesma grandeza por dois caminhos independentes é o segundo hábito que este módulo deixa.
Bloco seguinte — a conta que decide quanto otimizar
Bloco curto e aritmético. O custo de otimizar é pago uma vez por compilação; o ganho é colhido uma vez por execução; logo a decisão depende da razão entre execuções e compilações, e essa razão varia por ordens de grandeza. Um programa compilado uma vez e executado milhões de vezes justifica minutos de compilação; um programa recompilado a cada alteração e executado meia dúzia de vezes não justifica quase nada. É daí que saem os níveis de otimização dos compiladores de produção, e a escolha é de contexto de uso, não técnica.
Volte então ao número da abertura e pague a dívida: no nosso compilador o custo é real e pago sempre, e o ganho é zero no caso comum. A otimização está no programa porque a estrutura que ela exige é o conteúdo. Registre o custo que não é de tempo — código otimizado é mais difícil de depurar — e mencione, sem desenvolver, o problema da ordem das fases: aplicar A antes de B pode habilitar B, e a ordem em compiladores reais é calibragem empírica, não consequência de teorema.
Bloco seguinte — validação estática e o corpus que reprova
Percorra a cadeia inteira uma vez, com todas as fases à vista, e construa no quadro a tabela de interfaces preenchida — a mesma que ficou com lacunas no módulo 1. Chame a atenção para as duas linhas novas: a otimização, que este módulo acrescentou, e a validação, que não estava no plano original e apareceu por necessidade.
Desenvolva o balanço da pilha, que é a verificação mais bonita do semestre. Simulando apenas a altura, sem valores e sem executar nada, determina-se a altura em cada instrução. A exigência não é que a altura nunca fique negativa; é mais forte: a altura tem de ser propriedade do ponto, não do caminho. Faça o caso numérico no quadro — se um caminho chega à instrução com altura 1 e outro com altura 0, o objeto é malformado, ainda que nenhuma entrada exercite os dois. E explique por que essa varredura termina sem ponto fixo: todo desvio do nosso código salta para a frente, o fluxo é acíclico, e uma varredura em profundidade com marcação cobre tudo. Se houvesse laços, seria a maquinaria da seção de fluxo de dados — que é onde as duas metades do módulo se encontram.
Termine com a decisão que acompanha a validação e que os grupos tendem a achar exagerada: um objeto que não passa não é gravado, e o compilador sai com erro. Gravar um artefato sabidamente malformado é empurrar o problema para quem tem menos informação para diagnosticá-lo.
Sobre o corpus, seja específico, porque é o que separa a entrega boa da medíocre. Ele precisa das entradas degeneradas — vazia, sem casamento algum, só separadores —, dos casos de fronteira das condições — para “maior que cem”, os valores cem, noventa e nove e cento e um, que é o trio que distingue o maior do maior ou igual — e de entradas que exercitem os dois lados de cada desvio, sem o que o corpus não distingue uma otimização correta de uma que apagou um caminho inteiro.
Bloco de fechamento — a execução diferencial, e volta ao gancho
Feche com o método que responde à pergunta deixada em aberto na primeira aula: como saber que a otimização preservou a semântica? Não por argumento — argumento estabelece que a transformação é segura se implementada corretamente, e o que se quer verificar é a implementação. Compila-se o mesmo programa com e sem otimização, executam-se os dois objetos sobre o mesmo corpus, e exigem-se efeitos idênticos.
Insista na inclusão da posição na comparação, com a razão: comparar só os valores emitidos deixaria passar uma transformação que reordena emissões ou desloca o ponto de casamento, que são defeitos plausíveis e reais. Uma verificação vale exatamente o que ela é capaz de reprovar. E feche a honestidade do método: corpus finito é evidência, não prova; a equivalência observacional quantifica sobre todas as entradas. Dizer que é prova seria mais confortável e seria falso.
Volte então às três palavras do canto do quadro e à retrospectiva, nesta ordem e com números, não com sentimento. Estrutura: blocos e grafo, que tornaram formuláveis perguntas que a instrução isolada não responde. Condição: as quatro transformações, cada uma com a sua, e uma delas correta e inaplicável. Preservação: verificada por confronto de execuções.
Encerre com o quadro do percurso e com o dado do artefato: o objeto gerado tem 2816 células de transição e 11 instruções — a maior parte do arquivo, em bytes, são os autômatos. Diga a frase que fecha o semestre, e diga devagar: nesta disciplina a teoria de linguagens formais não é preâmbulo da construção de compiladores, ela é o produto. O autômato mínimo do módulo 5 está gravado no arquivo de saída, a diferença de autômatos do módulo 6 decidiu o sistema de tipos do módulo 12, e o casamento mais longo do módulo 7 é o laço principal do executor. Três resultados que, quando foram apresentados, pareciam exercícios de fronteira.
Aulas 3 a 6 — Tutoria do Projeto Integrador
Quatro aulas de tutoria — fechamento da integração, duas transformações locais com preservação demonstrada, revisão da documentação e preparação da apresentação.
Este é o módulo de andaime mínimo, e o contraste com o módulo 1 merece ser dito à turma em voz alta: lá você dava o critério, o modelo e o veredito; aqui você faz perguntas e quem responde é o grupo. Se o desvanecimento foi anunciado no começo do semestre, este é o momento de cobrar o que ele prometia — não conserte o projeto de ninguém nestas quatro aulas, pergunte até o grupo achar o próprio defeito.
Mantenha o projeto de referência aberto e projetado, mas use-o de outro modo agora. Não mostre a solução: mostre os relatórios. O grupo que vê o número zero publicado num relatório de otimização entende em um minuto que publicar ganho nulo é aceitável, e essa é a informação que mais muda comportamento nesta semana.
Primeira sessão de tutoria — a integração roda ponta a ponta
Abra com a ordem de prioridade escrita no quadro, e não negocie: primeiro o sistema compila, valida, grava e executa sobre entrada real; depois duas transformações locais com condição declarada; depois a apresentação; e só então qualquer refinamento. Grupos chegam ao último módulo com pendências acumuladas e a reação natural é atacar tudo ao mesmo tempo, o que produz um compilador que não roda e um otimizador que ninguém consegue demonstrar.
Faça o diagnóstico por grupo logo no início, e faça-o pela execução, não pela conversa. Peça que rodem o compilador na sua frente sobre três entradas: uma que funciona, a entrada vazia e uma que não casa com padrão algum. As duas últimas são onde um executor mal terminado trava ou acusa erro sem motivo, e é ali que quase todo grupo descobre uma pendência que jurava não ter. Anote no seu diário o que faltou em cada grupo, porque essa lista é a agenda da segunda sessão.
Enquanto circula, cobre as duas verificações baratas da decomposição em blocos, que pegam o erro mais comum da semana: a soma dos tamanhos dos blocos tem de bater com o número de instruções, e todo alvo de desvio tem de ser início de bloco. Um grupo cuja soma não fecha tem líderes faltando, e quase sempre é a terceira condição — a instrução seguinte a um desvio. Não conte a resposta: peça que listem os líderes em voz alta e parem no primeiro desvio.
O segundo problema que consome a sessão é a renumeração. Quando um grupo remover a primeira instrução morta e o programa começar a saltar para o lugar errado, resista a apontar a causa. Pergunte o que aconteceu com os índices das instruções depois da removida e espere. O grupo que descobre esse defeito sozinho não o repete; o grupo a quem você conta repete na semana seguinte, sob pressão da entrega.
Segunda sessão de tutoria — preservação demonstrada, documentação e apresentação
Comece exigindo a demonstração de preservação rodando, e não descrita. O critério é o mesmo do projeto de referência: mesmo programa compilado com e sem otimização, os dois objetos executados sobre o mesmo corpus, e comparação de rótulo, valor e posição de cada efeito. Um grupo que compara apenas os valores emitidos precisa ouvir o contraexemplo na hora — uma transformação que reordena as emissões passaria —, porque uma verificação vale exatamente o que ela é capaz de reprovar.
Confira também o corpus, que é onde o cuidado se manifesta e onde a complacência é fácil. Exija as entradas degeneradas, os valores de fronteira das condições que o grupo escreveu e entradas que exercitem os dois lados de cada desvio. Um corpus em que a condição é sempre verdadeira não distingue uma otimização correta de uma que apagou o caminho falso, e vale dizer isso com essa frase, porque ela é curta e cola.
A segunda metade da sessão é documentação e apresentação, e ela precisa de tempo protegido — é o item que os grupos deixam para o fim e conduzem mal. Na documentação, o problema típico não é ausência, é defasagem: há descrições do módulo 8 que não correspondem mais ao que o código faz. Peça que abram a documentação e o código lado a lado e confiram três pontos escolhidos por você, ao acaso, na frente deles.
Na apresentação, dê a estrutura em voz alta e insista em uma exigência que muda tudo: a demonstração roda ao vivo, sobre uma entrada que o grupo não escolheu previamente. Peça que o roteiro percorra o programa de exemplo, o objeto gerado, a execução, e um resultado medido — o número de instruções antes e depois, com a explicação do que ele significa. Repita, aqui e agora, que ganho nulo é resultado publicável, porque é neste momento da semana que os grupos ficam tentados a fabricar um exemplo que renda.
O fechamento, a avaliação por pares e a entrega
Reserve a parte final da última sessão para a segunda avaliação por pares, preenchida individualmente e em silêncio, e não a deixe para casa — preenchida em grupo ela vira consenso educado e perde toda a função. Compare-a com a primeira: a informação útil não é a nota, é a mudança de padrão de um integrante entre as duas.
Feche o semestre com cada grupo dizendo, em uma frase, qual defeito mais sério apareceu no projeto deles e como ele apareceu. Você vai ouvir alguma versão do mesmo padrão que o projeto de referência registrou três vezes: o defeito não apareceu por revisão nem por teste, apareceu quando uma peça passou a ser usada de verdade, ou quando a mesma grandeza foi calculada por dois caminhos que discordaram, ou porque uma verificação escrita para outro fim continuou ligada. Nomeie o padrão para a turma, porque é o que sobrevive à obsolescência de qualquer técnica que o semestre ensinou.
A entrega ocorre na semana seguinte ao término deste módulo. Deixe explícito o que ela contém e o que você fará com ela, para que nenhum grupo descubra na hora que faltava o diário ou a avaliação por pares.
Entregáveis e Avaliação
A entrega deste módulo é a entrega final do Projeto Integrador, e ela ocorre na semana seguinte ao término do módulo. Cada grupo entrega o compilador completo funcionando ponta a ponta, do texto de entrada ao efeito observável; blocos básicos identificados e grafo de fluxo construído sobre a própria representação intermediária; ao menos duas transformações locais implementadas, com a condição de segurança de cada uma declarada e a preservação demonstrada por confronto de execuções; a documentação integral revisada e coerente com o que o código faz hoje; o diário de atividades completo; a segunda avaliação por pares preenchida por cada integrante; e a apresentação.
Confira cada entrega na mesma ordem para todos os grupos, e comece pelo que mais reprova. Rode o compilador sobre o corpus do próprio grupo e sobre duas entradas que você preparou sem avisar, incluindo a vazia. Peça a demonstração da execução diferencial rodando na sua frente, não o relatório dela. Verifique que a soma dos tamanhos dos blocos bate com o número de instruções. Peça que apontem, no código, a linha em que a condição de segurança de cada transformação é verificada — a condição que não está escrita em lugar nenhum é a definição de um defeito, e essa é a pergunta que separa quem entendeu o módulo de quem implementou por imitação. Confira, por fim, que a compilação sai limpa no modo estrito e que as demonstrações dos módulos anteriores continuam passando.
Diga à turma, antes da entrega e por escrito, que ganho nulo não reprova. Um grupo que publica zero instruções removidas num programa típico e explica por que — representação intermediária sem redundância — recebe nota melhor que um grupo que apresenta ganhos espetaculares medidos sobre um exemplo escolhido a dedo. Um compilador completo com duas transformações simples vale mais que um compilador incompleto com otimização sofisticada, e esse critério precisa estar publicado antes, não descoberto na correção.
Orientações Sobre o Aplicativo
Use o aplicativo para as duas votações das aulas teóricas, com resultado anônimo na projeção. A segunda votação é a mais informativa do semestre inteiro: ela pergunta se correção semântica basta para uma transformação ser aplicável, e a turma quase toda vota que sim no primeiro voto, porque é o que o material diz até aquele ponto. A distância entre o primeiro e o segundo voto mede exatamente o efeito do caso das subexpressões comuns. Se a turma não mudar de opinião entre os dois votos, o caso não foi apresentado com clareza suficiente e vale refazê-lo com o relatório de violação projetado.
Compare, ao fim do módulo, os histogramas deste com os do módulo 1: as votações de abertura e de fechamento do semestre tratam do mesmo contrato — preservar significado ao transformar um programa —, e o par é o dado mais defensável que você terá sobre o percurso da turma.
Acompanhe também o engajamento no estudo do material, que responde por metade do componente contínuo, sabendo que ele cai neste módulo em quase toda turma — a atenção migra para a entrega e para a apresentação. Não leia essa queda como desinteresse sem cruzar com a atividade dos grupos na tutoria.
Pontos de Atenção Específicos
O módulo vira encerramento se você deixar. É o último, a turma está cansada e a retrospectiva é agradável de conduzir. O antídoto é operacional: a retrospectiva tem lugar marcado no bloco de fechamento, com números do artefato, e não se antecipa. Se ela começar a vazar para os blocos do meio, corte e siga.
Otimização é lida como esperteza. É o preconceito nomeado no plano da disciplina, e ele se desfaz com um caso, não com uma advertência. O caso é a emissão removida por um verificador impecavelmente coerente que só pergunta se o resultado é lido. Mostre o programa que compila, executa, não acusa erro e não produz saída alguma — e só depois enuncie a regra.
O caso das subexpressões comuns exige preparação. É o bloco em que você mais corre risco de se enrolar, porque envolve três coisas ao mesmo tempo: a transformação, a máquina de destino e uma invariante escrita dois módulos antes. Ensaie a explicação em três frases — um resultado para vários leitores, ler é desempilhar, o segundo leitor acha a pilha vazia — e só então mostre o relatório de violação.
Alguém vai pedir para implementar a análise global. Costuma ser o grupo mais adiantado, e o pedido é legítimo e sedutor. Recuse com o critério do plano: o tratamento da análise de fluxo de dados é panorâmico, o tempo de tutoria que sobra é do fechamento da integração, e um grupo que troca a integração pela otimização global entrega um compilador que não roda ponta a ponta. Ofereça a análise global como o primeiro item da seção de trabalhos futuros da apresentação.
A armadilha da renumeração aparece em quase todo grupo. Remover instrução renumera as seguintes e invalida os alvos de desvio posteriores ao ponto removido. O defeito não quebra nada visivelmente: o objeto continua bem formado e salta para o lugar errado. Antecipe as duas defesas na aula teórica — corrigir os alvos como parte da remoção, ou usar rótulos simbólicos resolvidos só na emissão — e cobre uma das duas explicitamente na tutoria.
Cuidado com a digressão sobre compiladores de produção. A pergunta “e como o compilador que a gente usa faz isso” aparece sempre no bloco do compromisso. Responda pela conta — custo por compilação contra ganho por execução, e níveis de otimização como consequência — e reconduza: comparar produtos concretos consome o bloco da validação estática, que é conteúdo.
O acúmulo é o risco da tutoria, não da aula. Grupos chegam ao último módulo com pendências dos módulos anteriores e tentam fazer tudo de uma vez. A ordem de prioridade é fixa e precisa ser dita em voz alta na primeira sessão: primeiro o sistema roda ponta a ponta, depois duas transformações com condição declarada, depois a apresentação, e só então qualquer refinamento.