It is tempting to naively design a traditional compiler for Tcl, which outputs pure executable code. However, there are numerous problems with this. First, Tcl is highly dynamic in nature. Commands (functions in the C model) can be called by their string names or rebound at any point through the rename command. Similarly, variables can be unset, commands may not exist (and hence trigger unknown to be called), traces can be placed on all data objects, and so on. To implement all of these features would require both a large runtime library and enormous overhead in each usage, not dissimilar from the same overhead that the existing interpreter incurs.
Second, it is unclear how to efficiently store data for Tcl. Since there are no types in the language, there is no obvious data layout method one can use besides strings. Again, this is what the current interpreter does.
Lastly, Tcl uses C callbacks (``builtins'') as first-class functions, where the callbacks have direct access to the interpreter state. Callback functions are free to make changes to, or depend on, the state of the virtual machine; these are called ``side effects'' in compiler parlance. The difficulty with these side effects in the Tcl/C model is that they are impossible to predict. Without this knowledge, the compiler cannot be sure that any given statement won't cause a rename, unset or some other state change in the virtual machine.