Repository: NationalSecurityAgency/ghidra
File: Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/ContextCache.java (Java)

Code before:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ghidra.app.plugin.processors.sleigh;

import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;

import java.math.BigInteger;
import java.util.Arrays;

public class ContextCache {
	private int context_size = 0;
	private Register contextBaseRegister = null;

	private BigInteger lastContextValue;
	private int[] lastContextWords;

	public ContextCache() {
	}

	public void registerVariable(Register register) {
		context_size = (register.getBaseRegister().getMinimumByteSize() + 3) / 4;
		contextBaseRegister = register.getBaseRegister();
	}

	public int getContextSize() {
		return context_size;
	}

	public void getContext(ProcessorContextView ctx, int[] buf) {
		if (contextBaseRegister == null) {
			return;
		}
		RegisterValue contextRegValue = ctx.getRegisterValue(contextBaseRegister);
		if (contextRegValue == null) {
			Arrays.fill(buf, 0);
			return;
		}
		BigInteger contextValue = contextRegValue.getUnsignedValueIgnoreMask();
		int[] words = getWords(contextValue);
		for (int i = 0; i < buf.length; i++) {

Review comment (performance):
This looks like an expensive function. Instead of removing this entirely, how about making these variables an object?

```
private ContextCapsule capsule;

class ContextCapsule {
    private BigInteger lastContextValue;
    private int[] lastContextWords;
}

... 
private int[] getWords(BigInteger value) {
    ContextCapsule tmpstor = this.capsule;
    if (value.equals(tmpstor.lastContextValue)) {
        return tmpstor.lastContextWords;
    }
...

    this.capsule = new ContextCapsule(value, words);
    return words;
}
```

It maintains the "last value" optimization at no real cost and most of the benefit. It makes the assignment atomic, which is probably the real benefit that syncronization provides here (for two variables). (If in doubt, store context_size when computation starts so it doesn't reference the object variable.) There's no risk of getting an improper `words`, *if* you grab the whole capsule before you do the value compare. (The `value`/`words` are consistent within that capsule / function call, even if it's replaced as you're comparing).

(my variable naming should of course not be used.)

Might there be an additional optimization in storing context_size? (will context values never change?) Then if the size doesn't change, we don't need to recompute / repopulate the words array? Will words entries never change? Can we copy them rather than recompute them if `value` is different? Maybe we can't do that - if value is an increasing BigInt then the low-order bytes will change frequently.

Code after:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ghidra.app.plugin.processors.sleigh;

import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;

import java.math.BigInteger;
import java.util.Arrays;

public class ContextCache {
	private int context_size = 0;
	private Register contextBaseRegister = null;

	public ContextCache() {
	}

	public void registerVariable(Register register) {
		context_size = (register.getBaseRegister().getMinimumByteSize() + 3) / 4;
		contextBaseRegister = register.getBaseRegister();
	}

	public int getContextSize() {
		return context_size;
	}

	public void getContext(ProcessorContextView ctx, int[] buf) {
		if (contextBaseRegister == null) {
			return;
		}
		RegisterValue contextRegValue = ctx.getRegisterValue(contextBaseRegister);
		if (contextRegValue == null) {
			Arrays.fill(buf, 0);
			return;
		}
		BigInteger contextValue = contextRegValue.getUnsignedValueIgnoreMask();
		int[] words = getWords(contextValue);
		for (int i = 0; i < buf.length; i++) {
			buf[i] = words[i];
		}
	}