Este projeto é uma implementação de um programa de multiplicação de matrizes com foco em programação de baixo nível, combinando a linguagem C para a lógica principal e RISC-V Assembly para o núcleo computacional.
O programa evita o uso de funções de I/O da biblioteca padrão C (como printf e scanf), optando por interagir diretamente com o kernel do sistema operacional através de chamadas de sistema (syscalls) para read e write.
Arquivos
O código foi estruturado de forma modular para separar as diferentes responsabilidades do programa.
main.c: Orquestra o fluxo principal do programa: recebe as dimensões, aloca memória, chama as funções de leitura, multiplicação e impressão.
io.c / io.h: Implementa uma camada de I/O de baixo nível. Contém as wrappers para as syscalls read e write e lógica para ler e processar números inteiros do stdin.
matmul.s: Contém a implementação do algoritmo de multiplicação de matrizes em Assembly RISC-V.
print_matrix.c / matrix.h: Fornece a funcionalidade de "pretty printing" das matrizes, garantindo que a saída seja alinhada e legível.
text.c / text.h: Centraliza todas as strings constantes usadas no programa, facilitando a manutenção e a modificação.
I/O de Baixo Nível
Durante o desenvolvimento, erroneamente interpretei a restrição "...utilize apenas a C standard library..." como "utilize apenas funções da stdlib.h", o que me levou a não usar as funções scanf e printf da biblioteca stdio.h.
Com a minha interpretação inicial da restrição em mente, usei as syscalls read e write e desenvolvi a função scan_int para substituir o uso de scanf e printf.
Posteriormente descobri que "C standard library" se refere a um conjunto de arquivos que excede stdlib.h. Porém, a decisão de desenvolver minhas próprias funções de I/O se tornou uma excelente oportunidade para implementar uma camada de I/O de baixo nível usando syscalls puras. A solução desenvolvida foi um ótimo exercício de parsing e programação de sistemas.
Integração C e Assembly
A função matmul foi escrita em Assembly para explorar o controle de baixo nível sobre o processador. A comunicação segue a ABI padrão do RISC-V.
Pré-requisitos
-
Um compilador da toolchain RISC-V que gera executáveis para Linux (ex: riscv64-linux-gnu-gcc)
-
Um emulador RISC-V, como o QEMU (qemu-riscv64).
Compilação
Use o seguinte comando para compilar todos os arquivos-fonte em um único executável:
make allPara apagar o executável, use:
make cleanPara recompilar o executável, use:
make recompileExecução
Para executar o programa compilado no QEMU de forma interativa (em que o usuário deve digitar as entradas no terminal):
make runou, alternativamente, caso se queira usar stream files:
qemu-riscv64 matrix <in >outLocalidade de Cache: A implementação atual em matmul.s acessa a matriz B por colunas. Como as matrizes são armazenadas em memória por linhas (row-major), isso leva a saltos grandes na memória, causando cache misses e degradando o desempenho em matrizes maiores. Uma melhoria futura seria implementar um algoritmo de blocagem (tiling) ou transpor a matriz B antes da multiplicação para garantir acessos sequenciais à memória.