Vejo vários problemas em potencial com essas seções críticas. Existem advertências e soluções para tudo isso, mas como um resumo:
- Não há nada que impeça o compilador de mover o código por essas macros, para otimização ou outros motivos aleatórios.
- Eles salvam e restauram algumas partes do estado do processador que o compilador espera que o assembly embutido deixe em paz (a menos que seja dito o contrário).
- Não há nada impedindo que uma interrupção ocorra no meio da sequência e alterando o estado entre a leitura e a gravação.
Primeiro, você definitivamente precisa de algumas barreiras de memória do compilador . O GCC os implementa como clobbers . Basicamente, essa é uma maneira de informar ao compilador "Não, não é possível mover os acessos à memória por essa parte do assembly embutido, pois isso pode afetar o resultado dos acessos à memória". Especificamente, você precisa dos dois "memory"
e dos "cc"
clobbers, nas macros inicial e final. Isso impedirá que outras coisas (como chamadas de função) também sejam reordenadas em relação ao assembly embutido, porque o compilador sabe que pode ter acesso à memória. Eu vi o GCC para ARM manter o estado nos registros de código de condição no conjunto em linha com os "memory"
clobbers, então você definitivamente precisa do"cc"
clobber.
Em segundo lugar, essas seções críticas estão economizando e restaurando muito mais do que apenas se as interrupções estão ativadas. Especificamente, eles estão salvando e restaurando a maior parte do CPSR (Registro atual do status do programa) (o link é para o Cortex-R4 porque não consegui encontrar um bom diagrama para um A9, mas deve ser idêntico). Existem restrições sutis torno das quais partes do estado podem realmente ser modificadas, mas é mais do que necessário aqui.
Entre outras coisas, isso inclui os códigos de condição (onde os resultados de instruções como cmp
são armazenados para que instruções condicionais subsequentes possam atuar no resultado). O compilador definitivamente ficará confuso com isso. Isso é facilmente solucionável usando o"cc"
triturador como mencionado acima. No entanto, isso fará com que o código falhe sempre, portanto, não parece com o que você está tendo problemas. Um pouco de uma bomba-relógio, porém, nessa modificação aleatória de outro código pode fazer com que o compilador faça algo um pouco diferente que será quebrado por isso.
Isso também tentará salvar / restaurar os bits de TI, que são usados para implementar a execução condicional do Thumb . Observe que, se você nunca executar o código Thumb, isso não importa. Eu nunca descobri como o assembly embutido do GCC lida com os bits de TI, além de concluir que não, o que significa que o compilador nunca deve colocar o assembly embutido em um bloco de TI e sempre espera que o assembly termine fora de um bloco de TI. Eu nunca vi o GCC gerar código violando essas suposições e fiz um assembly interno bastante intrincado com otimização pesada, por isso tenho certeza de que eles são válidos. Isso significa que provavelmente não tentará alterar os bits de TI; nesse caso, está tudo bem. A tentativa de modificar esses bits é classificada como "arquitetonicamente imprevisível", para que ele possa fazer todos os tipos de coisas ruins, mas provavelmente não fará nada.
A última categoria de bits que serão salvos / restaurados (além dos que realmente desativam interrupções) são os bits de modo. Provavelmente, isso não muda, portanto, provavelmente não importa, mas se você tiver algum código que mude deliberadamente os modos, essas seções de interrupção podem causar problemas. Mudar entre o modo privilegiado e o usuário é o único caso que eu esperaria.
Terceiro, nada impede que uma interrupção altere outras partes do CPSR entre o MRS
e o MSR
in ARM_INT_LOCK
. Quaisquer alterações podem ser substituídas. Na maioria dos sistemas razoáveis, as interrupções assíncronas não alteram o estado do código em que são interrompidas (incluindo o CPSR). Se o fizerem, fica muito difícil argumentar sobre o que o código fará. No entanto, é possível (a alteração do bit de desativação do FIQ me parece mais provável), portanto, você deve considerar se o seu sistema faz isso.
Aqui está como eu os implementaria de maneira a abordar todos os possíveis problemas que eu apontei:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
Certifique-se de compilar, -mcpu=cortex-a9
porque pelo menos algumas versões do GCC (como a minha) são padronizadas para uma CPU ARM mais antiga que não suporta cpsie
e cpsid
.
Eu usei em ands
vez de apenas and
no, ARM_INT_LOCK
por isso é uma instrução de 16 bits se for usada no código Thumb. De "cc"
qualquer forma, o clobber é necessário, por isso é estritamente um benefício de desempenho / tamanho do código.
0
e 1
são etiquetas locais , para referência.
Eles devem ser utilizados da mesma maneira que suas versões. O ARM_INT_LOCK
é tão rápido / pequeno quanto o original. Infelizmente, não consegui encontrar uma maneira de ARM_INT_UNLOCK
agir com segurança em qualquer lugar com poucas instruções.
Se o seu sistema tiver restrições quando IRQs e FIQs estiverem desativados, isso poderá ser simplificado. Por exemplo, se eles estiverem sempre desativados juntos, você poderá combinar um cbz
+ cpsie if
como este:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
Como alternativa, se você não se importa com FIQs, é semelhante a simplesmente deixar de ativar / desativar completamente.
Se você souber que nada mais altera nenhum dos outros bits de estado no CPSR entre o bloqueio e o desbloqueio, também poderá usar continue com algo muito semelhante ao seu código original, exceto com ambos "memory"
e "cc"
clobbers nos dois ARM_INT_LOCK
eARM_INT_UNLOCK