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