I guess the core difference is that we do not implement the instructions semantics ourselves, but we reuse QEMU.
QEMU, when in emulation mode (i.e., not KVM), goes from executable code to an intermediate representation (tiny code instructions) and then compile it back to the host architecture.
We use the first part of QEMU but then translate tiny code instructions into LLVM IR.
This means that:
1. We don't have to implement instruction semantics on our own;
2. We can easily support the architectures supported by QEMU. Currently we support x86-64, i386, ARM, AArch64, MIPS and S390. https://wiki.qemu.org/Documentation/Platforms
3. We get out of the box the accuracy in instructions semantics of a mature emulator such as QEMU.
We used to be able to lift `gcc` to LLVM IR, recompile it and have it working.
However, the real difference is that, while it still works, we're now no longer focused on binary-to-binary translation, but we're now 100% focused on writing a full blown decompiler, i.e., something that goes from binary code to (valid) C and has a nice interactive UI.
As you can imagine, the lifter is really just the first half of a decompiler.
For instance, one core feature of rev.ng is automatic detection of `struct`s, just by looking at how they're used: https://twitter.com/_revng/status/1575553313827069952
If you have been doing some reverse engineering, I'm sure you'd appreciate this feature.
Bonus, take a look at our UI: https://twitter.com/_revng/status/1720440515265474631
As a side note, towards the end of the month we're releasing the new website and starting to invite people to the closed beta. If you wanna join: https://rev.ng/register-for-nightly.html