Well, let us step through the instructions and see what they correspond to in the C code:
push %ebp
movl %esp, %ebp
These do not carry out any of the statements in the function. Instead, they are setting up a stack frame for the function simple: this allows us to push and pop off the stack, if necessary, while still referring to local variables and function arguments by constant offsets from ebp.
movl 8(%ebp), %eax
This moves the value at memory address ebp + 8 into eax. Since ebp + 8 is the address of the first function argument, we now have xp in eax.
movl (%eax), %edx
xp is a pointer to an int, which means it contains the memory address of an int. (%eax) retrieves the value at the memory address in eax (which is xp), and %edx means that the value is moved into edx. At this point, we can say that eax contains xp and edx contains *xp (the int to which xp points).
addl 12(%ebp), %edx
12(%ebp) is the value at memory location ebp + 12, i.e. the value of y, the second function argument. We are to add y to the value in edx (*xp). This finishes implementing int t=*xp + y, and stores t in edx.
movl %edx, (%eax)
Moves t into the memory location specified in eax (xp). This implements *xp = t.
movl %edx, %eax
Moves t into register eax. This a different instruction than the one above, because the op above does not change eax, it just changes the dword in the memory block at which eax points. Since ints are returned in eax, and simple returns t, we need to move the actual value of t into eax before returning.
movl %ebp, %esp
popl %ebp
We are destroying simple's stack frame, so that the calling function's stack is preserved when we return from simple.