TPDE
Loading...
Searching...
No Matches
Compiler.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
6#include "CompilerConfig.hpp"
7#include "base.hpp"
8#include "tpde/Assembler.hpp"
9#include <concepts>
10#include <type_traits>
11
12#ifdef ARG
13 #error ARG is used as a temporary preprocessor macro
14#endif
15
16#define ARG(x) std::declval<x>()
17
18namespace tpde {
19
20class AssignmentPartRef;
21class CCAssigner;
22struct RegBank;
23
24template <bool B>
25concept IsTrue = (B == true);
26
27template <typename T>
28concept ValueParts = requires(T a) {
29 /// Provides the number of parts for a value
30 { a.count() } -> std::convertible_to<u32>;
31
32 /// Provides the size in bytes of a value part (must be a power of two)
33 { a.size_bytes(ARG(u32)) } -> std::convertible_to<u32>;
34
35 /// Provides the bank for a value part
36 { a.reg_bank(ARG(u32)) } -> std::convertible_to<RegBank>;
37};
38
39template <typename T>
40concept ValRefSpecialStruct = requires(T a) {
41 // Clang does not support std::is_corresponding_member.
42 { a.mode } -> std::same_as<uint8_t &>;
43 requires std::is_standard_layout_v<T>;
44 requires offsetof(T, mode) == 0;
45};
46
47template <typename T, typename Config>
48concept Compiler = CompilerConfig<Config> && requires(T a) {
49 // mostly platform things
50 { T::NUM_FIXED_ASSIGNMENTS } -> SameBaseAs<u32[Config::NUM_BANKS]>;
51
52 // (func_idx)
53 { a.start_func(ARG(u32)) };
54
55 {
56 a.prologue_assign_arg(
57 ARG(CCAssigner *), ARG(u32), ARG(typename T::IRValueRef))
58 };
59
60 // This has to call assembler->finish_func
61 // (func_idx)
62 { a.finish_func(ARG(u32)) };
63
64 // (reg_to_spill, frame_off, size)
65 { a.spill_reg(ARG(typename Config::AsmReg), ARG(u32), ARG(u32)) };
66
67 // (dst_reg, frame_off, size)
68 { a.load_from_stack(ARG(typename Config::AsmReg), ARG(u32), ARG(u32)) };
69
70 // (dst_reg, src_reg, size)
71 {
72 a.mov(ARG(typename Config::AsmReg), ARG(typename Config::AsmReg), ARG(u32))
73 };
74
75 // (value); might allocate register
76 {
77 a.gval_expr_as_reg(ARG(typename T::GenericValuePart &))
78 } -> std::same_as<typename Config::AsmReg>;
79
80 {
81 a.select_fixed_assignment_reg(ARG(AssignmentPartRef),
82 ARG(typename T::IRValueRef))
83 } -> std::same_as<typename Config::AsmReg>;
84
85
86 // mostly implementor stuff
87
88 { a.cur_func_may_emit_calls() } -> std::convertible_to<bool>;
89
90 /// Provides the personality function for the current function or
91 /// an invalid SymRef otherwise.
92 { a.cur_personality_func() } -> std::same_as<SymRef>;
93
94 /// Provides a calling convention assigner for the current function.
95 /// Optional, if not implemented, the default C calling convention will be
96 /// used.
97 { a.cur_cc_assigner() } -> std::convertible_to<CCAssigner *>;
98
99 {
100 a.try_force_fixed_assignment(ARG(typename T::IRValueRef))
101 } -> std::convertible_to<bool>;
102
103 { a.val_parts(ARG(typename T::IRValueRef)) } -> ValueParts;
104
105 /// A compiler can provide a data structure that is a non-assignment ValueRef.
106 /// This struct is used in a union, so it must be a standard-layout struct and
107 /// have "uint8_t mode;" as first member, which must be >= 4.
108 requires ValRefSpecialStruct<typename T::ValRefSpecial>;
109
110 /// Provides the implementation to return special ValueRefs, e.g. for
111 /// constants or globals.
112 {
113 a.val_ref_special(ARG(typename T::IRValueRef))
114 } -> std::same_as<std::optional<typename T::ValRefSpecial>>;
115
116 /// Provides the implementation to construct a ValuePartRef from a
117 /// ValRefSpecial.
118 {
119 a.val_part_ref_special(ARG(typename T::ValRefSpecial &), ARG(u32))
120 } -> std::same_as<typename T::ValuePart>;
121
122 /// The compiler numbers functions and this gives the derived implementation
123 /// a chance to save that mapping
124 ///
125 /// (func_ref, idx)
126 { a.define_func_idx(ARG(typename T::IRFuncRef), ARG(u32)) };
127
128 /// When the default variable reference handling is disabled, the
129 /// implementation has to provide a few helpers to tell us what to do
130 requires IsTrue<Config::DEFAULT_VAR_REF_HANDLING> || requires {
131 /// Called when starting to compile a func.
132 /// This may initialize variable reference assignments
133 /// which should be live at the beginning of the function
134 { a.setup_var_ref_assignments() };
135
136 /// Load the address of a variable reference referred to
137 /// by the AssignmentPartRef into a register
138 // (dst_reg, ap_ref)
139 {
140 a.load_address_of_var_reference(ARG(typename Config::AsmReg),
141 ARG(AssignmentPartRef))
142 };
143 };
144
145 {
146 a.compile_inst(ARG(typename T::IRInstRef), ARG(typename T::InstRange))
147 } -> std::convertible_to<bool>;
148};
149
150} // namespace tpde