






#define Size 819000
int sieve ( int N ) {
int64_t i , k , prime , count , n ; char flags [ Size ];
for ( n = 0 ; n < N ; n ++ ) {
count = 0 ;
for ( i = 0 ; i < Size ; i ++ )
flags [ i ] = 1 ;
for ( i = 0 ; i < Size ; i ++ )
if ( flags [ i ]) {
prime = i + i + 3 ;
for ( k = i + prime ; k < Size ; k += prime )
flags [ k ] = 0 ;
count ++ ;
}
}
return count ;
}
void ex100 ( void ) {
printf ("sieve (100) = %d", sieve (100));
} m_sieve : module
export sieve
sieve : func i32, i32:N
local i64:iter, i64:count, i64:i, i64:k, i64:prime, i64:temp, i64:flags
alloca flags, 819000
mov iter, 0
loop : bge fin, iter, N
mov count, 0; mov i, 0
loop2 : bge fin2, i, 819000
mov u8:(flags, i), 1; add i, i, 1
jmp loop2
fin2 : mov i, 0
loop3 : bge fin3, i, 819000
beq cont3, u8:(flags,i), 0
add temp, i, i; add prime, temp, 3; add k, i, prime
loop4 : bge fin4, k, 819000
mov u8:(flags, k), 0; add k, k, prime
jmp loop4
fin4 : add count, count, 1
cont3 : add i, i, 1
jmp loop3
fin3 : add iter, iter, 1
jmp loop
fin : ret count
endfunc
endmodule
m_ex100 : module
format : string "sieve (10) = %dn"
p_printf : proto p:fmt, i32:result
p_sieve : proto i32, i32:iter
export ex100
import sieve, printf
ex100 : func v, 0
local i64:r
call p_sieve, sieve, r, 100
call p_printf, printf, format, r
endfunc
endmodulefunc descreve a assinatura da função (pegando o argumento inteiro com sinal de 32 bits e retornando o valor inteiro com sinal de 32 bits) e o argumento da função N , que será uma variável local do tipo inteiro com sinal de 64 bits;string descreve dados na forma de string Cexport descreve as funções ou dados do módulo que são visíveis fora do módulo atualimport descreve as funções ou dados do módulo que devem ser definidos em outros módulos MIRproto descreve protótipos de funções. Sua sintaxe é igual à sintaxe funccall são instruções MIR para chamar funções MIR_load_externalm1 e m2 são os módulos m_sieve e m_e100 , func é a função ex100 , sieve é a função sieve ): /* ctx is a context created by MIR_init / MIR_init2 */
MIR_load_module ( ctx , m1 ); MIR_load_module ( ctx , m2 );
MIR_load_external ( ctx , "printf" , printf );
MIR_link ( ctx , MIR_set_interp_interface , import_resolver );
/* or use MIR_set_gen_interface to generate and use the machine code */
/* or use MIR_set_lazy_gen_interface to generate function code on its 1st call */
/* use MIR_gen (ctx, func) to explicitly generate the function machine code */
MIR_interp ( ctx , func , & result , 0 ); /* zero here is arguments number */
/* or ((void (*) (void)) func->addr) (); to call interpr. or gen. code through the interface */binfmt_misc O binário mir-bin-run está preparado para ser usado a partir de binfmt_misc com a seguinte linha (exemplo):
line=:mir:M::MIR::/usr/local/bin/mir-bin-run:P
echo $line > /proc/sys/fs/binfmt_misc/registerAdapte o caminho binário mir-bin-run ao seu sistema, que é o padrão
E correr com
c2m your-file.c -o your-file
chmod +x your-file
./your-file your argsO executável é "configurável" com variáveis de ambiente:
MIR_TYPE define a interface para execução de código: interp (para interpretação), jit (para geração) e lazy (para geração lenta, padrão);MIR_LIBS (lista separada por dois pontos) define uma lista de bibliotecas extras para carregar;MIR_LIB_DIRS ou LD_LIBRARY_PATH (lista separada por dois pontos) define uma lista extra de diretórios para pesquisar as bibliotecas.Devido à natureza vinculada de
mir-bin-runcombinfmt_misc, pode ser um pouco estranho chamarmir-bin-rundiretamente. O sinalizadorPno binfmt_misc passa um argumento extra com o caminho completo para o binário MIR.
Pipeline de otimização muito curto para velocidade e leveza
Apenas o uso de otimização mais valioso :
Diferentes níveis de otimização para ajustar a velocidade de compilação versus o desempenho do código gerado
A forma SSA do MIR é usada antes da alocação de registro
Simplicidade de implementação de otimizações em relação ao desempenho extremo do código gerado
Mais detalhes sobre o pipeline completo do compilador JIT :
Simplifique : diminuindo o MIR
Inline : chamadas MIR embutidas
Construir CFG : construindo Gráfico de Fluxo de Controle (blocos básicos e bordas CFG)
Construir SSA : Construindo Formulário de Atribuição Estática Única adicionando nós phi e bordas SSA aos operandos
Transformação de endereço : remova ou altere instruções MIR ADDR
Numeração de valor global : removendo insns redundantes através do GVN. Isso inclui propagação constante e eliminações de carga redundantes
Propagação de cópia : propagação de cópia SSA e remoção de instruções de extensão redundantes
Eliminação de lojas mortas : removendo lojas redundantes
Eliminação de código morto : removendo insns com saídas não utilizadas
Alívio de pressão : movendo insns para diminuir a pressão do registro
Combinação SSA : combinação de endereços e pares de instruções de comparação e ramificação
Fora do SSA : Removendo nós phi e bordas SSA
Opções de salto : diferentes otimizações de salto
Machinize : execute código dependente da máquina transformando MIR para chamadas ABI, 2-op insns, etc.
Find Loops : encontrando loops naturais e construindo árvore de loops
Build Live Info : calculando live in e live out para os blocos básicos
Construir Conflitos de Cadastro : construção de matriz de conflitos para cadastros envolvidos em movimentações. É usado para coalescência de registros
Coalesce : coalescência de registro agressivo
Alocador de registro (RA) : RA de varredura linear baseada em prioridade com divisão de faixa ao vivo
Construir Live Ranges : calculando intervalos de pontos do programa para registros
Atribuir : RA rápido para -O0 ou RA de varredura linear baseada em prioridade para -O1 e superior
Reescrever : transforme o MIR de acordo com a atribuição usando registros rígidos reservados
Combinar (seleção de código): mesclando insns dependentes de dados em um
Eliminação de código morto : removendo insns com saídas não utilizadas
Gerar Insns de Máquina : executa código dependente de máquina criando insns de máquina
c2m . Veja README.mdmir.h e mir.c contêm o principal código API, incluindo entrada/saída de binário MIR e representação de texto MIRmir-dlist.h , mir-mp.h , mir-varr.h , mir-bitmap.h , mir-hash.h , mir-htab.h , mir-reduce.h contêm código genérico correspondentemente para link duplo listas, conjuntos de memória, matrizes de comprimento variável, bitmaps, cálculos de hash, tabelas de hash e compactação/descompactação de dados. O arquivo mir-hash.h é uma função hash geral, simples e de alta qualidade usada por hashtablesmir-interp.c contém código para interpretação do código MIR. Está incluído no mir.c e nunca é compilado separadamentemir-gen.h , mir-gen.c , mir-gen-x86_64.c , mir-gen-aarch64.c , mir-gen-ppc64.c , mir-gen-s390x.c e mir-gen-riscv64.c contém código para o compilador MIR JITmir-gen-x86_64.c , mir-gen-aarch64.c , mir-gen-ppc64.c , mir-gen-s390x.c e mir-gen-riscv64.c são códigos dependentes da máquina do compilador JITmir-<target>.c contêm código simples dependente de máquina, comum para interpretador e compilador JITmir-<target>.h contêm declarações comuns para interpretador e compilador JITmir2c/mir2c.h e mir2c/mir2c.c contêm código para o compilador MIR para C. O código gerado pode não ser portátilc2mir/c2mir.h , c2mir/c2mir.c , c2mir/c2mir-driver.c e c2mir/mirc.h contêm código para o compilador C para MIR. Arquivos nos diretórios c2mir/x86_64 e c2mir/aarch64 , c2mir/ppc64 , c2mir/s390x e c2mir/riscv64 contêm correspondentemente x86_64, aarch64, ppc64le, s390x e riscv código dependente de máquina para compilador C para MIRmir-bin-run.c contém o código para mir-bin-run descrito acimamir-bin-driver.c com o utilitário b2ctab pode ser usado como forma portátil de gerar binário a partir de arquivos binários MIRmir-utils contém diferentes utilitários para trabalhar com MIR, por exemplo, transformar MIR binário em MIR textual e vice-versaadt-tests , mir-tests , c-tests e c-benchmarks contém código para teste e benchmarking MIR e c2m make bench e make test Intel i5-13600K com 64 GB de memória sob FC37 com GCC-12.3.1
| Gerador MIR | Intérprete MIR | gcc -O2 | gcc -O0 | |
|---|---|---|---|---|
| compilação [1] | 1,0 (249us) | 0,09 (22us) | 109 (27,1ms) | 105 (26,1ms) |
| execução [2] | 1,0 (1,74s) | 13,7 (23,8s) | 0,92 (1,6s) | 2,28 (3,97s) |
| tamanho do código [3] | 1,0 (557 KB) | 0,43 (240 KB) | 58 (32,2 MB) | 58 (32,2 MB) |
| LOC [4] | 1,0 (23,4K) | 0,48 (11,3K) | 103 (2420K) | 103 (2402K) |
[1] é baseado no tempo de compilação do código de peneira C (sem qualquer arquivo incluído e com o uso do sistema de arquivos de memória para GCC) e o código de peneira MIR correspondente pelo interpretador MIR e gerador MIR com nível de otimização 2
[2] é baseado no melhor tempo de parede de 10 execuções com nível de otimização 2 do gerador MIR usado
[3] é baseado em tamanhos reduzidos de cc1 para núcleo GCC e MIR e intérprete ou gerador para MIR
[4] minha estimativa baseada apenas em arquivos necessários para o compilador x86-64 GNU C e arquivos MIR para um programa mínimo para criar e executar código MIR
Intel i5-13600K com 64 GB de memória sob FC37 com GCC-12.3.1
| c2m -O2 -por exemplo (gerador) | c2m -ei (intérprete) | gcc -O2 | gcc -O0 | |
|---|---|---|---|---|
| compilação [1] | 1,0 (336us) | 1,0 (337us) | 80 (27,1ms) | 77 (26,1ms) |
| execução [2] | 1,0 (1,74s) | 13,7 (23,8s) | 0,92 (1,6s) | 2,28 (3,97s) |
| tamanho do código [3] | 1,0 (961 KB) | 1,0 (961 KB) | 34 (32,2 MB) | 34 (32,2 MB) |
| LOC [4] | 1,0 (54,8 K) | 1,0 (54,8 K) | 44 (2420K) | 44 (2420K) |
[1] é baseado no tempo de compilação do código C Sieve (sem qualquer arquivo incluído e com o uso do sistema de arquivos de memória para GCC)
[2] é baseado no melhor tempo de parede de 10 execuções com nível de otimização 2 do gerador MIR usado
[3] é baseado em tamanhos reduzidos de cc1 para GCC e C2MIR, núcleo MIR, intérprete e gerador para MIR
[4] é baseado em todos os arquivos de origem, excluindo testes
Aqui é gerado o desempenho do código relacionado ao GCC -O2 para diferentes compiladores C em 15 benchmarks C pequenos (do diretório c-benchmarks ) na mesma máquina onde
| Média | Geomeia | |
|---|---|---|
| gcc -O2 | 1,00 | 1,00 |
| gcc -O0 | 0,63 | 0,57 |
| c2m -por exemplo | 0,96 | 0,91 |
| c2m-eb | 0,92 | 0,85 |
| chibicc | 0,38 | 0h30 |
| clang -O2 | 1.12 | 1.09 |
| analisador -O3 | 1.02 | 0,98 |
| cproc | 0,68 | 0,65 |
| lacc -O3 | 0,47 | 0,39 |
| pcc-O | 0,80 | 0,78 |
| tcc | 0,54 | 0,50 |
| emcc -O2/lavador | 0,60 | 0,55 |
| wasi -O2/wasmer guindaste | 0,60 | 0,54 |
| wasi -O2/wasmer LLVM | 0,78 | 0,72 |
| wasi -O2/wasmer singlepass | 0,45 | 0,36 |
| wasi -O2/wasmtime | 0,92 | 0,87 |
c2m ) para outro destino por 1-2 meses