To avoid polluted profiles, tier one should create disjoint "split" profiles for methods that it inlines. If a method is inlined three times, it should get three fresh copies of its global profile, initialized to no types or counts. When a later tier re-optimizes the code and inlines the same method, the compiler should use the context-dependent profile in preference to the global profile, if one is available.
Background:
The interpreter collects type profiles for invoke receivers and type checks. The profiles are crucial to later optimizing compilation. Tier one currently emulates the interpreter with respect to profiling.
Profiles are created when a method is somehow noticed as relevant to execution (e.g., warm enough). Each profile applies to one bytecode method, and is affected by all executions of that method, from whatever caller. Each original instance of a relevant bytecode instruction (invokevirtual, checkcast, etc.) gets a ReceiverTypeData record in the profile structure, which is called MethodData. Each record has a number (TypeProfileWidth, default 2) of rows, each of which contains a count and an exact object type.
Profiles have two failure modes: First, a method might be compiled before its profile exists and is "mature", so that no stable conclusions can be drawn about operands in that method. Second, a method might be used from many different contexts with independent operand types (as with ArrayList.contains), so that the profile becomes "polluted" by many independent types. A polluted profile contains the first few (2) encountered types with low counts, and a high total count that signals that the ReceiverTypeData structure was too small.
Polluted profiles stem from the fact that a method (containing generically reusable code) has only one profile structure, but the method is reused from a variety of contexts, providing a variety of operand types.