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 AsmReg release() noexcept {
42 AsmReg res = reg;
43 reset();
44 return res;
45 }
46
47 void reset() noexcept;
48
49 /// Forcefully change register without updating register file. Avoid.
50 void force_set_reg(AsmReg reg) noexcept { this->reg = reg; }
51};
52
53template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
54CompilerBase<Adaptor, Derived, Config>::ScratchReg::ScratchReg(
55 ScratchReg &&other) noexcept {
56 this->compiler = other.compiler;
57 this->reg = other.reg;
58 other.reg = AsmReg::make_invalid();
59}
60
61template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
62typename CompilerBase<Adaptor, Derived, Config>::ScratchReg &
63 CompilerBase<Adaptor, Derived, Config>::ScratchReg::operator=(
64 ScratchReg &&other) noexcept {
65 if (this == &other) {
66 return *this;
67 }
68
69 reset();
70 this->compiler = other.compiler;
71 this->reg = other.reg;
72 other.reg = AsmReg::make_invalid();
73 return *this;
74}
75
76template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
77typename CompilerBase<Adaptor, Derived, Config>::AsmReg
78 CompilerBase<Adaptor, Derived, Config>::ScratchReg::alloc_specific(
79 AsmReg reg) noexcept {
80 assert(compiler->may_change_value_state());
81 assert(!compiler->register_file.is_fixed(reg));
82 reset();
83
84 if (compiler->register_file.is_used(reg)) {
85 compiler->evict_reg(reg);
86 }
87
88 compiler->register_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
89 compiler->register_file.mark_clobbered(reg);
90 compiler->register_file.mark_fixed(reg);
91 this->reg = reg;
92 return reg;
93}
94
95template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
96CompilerBase<Adaptor, Derived, Config>::AsmReg
97 CompilerBase<Adaptor, Derived, Config>::ScratchReg::alloc(
98 RegBank bank) noexcept {
99 assert(compiler->may_change_value_state());
100
101 auto &reg_file = compiler->register_file;
102 if (!reg.invalid()) {
103 assert(bank == reg_file.reg_bank(reg));
104 return reg;
105 }
106
107 reg = compiler->select_reg(bank, /*exclusion_mask=*/0);
108 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
109 reg_file.mark_clobbered(reg);
110 reg_file.mark_fixed(reg);
111 return reg;
112}
113
114template <IRAdaptor Adaptor, typename Derived, CompilerConfig Config>
115void CompilerBase<Adaptor, Derived, Config>::ScratchReg::reset() noexcept {
116 if (reg.invalid()) {
117 return;
118 }
119
120 compiler->register_file.unmark_fixed(reg);
121 compiler->register_file.unmark_used(reg);
122 reg = AsmReg::make_invalid();
123}
124} // namespace tpde
CompilerBase(Adaptor *adaptor)
Initialize a CompilerBase, should be called by the derived classes.