Skip to content

Commit eb060d1

Browse files
committed
x86_64.Lower: replace slow stringToEnum call
Looking at a compilation of 'test/behavior/x86_64/unary.zig' in callgrind showed that a full 30% of the compiler runtime was spent in this `stringToEnum` call. Replace it with some nested `switch` statements using `inline else` to generate the cases at comptime. There are a lot, but most of them end up falling through to the runtime error case, so the block will get optimized away. Notably, this commit builds itself faster than the previous commit builds *it*self, because the performance degradation from the compiler having to analyze the `inline else` is beaten out by the performance gain from using this faster logic in `Lower`
1 parent 7987adf commit eb060d1

File tree

1 file changed

+25
-11
lines changed

1 file changed

+25
-11
lines changed

src/arch/x86_64/Lower.zig

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ fn encode(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operan
428428
}
429429

430430
fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
431-
@setEvalBranchQuota(2_800);
431+
@setEvalBranchQuota(80_000);
432432
const fixes = switch (inst.ops) {
433433
.none => inst.data.none.fixes,
434434
.inst => inst.data.inst.fixes,
@@ -457,19 +457,33 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
457457
else
458458
.none,
459459
}, mnemonic: {
460-
comptime var max_len = 0;
461-
inline for (@typeInfo(Mnemonic).@"enum".fields) |field| max_len = @max(field.name.len, max_len);
462-
var buf: [max_len]u8 = undefined;
463-
460+
switch (fixes) {
461+
inline else => |ct_fixes| {
462+
const fixes_name = @tagName(ct_fixes);
463+
const pattern = comptime fixes_name[if (std.mem.indexOfScalar(u8, fixes_name, ' ')) |i| i + " ".len else 0..];
464+
const wildcard_index = comptime std.mem.indexOfScalar(u8, pattern, '_').?;
465+
const header = pattern[0..wildcard_index];
466+
const trailer = pattern[wildcard_index + 1 ..];
467+
switch (inst.tag) {
468+
inline else => |ct_inst_tag| {
469+
const name = header ++ @tagName(ct_inst_tag) ++ trailer;
470+
if (@hasField(Mnemonic, name)) {
471+
break :mnemonic @field(Mnemonic, name);
472+
}
473+
// Fall through to runtime error case below
474+
},
475+
}
476+
},
477+
}
478+
// Error case
464479
const fixes_name = @tagName(fixes);
465480
const pattern = fixes_name[if (std.mem.indexOfScalar(u8, fixes_name, ' ')) |i| i + " ".len else 0..];
466481
const wildcard_index = std.mem.indexOfScalar(u8, pattern, '_').?;
467-
const parts = .{ pattern[0..wildcard_index], @tagName(inst.tag), pattern[wildcard_index + "_".len ..] };
468-
const err_msg = "unsupported mnemonic: ";
469-
const mnemonic = std.fmt.bufPrint(&buf, "{s}{s}{s}", parts) catch
470-
return lower.fail(err_msg ++ "'{s}{s}{s}'", parts);
471-
break :mnemonic std.meta.stringToEnum(Mnemonic, mnemonic) orelse
472-
return lower.fail(err_msg ++ "'{s}'", .{mnemonic});
482+
return lower.fail("unsupported mnemonic: '{s}{s}{s}'", .{
483+
pattern[0..wildcard_index],
484+
@tagName(inst.tag),
485+
pattern[wildcard_index + "_".len ..],
486+
});
473487
}, switch (inst.ops) {
474488
.none => &.{},
475489
.inst => &.{

0 commit comments

Comments
 (0)