#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
describe la firma de la función (tomando un argumento entero con signo de 32 bits y devolviendo un valor entero con signo de 32 bits) y el argumento de la función N
, que será una variable local de tipo entero con signo de 64 bits.;
string
describe datos en forma de cadena Cexport
describe las funciones o datos del módulo que son visibles fuera del módulo actualimport
describe las funciones o datos del módulo que deben definirse en otros módulos MIRproto
describe prototipos de funciones. Su sintaxis es la misma que la sintaxis func
.call
son instrucciones MIR para llamar funciones MIR_load_external
m1
y m2
son módulos m_sieve
y m_e100
, func
es la función ex100
, sieve
es la función 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
El binario mir-bin-run
está preparado para usarse desde binfmt_misc
con la siguiente línea (ejemplo):
line=:mir:M::MIR::/usr/local/bin/mir-bin-run:P
echo $line > /proc/sys/fs/binfmt_misc/register
Adapte la ruta binaria mir-bin-run a su sistema, que es la predeterminada
y correr con
c2m your-file.c -o your-file
chmod +x your-file
./your-file your args
El ejecutable es "configurable" con variables de entorno:
MIR_TYPE
establece la interfaz para la ejecución de código: interp
(para interpretación), jit
(para generación) y lazy
(para generación diferida, predeterminado);MIR_LIBS
(lista separada por dos puntos) define una lista de bibliotecas adicionales para cargar;MIR_LIB_DIRS
o LD_LIBRARY_PATH
(lista separada por dos puntos) define una lista adicional de directorios para buscar bibliotecas.Debido a la naturaleza vinculada de
mir-bin-run
conbinfmt_misc
, puede resultar un poco extraño llamarmir-bin-run
directamente. El indicadorP
en binfmt_misc pasa un argumento adicional con la ruta completa al binario MIR.
Proceso de optimización muy corto para velocidad y peso ligero
Solo el uso de optimización más valioso :
Diferentes niveles de optimización para ajustar la velocidad de compilación frente al rendimiento del código generado
La forma SSA de MIR se utiliza antes de la asignación de registros
Simplicidad de implementación de optimizaciones sobre el rendimiento extremo del código generado
Más detalles sobre la canalización completa del compilador JIT :
Simplificar : bajar MIR
En línea : incluir llamadas MIR en línea
Build CFG : creación de un gráfico de flujo de control (bloques básicos y bordes de CFG)
Construya SSA : cree un formulario de asignación estática único agregando nodos phi y bordes SSA a los operandos
Transformación de dirección : eliminar o cambiar las instrucciones MIR ADDR
Numeración de valores globales : eliminación de insns redundantes a través de GVN. Esto incluye propagación constante y eliminaciones de carga redundantes.
Propagación de copia : propagación de copia SSA y eliminación de instrucciones de extensión redundantes
Eliminación de tiendas muertas : eliminación de tiendas redundantes
Eliminación de código muerto : eliminación de insns con salidas no utilizadas
Alivio de presión : mover INSNS para disminuir la presión de registro.
Combinación SSA : combinación de direcciones y pares de instrucciones de comparación y bifurcación
Fuera de SSA : eliminación de nodos phi y bordes de SSA
Opciones de salto : diferentes optimizaciones de salto
Machinize : ejecuta código dependiente de la máquina transformando MIR para llamadas ABI, insns de 2 operaciones, etc.
Buscar bucles : encontrar bucles naturales y construir un árbol de bucles
Build Live Info : cálculo de la entrada y salida de los bloques básicos
Construir conflictos de registros : creación de una matriz de conflictos para los registros involucrados en movimientos. Se utiliza para la fusión de registros.
Coalesce : registro agresivo que se fusiona
Asignador de registros (RA) : escaneo lineal basado en prioridades RA con división de rango en vivo
Build Live Ranges : cálculo de rangos de puntos del programa para registros
Asignar : RA rápido para -O0
o RA de escaneo lineal basado en prioridad para -O1
y superior
Reescribir : transformar MIR de acuerdo con la asignación utilizando registros rígidos reservados
Combinar (selección de código): fusionar insns dependientes de datos en uno
Eliminación de código muerto : eliminación de insns con salidas no utilizadas
Generar Machine Insns : ejecuta código dependiente de la máquina creando insns de máquina.
c2m
. Ver README.mdmir.h
y mir.c
contienen código API importante que incluye entrada/salida de binario MIR y representación de texto MIR.mir-dlist.h
, mir-mp.h
, mir-varr.h
, mir-bitmap.h
, mir-hash.h
, mir-htab.h
, mir-reduce.h
contienen el código genérico correspondiente para doble enlace. listas, grupos de memoria, matrices de longitud variable, mapas de bits, cálculos hash, tablas hash y compresión/descompresión de datos. El archivo mir-hash.h
es una función hash general, simple y de alta calidad utilizada por las tablas hash.mir-interp.c
contiene código para la interpretación del código MIR. Está incluido en mir.c
y nunca se compila por separado.mir-gen.h
, mir-gen.c
, mir-gen-x86_64.c
, mir-gen-aarch64.c
, mir-gen-ppc64.c
, mir-gen-s390x.c
y mir-gen-riscv64.c
contiene código para el compilador MIR JITmir-gen-x86_64.c
, mir-gen-aarch64.c
, mir-gen-ppc64.c
, mir-gen-s390x.c
y mir-gen-riscv64.c
son códigos dependientes de la máquina del compilador JIT.mir-.c
contienen código simple dependiente de la máquina común para el intérprete y el compilador JIT.mir-.h
contienen declaraciones comunes para el intérprete y el compilador JITmir2c/mir2c.h
y mir2c/mir2c.c
contienen código para el compilador MIR a C. Es posible que el código generado no sea portátil.c2mir/c2mir.h
, c2mir/c2mir.c
, c2mir/c2mir-driver.c
y c2mir/mirc.h
contienen código para el compilador de C a MIR. Los archivos en los directorios c2mir/x86_64
y c2mir/aarch64
, c2mir/ppc64
, c2mir/s390x
y c2mir/riscv64
contienen correspondientemente código x86_64, aarch64, ppc64le, s390x y riscv dependiente de la máquina para el compilador de C a MIR.mir-bin-run.c
contiene el código para mir-bin-run
descrito anteriormentemir-bin-driver.c
con la utilidad b2ctab
se puede utilizar de forma portátil para generar archivos binarios a partir de archivos binarios MIR.mir-utils
contiene diferentes utilidades para trabajar con MIR, por ejemplo, transformar MIR binario a MIR textual y viceversa.adt-tests
, mir-tests
, c-tests
y c-benchmarks
contiene código para probar y comparar MIR y c2m
make bench
y make test
Intel i5-13600K con 64 GB de memoria bajo FC37 con GCC-12.3.1
generador MIR | intérprete MIR | gcc -O2 | gcc -O0 | |
---|---|---|---|---|
compilación [1] | 1.0 (249us) | 0,09 (22us) | 109 (27,1 ms) | 105 (26,1 ms) |
ejecución [2] | 1,0 (1,74 s) | 13,7 (23,8s) | 0,92 (1,6s) | 2,28 (3,97s) |
tamaño del 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] se basa en el tiempo de compilación del código de tamiz C (sin ningún archivo de inclusión y con el uso del sistema de archivos de memoria para GCC) y el código de tamiz MIR correspondiente mediante el intérprete MIR y el generador MIR con nivel de optimización 2.
[2] se basa en el mejor tiempo de pared de 10 ejecuciones con el nivel 2 de optimización del generador MIR usado
[3] se basa en tamaños despojados de cc1 para GCC y núcleo MIR e intérprete o generador para MIR
[4] mi estimación se basa únicamente en los archivos necesarios para el compilador GNU C x86-64 y los archivos MIR para un programa mínimo para crear y ejecutar código MIR
Intel i5-13600K con 64 GB de memoria bajo FC37 con GCC-12.3.1
c2m -O2 -eg (generador) | c2m -ei (intérprete) | gcc -O2 | gcc -O0 | |
---|---|---|---|---|
compilación [1] | 1.0 (336us) | 1.0 (337us) | 80 (27,1 ms) | 77 (26,1 ms) |
ejecución [2] | 1,0 (1,74 s) | 13,7 (23,8s) | 0,92 (1,6s) | 2,28 (3,97s) |
tamaño del código [3] | 1.0 (961 KB) | 1.0 (961 KB) | 34 (32,2 MB) | 34 (32,2 MB) |
LOC [4] | 1.0 (54.8K) | 1.0 (54.8K) | 44 (2420K) | 44 (2420K) |
[1] se basa en el tiempo de compilación del código de tamiz C (sin ningún archivo de inclusión y utilizando el sistema de archivos de memoria para GCC)
[2] se basa en el mejor tiempo de pared de 10 ejecuciones con el nivel 2 de optimización del generador MIR usado
[3] se basa en tamaños eliminados de cc1 para GCC y C2MIR, núcleo MIR, intérprete y generador para MIR
[4] se basa en todos los archivos fuente, excluyendo las pruebas
Aquí se genera el rendimiento del código relacionado con GCC -O2 para diferentes compiladores de C en 15 puntos de referencia de C pequeños (del directorio c-benchmarks
) en la misma máquina donde
Promedio | Geomedia | |
---|---|---|
gcc -O2 | 1.00 | 1.00 |
gcc -O0 | 0,63 | 0,57 |
c2m -eg | 0,96 | 0,91 |
c2m-eb | 0,92 | 0,85 |
chibicc | 0,38 | 0,30 |
sonido metálico -O2 | 1.12 | 1.09 |
analizador -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/agua | 0,60 | 0,55 |
grúa elevadora wasi -O2/wasmer | 0,60 | 0,54 |
wasi -O2/wasmer LLVM | 0,78 | 0,72 |
wasi -O2/wasmer de paso único | 0,45 | 0,36 |
wasi -O2/wasmtime | 0,92 | 0,87 |
c2m
) a otro objetivo durante 1 o 2 meses.