Arguments are placed in memory "slots" on the stack. A small argument might take one slot, or share a slot with another argument, while a large argument might take two slots. You can see how if you mistake a small argument for a large one, it will shift all the subsequent arguments into incorrect positions.
void my_func(int32 p1, int64 p2)
| |
+---------------+
| P2 (64-bit) |
+---------------+
| P2 | (P2 takes up two slots)
+---------------+
+ P1 (32-bit) |
+---------------+ <-- Top of stack
Now if you mistakenly use a 64-bit value for P1, you get this instead:
| |
+---------------+
| P2 (64-bit) |
+---------------+
| P2 |
+---------------+
+ P1 (64-bit) |
+---------------+
+ P1 |
+---------------+ <-- Top of stack
The callee (the function you called) has no idea that this shift has taken place, and therefore attempts to read the arguments from their expected positions. For small arguments it might get a piece of a larger one, and for larger arguments it might get pieces of two or more other arguments.
What the callee actually sees is this:
| |
+---------------+
| (P2) |
+---------------+
+ (P1) |
+---------------+
+ (P1) |
+---------------+ <-- Top of stack
You can see that the value read for P1 is actually only half of the larger value, while the value read for P2 comprises the other half of P1 and half of the value passed in as P2.
(This explanation is somewhat simplified, but generally indicates how the stack works)