TPDE
Loading...
Searching...
No Matches
ScratchReg.hpp
1// SPDX-FileCopyrightText: 2025 Contributors to TPDE <https://tpde.org>
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4#pragma once
5
6namespace tpde {
7template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
8struct CompilerBase<Adaptor, Derived, Config>::ScratchReg {
9private:
10 CompilerBase *compiler;
11 // TODO(ts): get this using the CompilerConfig?
12 AsmReg reg = AsmReg::make_invalid();
13
14public:
15 explicit ScratchReg(CompilerBase *compiler) : compiler(compiler) {}
16
17 explicit ScratchReg(const ScratchReg &) = delete;
18 ScratchReg(ScratchReg &&) noexcept;
19
20 ~ScratchReg() noexcept { reset(); }
21
22 ScratchReg &operator=(const ScratchReg &) = delete;
23 ScratchReg &operator=(ScratchReg &&) noexcept;
24
25 bool has_reg() const noexcept { return reg.valid(); }
26
27 AsmReg cur_reg() const noexcept {
28 assert(has_reg());
29 return reg;
30 }
31
32 AsmReg alloc_specific(AsmReg reg) noexcept;
33
34 AsmReg alloc_gp() noexcept { return alloc(Config::GP_BANK); }
35
36 /// Allocate register in the specified bank, optionally excluding certain
37 /// non-fixed registers. Spilling can be disabled for spill code to avoid
38 /// recursion; if spilling is disabled, the allocation can fail.
39 AsmReg alloc(RegBank bank) noexcept;
40
41 void reset() noexcept;
42
43 /// Forcefully change register without updating register file. Avoid.
44 void force_set_reg(AsmReg reg) noexcept { this->reg = reg; }
45};
46
47template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
48CompilerBase<Adaptor, Derived, Config>::ScratchReg::ScratchReg(
49 ScratchReg &&other) noexcept {
50 this->compiler = other.compiler;
51 this->reg = other.reg;
52 other.reg = AsmReg::make_invalid();
53}
54
55template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
56typename CompilerBase<Adaptor, Derived, Config>::ScratchReg &
57 CompilerBase<Adaptor, Derived, Config>::ScratchReg::operator=(
58 ScratchReg &&other) noexcept {
59 if (this == &other) {
60 return *this;
61 }
62
63 reset();
64 this->compiler = other.compiler;
65 this->reg = other.reg;
66 other.reg = AsmReg::make_invalid();
67 return *this;
68}
69
70template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
71typename CompilerBase<Adaptor, Derived, Config>::AsmReg
72 CompilerBase<Adaptor, Derived, Config>::ScratchReg::alloc_specific(
73 AsmReg reg) noexcept {
74 assert(compiler->may_change_value_state());
75 assert(!compiler->register_file.is_fixed(reg));
76 reset();
77
78 if (compiler->register_file.is_used(reg)) {
79 compiler->evict_reg(reg);
80 }
81
82 compiler->register_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
83 compiler->register_file.mark_clobbered(reg);
84 compiler->register_file.mark_fixed(reg);
85 this->reg = reg;
86 return reg;
87}
88
89template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
90CompilerBase<Adaptor, Derived, Config>::AsmReg
91 CompilerBase<Adaptor, Derived, Config>::ScratchReg::alloc(
92 RegBank bank) noexcept {
93 assert(compiler->may_change_value_state());
94
95 auto &reg_file = compiler->register_file;
96 if (!reg.invalid()) {
97 assert(bank == reg_file.reg_bank(reg));
98 return reg;
99 }
100
101 // TODO(ts): try to first find a non callee-saved/clobbered register...
102 auto reg = reg_file.find_first_free_excluding(bank, 0);
103 if (reg.invalid()) {
104 // TODO(ts): use clock here?
105 reg = reg_file.find_first_nonfixed_excluding(bank, 0);
106 if (reg.invalid()) [[unlikely]] {
107 TPDE_FATAL("ran out of registers for scratch registers");
108 }
109 compiler->evict_reg(reg);
110 }
111
112 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
113 reg_file.mark_clobbered(reg);
114 reg_file.mark_fixed(reg);
115 this->reg = reg;
116 return reg;
117}
118
119template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
120void CompilerBase<Adaptor, Derived, Config>::ScratchReg::reset() noexcept {
121 if (reg.invalid()) {
122 return;
123 }
124
125 compiler->register_file.unmark_fixed(reg);
126 compiler->register_file.unmark_used(reg);
127 reg = AsmReg::make_invalid();
128}
129} // namespace tpde
CompilerBase(Adaptor *adaptor)
Initialize a CompilerBase, should be called by the derived classes.