Looking at the code generated for users of IdString reveals some issues. For example, here’s a disassembly of OptMergeWorker::hash_cell_inputs (x86-64):
Every ID() macro call site has a C++ static variable associated with it. For each of those, there’s a hidden “initialized” flag that has to be checked using a load and conditional branch. It’s cheaper to use global IdString variables in the ID:: namespace. The ID:: constants don’t support IdStrings starting with $, but it’s easy enough to add something similar. There are still thousands of calls to the ID() macro but I think it would be good to eventually eliminate them. Please let me know if people agree or disagree…
Every ID() macro call invokes a lambda that returns the IdString. The inferred return type for that lambda is IdString (not a reference), so we create a temporary IdString, which requires incrementing (and eventually decrementing) a refcount. Similarly, CellTypes::output() and other methods take IdString by value, leading to refcount churn. So do IdString::in() and hashlib::hash_ops::hash(). I have created a PR that fixes all these. There are a lot more places that should be converted to take IdString by reference, but that PR is a good start.
Large in() expressions compile to a long chain of comparisons. In this case, clang compiles them as a chain of cmp/sete/or, which avoids branch mispredictions within a single in()… but it’s still a lot of instructions, and as many branches as if cases. The underlying problem is that even for the ID:: constants, theirIdString indices are not known at compile time. Fixing this is a little tricky but not a lot of code. With that fixed, clang turnshash_cell_inputs’s chain of ifs andin()s into a jump table. It’s beautiful.
8db5d7: 8b 46 48 mov 0x48(%rsi),%eax // load 'cell->type'
...
8db607: 05 de fe ff ff add $0xfffffede,%eax
8db60c: 83 f8 1c cmp $0x1c,%eax
8db60f: 0f 87 a0 08 00 00 ja 8dbeb5 <_ZZN12_GLOBAL__N_114OptMergeWorkerC1EPN5Yosys5RTLIL6DesignEPNS2_6ModuleEbbbENK11CellPtrHashclEPKNS2_4CellE+0x8f5>
8db615: 48 8d 0d 40 9e a3 00 lea 0xa39e40(%rip),%rcx # 131545c <_ZTSN12_GLOBAL__N_17OptPassE+0x90>
8db61c: 48 63 04 81 movslq (%rcx,%rax,4),%rax
8db620: 48 01 c8 add %rcx,%rax
8db623: ff e0 jmp *%rax
Making the well-known IdString indices compile-time constants seems like a clear win to me. I’ve made this into a PR.
If this sounds good and we want to keep pushing in this direction then future steps would be to convert more code to take IdString by reference, and remove those ID() macro calls.