In rare cases, it was possible to cause high contention on a lock used
while compiling code to run in the interpreter.
This fixes the problem in a couple different ways:
* Use ConditionalWeakTable which has finer granularity in locking
* Avoid dictionary lookups in the most common cases
- There really aren't too many real cases, I could have covered them
all, but keeping the code generic is useful for the future.
Not quite related, but I noticed we didn't cache certain interpreter
instructions and it made sense to add a cache because we generate a lot
of array-init instructions.