#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
endmodule
func
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 func
call
são instruções MIR para chamar funções MIR_load_external
m1
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/register
Adapte 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 args
O 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-run
combinfmt_misc
, pode ser um pouco estranho chamarmir-bin-run
diretamente. O sinalizadorP
no 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-.c
contêm código simples dependente de máquina, comum para interpretador e compilador JITmir-.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