Bem, vamos por partes:
O caso do andl -16, é como eu respondi na outra dúvida que tiveram ainda agora, eu não tenho 100%, mas acredito que seja uma instrução que o compilador adicionou para "ajustar" a pilha, repare que ela aparece sempre ao início da função main, no caso ele não influência na lógica do algoritmo que foi descrito.
Quanto ao que o programa faz, vou quebrar ele em partes para tentar explicar melhor, começando pela main(vou pular as instruções até o andl $-16):
subl $16, %esp -> Reservo 16 espaços na pilha, no caso, espaço para 4 inteiros
movl $400, 12(%esp) -> guardo o valor inteiro 400 na posição dada
movl $300, 8(%esp) -> guardo o valor inteiro 300 na posição dada
movl $200, 4(%esp) -> guardo o valor inteiro 200 na posição dada
movl $100, (%esp) -> guardo o valor inteiro 100 na posição dada
call multiplica -> Chamo a função multiplica
Bem, só de olhar esse pedaço de código em separado, parece que esses valores inteiros estão sendo passados como argumentos para a função multiplica que está sendo chamada, quando analisar a função, veremos que é exatamente isso.
movl %eax, 8(%esp) -> Movo o valor de EAX para a posição dada na pilha
movl $.LC0, 4(%esp) -> Movo o endereço da string definida acima("%d\n") para a pilha
movl $1, (%esp) -> Movo o inteiro 1 para a pilha
call __printf_chk -> chamo a função __printf_chk
No caso aqui, estou novamento empilhando parametrôs de uma função, no caso a __printf_chk, que só de olhar o código, sem o enunciado, deve funcionar de forma bem similar ao printf
No caso os parametrôs são:
1) "%d\n", que se lembramos de comp1 é a string que temos que passar para o printf imprimir um inteiro na tela e realizar uma quebra de linha
2) o valor que estava em EAX, que no caso vai ser um inteiro que é resultante da função multiplica, como veremos depois
3) e esse valor inteiro 1, que(como eu acabei de descobrir) é uma flag usada na função, deêm uma procurada pelo nome da função no google que teve ter uma explicação boa de para que serve essa flag.
Bem, isso é a main, vamos olhar a multiplica(vou pular as linhas de ajuste do EBP porque são aquelas clássicas de todas as funções):
movl 12(%ebp), %eax -> movo o argumento que estava nessa posição, no caso o 2º argumento, para EAX
imull 8(%ebp), %eax -> multiplico o valor de EAX pelo 1º argumento, logo EAX = 1ºarg x 2ºarg
imull 16(%ebp), %eax -> multiplico o resultado acima pelo 3º argumento
imull 20(%ebp), %eax -> multiplico o resultado acima pelo 4º argumento
Esse resultado fica guardado em EAX, logo, EAX = 100*200*300*400, e no caso como o EAX é usado como argumento do print, ele está meio que funcionando como um registrador que guarda o valor de retorno da função nesse caso.
Espero que tenha ficado mais claro agora, qualquer coisa é só falar.