How is Ultrassembler so fast?
Ultrassembler is a superfast and complete RISC-V assembler library that I'm writing as a component of the bigger Chata signal processing project.
Assemblers take in a platform-dependent assembly language and output that platform's native machine code which runs directly on the processor.
"Why would you want to do this?" you might ask. First, existing RISC-V assemblers that conform the the entirety of the specification, as and llvm-mc , ship as binaries that you run as standalone programs. This is normally not an issue. However, in Chata's case, it needs to access a RISC-V assembler from within its C++ code. The alternative is to use some ugly C function like system() to run external software as if it were a human or script running a command in a terminal.
Here's an example of what I'm talking about:
#include < iostream > #include < string > #include < stdlib.h > int main () { std::string command = " riscv64-linux-gnu-as code.s -o code.bin "; int res = std:: system (command. data ()); if (res != 0 ) { std::cerr << " Error executing command: " << command << std::endl; } return res; }
It gets even worse once you realize you need temporary files and possibly have to artisanally craft the command beforehand. Additionally, invoking the assembler in this manner incurs a significant performance overhead on embedded systems which lack significant processing power. There must be a better way.
Enter Ultrassembler.
With these two goals of speed and standard conformance in mind, I wrote Ultrassembler as a completely standalone library with GNU as as the speed and standard conformity benchmark.
The results are nothing short of staggering.
... continue reading