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

Code before:
	}

	@Override
	public InstructionPrototype parse(MemBuffer buf, ProcessorContext context, boolean inDelaySlot)
			throws InsufficientBytesException, UnknownInstructionException {
		if (alignment != 1) {
			if (buf.getAddress().getOffset() % alignment != 0) {
				throw new UnknownInstructionException(
					"Instructions must be aligned on " + alignment + "byte boundary.");
			}
		}

		SleighInstructionPrototype res = null;

		try {
			SleighInstructionPrototype newProto =
				new SleighInstructionPrototype(this, buf, context, contextcache, inDelaySlot, null);
			Integer hashcode = newProto.hashCode();

			if (!instructProtoMap.containsKey(hashcode)) {
				newProto.cacheInfo(buf, context, true);
			}

			synchronized (instructProtoMap) {
				res = instructProtoMap.get(hashcode);
				if (res == null) { // We have a prototype we have never seen
					// before, build it fully
					instructProtoMap.put(hashcode, newProto);
					res = newProto;
				}
				if (inDelaySlot && res.hasDelaySlots()) {
					throw new NestedDelaySlotException();
				}
			}
		}
		catch (MemoryAccessException e) {
			throw new InsufficientBytesException(e.getMessage());
		}

		try {
			SleighParserContext protoContext = res.getParserContext(buf, context);
			protoContext.applyCommits(context);
		}
		catch (Exception e) {
			throw new UnknownInstructionException();
		}
		return res;
	}

	public DecisionNode getRootDecisionNode() {
		return root;

Review comment (bug):
Before, it builds a prototype, and synchronizes storing the prototype if there isn't one.

Now it builds a prototype, checks if there is one, and has a bit of a race condition: it might set the prototype multiple times, and use a prototype that is no longer recorded. (In a thread-safe manner.)

does it matter that a prototype may be removed (overwritten) from `instructProtoMap`? is anything from the prototype stored elsewhere? (`instructProtoMap`?) or is it just a schema that will be the same even if it's built in parallel?

maybe a better change is removing `if (inDelaySlot && res.hasDelaySlots())` from the sync block, but I'm unsure what that actually does. Or putting it within the `if (res == null`..?

With the concurrent hash map, perhaps,
```
    res = instructProtoMap.putIfAbsent(hashcode, newProto);
    if (res == null) res = instructProtoMap.get(hashcode);
```

and call it a day. :-)

* [PutIfAbsent](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html#putIfAbsent-K-V-) - that return value, though.


Code after:
		return volatileAddresses.contains(addr);
	}

	@Override
	public InstructionPrototype parse(MemBuffer buf, ProcessorContext context, boolean inDelaySlot)
			throws InsufficientBytesException, UnknownInstructionException {
		if (alignment != 1) {
			if (buf.getAddress().getOffset() % alignment != 0) {
				throw new UnknownInstructionException(
					"Instructions must be aligned on " + alignment + "byte boundary.");
			}
		}

		SleighInstructionPrototype res = null;

		try {
			SleighInstructionPrototype newProto =
				new SleighInstructionPrototype(this, buf, context, contextcache, inDelaySlot, null);
			Integer hashcode = newProto.hashCode();

			if (!instructProtoMap.containsKey(hashcode)) {
				newProto.cacheInfo(buf, context, true);
			}

			res = instructProtoMap.get(hashcode);
			if (res == null) { // We have a prototype we have never seen
				// before, build it fully
				instructProtoMap.put(hashcode, newProto);
				res = newProto;
			}
			if (inDelaySlot && res.hasDelaySlots()) {
				throw new NestedDelaySlotException();
			}
		}
		catch (MemoryAccessException e) {
			throw new InsufficientBytesException(e.getMessage());
		}

		try {
			SleighParserContext protoContext = res.getParserContext(buf, context);
			protoContext.applyCommits(context);
		}
		catch (Exception e) {
			throw new UnknownInstructionException();
		}
		return res;
	}

	public DecisionNode getRootDecisionNode() {
		return root;
	}