Conceptually, the backend of an optimizing compiler is supposed to be the part that performs CPU architecture specific optimizations, while the middle-end is target-independent. In reality, the middle-end does need to take certain properties of the target into account. However, these need to be carefully managed to maintain the overall architecture of the compiler.