A while back I got about half of an LLVM backend for the VideoCore IV done (since completed by someone else). I eventually ground to a halt due to intractable bugs; later, someone picked it up and completed it.
This is from years ago, mind, but my recollection of the pain points were:
- enormously steep learning curve; very little of this layer is documented and there's a lot of concepts hidden behind layers of abstractions hidden behind other layers of abstractions, all of which you need to understand, but they're all cyclic. There's nowhere to start; everything you look at depends on your understanding of something else.
- very hard to debug --- the compiler's main reaction to things going wrong is to seg fault, which requires debugging through compiler internals (and understanding the compiler internals) to fix.
- holes in the tools. After generating the IR node tree, the main piece of machinery in the compiler is a pattern matcher which finds machine code instructions for sets of nodes in the tree. This is automatically generated by tablegen, but there are some patterns that it can't match, which means that along with the tablegen matcher you also need to write a manual matcher in C++ for the other patterns, which is very verbose and difficult to keep track of. Also, the tablegen matcher is pretty brittle and has a tendency to either hang or match the wrong pattern if you don't get it just right. Understanding why it's doing what it's doing is non-trivial.
The main problem, though, is that the pool of knowledge of how to write targets is restricted to a very small number of people --- it's all what an ex-boss of mine used to refer to as 'tribal knowledge'. So that when you run into a problem, you're reliant on finding one of these people who has enough time or incentive to help. Since the problems you run into tend to be complex, helping is hard, so getting good help isn't easy, and so on.
I should add that gcc suffers from exactly the same set of problems, except possibly a little more so. gcc's get better target layer documentation, but the LLVM community is much more helpful; gcc is simpler, but LLVM I think has the edge of good design. They're both much of a muchness.
It's frustrating, as compiler backends aren't that hard. There should be a compiler somewhere which combines easy porting with adequate code generation[1], for people who don't want to spend the resources needed for an LLVM or gcc port.
[1] I'm actually working on this.