Since you tagged the question as related to microcontrollers, I'll take a try.
a) my intuition is correct
Your intuition is correct in the sense that it might be possible to determine current draw, specially when dealing only with the instructions themselves. If I were to do it, I'd make a long program with many equal instructions, flash the microcontroller and then measure average current draw (easier to do with microcontrollers than with traditional computers). Then repeat for each instruction.
With a table of current draw per instruction, it should, theoretically, be possible to select, from the compiler options, the arrangements which have lesser draw (taking into consideration also the time taken to complete the operation sequence). Of course, this is very hardware-dependent, and you would have to do the measurements for each chip you use, or at least do transistor-level simulations for each.
To make matters worse, each physical chip has slight differences that may affect current draw, even changing your supposed table. There's no way your compiler can account for that, except if you're tailoring the compiler for a specific chip, or if you're only estimating measurements statistically.
From the compiler standpoint, it probably wouldn't make sense to do this for operands, since for most situations the compiler won't know the operands ahead-of-time, unless you're targeting very specific cases like zeroing a register (comment about that below).
The exception, of course, is if you're designing encryption software. For this case, current draw analysis is a known side-channel attack vector (see some studies. However, like all other cases, this is also very hardware-dependent, and encryption-focused hardware is designed to use the same current draw regardless of the operands, which defeats your whole idea.
b) it's practical to estimate the current draw of a CPU instruction if you know what state the CPU's in, so that you know exactly what the instruction is doing.
As explained, very maybe possible, but not practical, especially for a 32-bit microcontroller, and probably unfeasible if taking operand effects into account. Would your "state" also include every possible operand for every possible instruction? How much time does each experiment or simulation take? It might be feasible for a single chip only, or maybe if you are the chip manufacturer, or if the chip is also only theoretical.
subtracting 0xff from 0xff would draw more power than subtracting 0x01 from 0x01 because I think the inputs to the ALU need to be precharged more or something.
This is also very hardware-dependent. Remember that the classical x86 example to zero a register is to XOR it with itself. This is done regardless of the register value, and you probably can't select the value that's already in there.
As a final thought, consider that manufacturers already struggle to make each instruction use as little energy as possible. If compilers use faster sequences of instructions, they already spend less energy. If they reduce the time taken doing calculations, they can get to sleep earlier and reduce total energy. For example, using the hardware FPU might actually use less energy, since doing FP calculations in software takes much longer to complete. The only way to avoid it is by using fixed-point, but you don't want your compiler to make this kind of decision.
My conclusion is: while a nice thought experiment, I wouldn't pursue this idea.
mov r0, 0
is more power hungry thanxor r0, r0
. Then tomorrow, the CPU manufacturer decided to change its hardware so thatmov
is now less power-consuming thanxor
and simply released this processor as a revision of the first one. This will effectively invalidate your compiler effort. \$\endgroup\$