r/computerscience • u/ShadowGuyinRealLife • 2d ago
General Does Replacing Instructions that Do Nothing with NOPs Change Anything?
Suppose in the executable, instructions N to N+6 do some load instructions from memory, then N+7 to N+30 are some integer operations like add, subtract, multiply, bitwise operations and so on. the results are not stored to memory. Then Instruction N+31 to N+37 loads new values in the registers and none of the results of the previous calculations do anything. A different version of the executable has instructions N to N+30 all be "NOP" and N+31 to N+37 are the same as before. Does anything change? My inspiration for this is the game Mario 64 had been dissected and if you look at the assembly of the NTCS version, there are plenty of instructions that do some calculations that aren't used and some weird things like loading from the same address to the same register when the register's value could not possibly be changed. At the same time these instructions couldn't outright be removed or the alignment of all the JMPs would be thrown off. My friend said perhaps some NOPs might be done with a lower power consumption than some of the multiplication instructions, but otherwise if the later instructions don't use the results of the extraneous instructions and are unaffected by their flags nothing should change.
3
u/xcookiekiller 2d ago
I don't know if this is relevant for this exact case but I think it's worth looking into the topic of memory mapped I/O, it kinda breaks the assumption of no side effects of a read/write when the value isn't used again
3
u/InjAnnuity_1 1d ago
As mentioned elsewhere, reads (and writes) to specific hardware addresses may have side effects outside of the CPU and memory, i.e., to other specific hardware such as audio, video, or other memory-mapped devices.
Even if the results are thrown away, the instructions may have been chosen to run for a specific length of time. For example, if a function has two execution paths, it may help other hardware-specific code (or help the programmer!) if the execution time is guaranteed to be identical either way.
This can help with some old video hardware, especially if it shared memory with the main CPU. You wanted to make sure that video memory accesses were timed so that they didn't garble the image.
Radio Shack Model I was just such a machine.
1
1
u/soundman32 1d ago
Back in the c64 days, you could ask the video chip to interrupt the cpu when the display scan line hit a certain line onscreen. If you wanted to change the colour of the border half way down the screen, you could configure the interrupt and write to the border colour register and return. The problem was that the interrupt took enough microseconds that the colour change happened part of the way through the left border, so you got a step. The trick was to nop enough times (could be 2 or 3 i cant remember) that you delayed the processor until it was inside the border and then did the switch. So, the right border was changed on the correct line 125 and the left border was changed on the line after and nobody noticed. (Then you reset the colour back at line zero).
1
u/Guvante 3h ago
NOPs serve a couple vital purposes. The biggest one is alignment, there are a ton of reasons you want an instruction to have a particular alignment and NOPs are the gold standard.
Note that NOP tends to refer to a collection of instructions that do nothing like multiply a register by 1.
Useless instructions is more complicated especially with an old game for a unique architecture.
Sometimes the compiler messes up for a bunch of complex reasons. For instance if you have a calculation and then branch on the result but the compiler realizes the branch isn't needed so elides it but doesn't (for a ton of potential reasons) remove the calculation.
Another factor can be helpers used to simplify programming that accidentally introduce extraneous work. The compiler can't always fix those up.
A final note these kinds of issues are really hard to fix because as you noted any change wrecks everything else in the program and more importantly these little mistakes aren't consistent so if you change anything else they might change or go away.
21
u/nuclear_splines PhD, Data Science 2d ago
Well, it depends on what you mean by "change anything".
If you perform a calculation and throw the results away, that should be equivalent to not performing the calculation in abstract logic land
If you read a value from memory it may be placed in cache, or could be kept around in cache for longer, even if the value isn't used. This could incidentally change the performance of future instructions
Depending on the hardware architecture, some instructions may take much longer to run than others. If the rest of the program is timing-sensitive, replacing 'unused' instructions with NOPs could upset that timing
Changing the instructions will of course change the bytes of the executable and its hash, which could theoretically impact behavior (such as copy protection checking whether the program has been altered)