From my small experience of Ghidra, it didn't do great once the code was not using standard calling conventions (i.e it was probably compiled with optimization flags )
Sometimes it would just straight up ignore (functional) assembly for apparently no reason. Or it would turn simple code into a myriad of nested conditionals and loops, achieving the same goal, but looking nothing like a human would write.
It was still very helpful in understanding blocks of assembly much faster than I otherwise would, and it's possible I was lacking some configuration that a more experienced user could do to help the decompiler out.
Or dead assignments. I've seen it with HexRays: if you don't tell it that e.g. var_16 is actually 32 bytes long, not 4, it will completely ignore any code that reads/writes stack between var_16 + 4 and var_48 (which is at var_16 + 32). It's quite an amusing sight to see: you have an 8 lines-long decompiled function from 300 lines of assembly, you edit a variable's annotation, boom, the decompiled function is now 40 lines long, with all kinds of interesting computations in its body.
Sometimes it would just straight up ignore (functional) assembly for apparently no reason. Or it would turn simple code into a myriad of nested conditionals and loops, achieving the same goal, but looking nothing like a human would write.
It was still very helpful in understanding blocks of assembly much faster than I otherwise would, and it's possible I was lacking some configuration that a more experienced user could do to help the decompiler out.