For signed integers, the 8-bit value might have to be sign-extended to load it into a 32-bit or 64-bit register and use it with 32-bit instructions.
For unsigned integers, the intermediate results might be carried out in 32-bit or 64-bit arithmetic, in which case the result has to be reduced modulo 2N.
As a concrete example, consider:
#include <cstdint>
void i8div(int8_t &a, const int8_t &b) {
a /= b;
}
void i32div(int32_t &a, const int32_t &b) {
a /= b;
}
On an ESP32, this compiles to:
i8div(signed char&, signed char const&):
entry sp, 32 ; enter function
l8ui a8, a2, 0 ; load a
l8ui a9, a3, 0 ; load b
sext a8, a8, 7 ; sign extend a
sext a9, a9, 7 ; sign extend b
quos a8, a8, a9 ; divide
s8i a8, a2, 0 ; store quotient in a
retw.n ; return from function
i32div(int&, int const&):
entry sp, 32 ; enter function
l32i.n a8, a2, 0 ; load a
l32i.n a9, a3, 0 ; load b
quos a8, a8, a9 ; divide
s32i.n a8, a2, 0 ; store quotient in a
retw.n ; return from function
Notice how the 8-bit version includes additional sext
instructions (sign extend).
An example for unsigned integers on ARM:
uint8_t foo8(uint8_t a, uint8_t b) {
uint8_t c = a + b;
return c * c;
}
uint32_t foo32(uint32_t a, uint32_t b) {
uint32_t c = a + b;
return c * c;
}
foo8(unsigned char, unsigned char):
and w1, w1, 255 ; b = b mod 256
add w0, w1, w0, uxtb ; c = a + b (with zero extension of byte)
and w0, w0, 255 ; c = c mod 256
mul w0, w0, w0 ; c * c
ret
foo32(unsigned int, unsigned int):
add w0, w0, w1 ; c = a + b
mul w0, w0, w0 ; c * c
ret
Here, notice the additional and
instructions to reduce the intermediate values modulo 256.