diff options
author | Mathias Magnusson <mathias@magnusson.space> | 2025-05-31 22:54:26 +0200 |
---|---|---|
committer | Mathias Magnusson <mathias@magnusson.space> | 2025-05-31 22:54:26 +0200 |
commit | 5749be69125dc87ac50742295272a7e21f4f472e (patch) | |
tree | cf0aa4776eda997f94ef35e06e41f16678aedebd /src/codegen.zig | |
parent | 590a76edb6a88f754a43e96f16f2fc73845238b5 (diff) | |
download | huginn-5749be69125dc87ac50742295272a7e21f4f472e.tar.gz |
codegen integer literals correctly
This was not as easy as one would expect ☠️
Diffstat (limited to 'src/codegen.zig')
-rw-r--r-- | src/codegen.zig | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index b0bf5c5..368309c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -485,20 +485,36 @@ const Context = struct { } } - fn genConstant(self: *Context, constant: compile.Instr.Constant) !void { - const reg = self.register_allocator.allocate(constant.dest) orelse return error.OutOfRegisters; - - if (constant.value <= std.math.maxInt(i12)) { - try self.emit(.addi(reg, .zero, @intCast(constant.value))); - } else if (constant.value <= std.math.maxInt(i32)) { - // If the higest bit in the immediate in addi is set, it will be sign extended. We negate that by adding one more to the immediate for lui. - try self.emit(.lui(reg, @intCast((constant.value >> 12) + if (constant.value & (1 << 11) != 0) @as(u64, 1) else 0))); - try self.emit(.addi(reg, reg, @bitCast(@as(u12, @truncate(constant.value))))); + fn genConstantInner(self: *Context, reg: Register, value: u64) !void { + if (value <= std.math.maxInt(i12)) { + // If the highest bit is set, we will get sign extension from this, but it will be + // cleared by the next addiw. + try self.emit(Instruction.addi(reg, .zero, @intCast(value))); + } else if (value <= std.math.maxInt(i32)) { + const lower: u12 = @truncate(value); + const upper: u20 = @truncate(if (lower >> 11 == 1) (value >> 12) + 1 else value >> 12); + // If the highest bit in upper is set here, we will get an unwanted sign extension, but + // that will be cleared in the `addiw` right afterwards. If the highest bit was set, + // then that must be because (lower >> 11 == 1) happened, so lower is not zero, thus the + // `addiw` is guaranteed to be run. The only other way the highest bit can be set would + // be if value had `1 << 32 == 1`, but then it would not be smaller than + // `std.math.maxInt(i32)`. + try self.emit(.lui(reg, @bitCast(upper))); + if (lower > 0) try self.emit(.addiw(reg, reg, @bitCast(lower))); } else { - unreachable; // TODO + const thisVal: u12 = @truncate(value); + const nextVal = if (thisVal >> 11 == 1) (value >> 12) + 1 else value >> 12; + try self.genConstantInner(reg, nextVal); + try self.emit(.slli(reg, reg, 12)); // TODO: sometimes these `slli`s can be collapsed + if (thisVal > 0) try self.emit(.addi(reg, reg, @bitCast(thisVal))); } } + fn genConstant(self: *Context, constant: compile.Instr.Constant) !void { + const reg = self.register_allocator.allocate(constant.dest) orelse return error.OutOfRegisters; + try self.genConstantInner(reg, constant.value); + } + fn genBinOp(self: *Context, bin_op: compile.Instr.BinOp) !void { const lhs = self.register_allocator.get(bin_op.lhs); const rhs = self.register_allocator.get(bin_op.rhs); |