VapourSynth-llvmexpr
Loading...
Searching...
No Matches
GLSLGenerator.hpp
Go to the documentation of this file.
1
19
20#ifndef LLVMEXPR_CODEGEN_LLVM_GLSL_GENERATOR_HPP
21#define LLVMEXPR_CODEGEN_LLVM_GLSL_GENERATOR_HPP
22
25
26#include <map>
27#include <optional>
28#include <set>
29#include <sstream>
30#include <string>
31#include <unordered_map>
32#include <unordered_set>
33#include <vector>
34
36 public:
37 GLSLGenerator(const std::vector<Token>& tokens, int num_inputs,
38 int num_intermediate_inputs, int width, int height,
39 bool mirror_boundary,
40 const std::map<std::pair<int, std::string>, int>& prop_map,
41 const analysis::ExpressionAnalysisResults& analysis_results);
42
43 [[nodiscard]] std::string generate();
44
45 private:
46 const std::vector<Token>& tokens;
47 int num_inputs;
48 int num_intermediate_inputs;
49 bool mirror_boundary;
50 const std::map<std::pair<int, std::string>, int>& prop_map;
52
53 std::ostringstream out;
54
55 int indent_level = 0;
56
57 std::vector<std::string> stack;
58
59 int temp_counter = 0;
60
61 int slot_counter = 0;
62
63 std::set<std::string> user_variables;
64
65 // Array name -> static size
66 std::map<std::string, int> arrays;
67
68 // Block -> entry stack variable names (for merge points)
69 std::map<int, std::vector<std::string>> block_entry_stack;
70
71 struct CanKey {
72 int start_block = -1;
73 int stop_block = -1;
74 std::vector<int> header_stack;
75
76 bool operator==(const CanKey&) const = default;
77 };
78
79 struct CanKeyHash {
80 [[nodiscard]] size_t operator()(const CanKey& key) const noexcept {
81 // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
82 constexpr size_t HASH_MAGIC = 0x9e3779b97f4a7c15ULL;
83 auto hash_combine = [](size_t seed, size_t v) -> size_t {
84 return seed ^ (v + HASH_MAGIC + (seed << 6) + (seed >> 2));
85 };
86 // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
87
88 size_t h = 0;
89 h = hash_combine(h, std::hash<int>{}(key.start_block));
90 h = hash_combine(h, std::hash<int>{}(key.stop_block));
91 h = hash_combine(h, std::hash<size_t>{}(key.header_stack.size()));
92 for (int x : key.header_stack) {
93 h = hash_combine(h, std::hash<int>{}(x));
94 }
95 return h;
96 }
97 };
98
99 mutable std::unordered_map<CanKey, bool, CanKeyHash> can_structure_cache;
100 mutable std::unordered_set<CanKey, CanKeyHash> can_structure_in_progress;
101 mutable std::unordered_map<CanKey, bool, CanKeyHash> can_edge_cache;
102 mutable std::unordered_set<CanKey, CanKeyHash> can_edge_in_progress;
103
104#ifndef NDEBUG
105 bool debug_structurize_cfg = false;
106#endif
107 void debugEmitCfgComment();
108
109 int break_flag_counter = 0;
110 std::map<int, std::string> loop_break_flags; // loop header -> bool var
111
112 void emit(const std::string& code);
113 void emitLine(const std::string& code);
114 void emitNewline();
115 void indent();
116 void dedent();
117
118 void emitHeader();
119 void emitBufferDeclarations();
120 void emitHelperFunctions();
121 void emitVariableDeclarations();
122 void emitMainFunction();
123 void emitMainFunctionStateMachine();
124 void emitMainFunctionStructured();
125 void emitBlockCode(int block_idx);
126 void emitSetResultAndExit(const std::string& result_expr);
127 void emitResultEpilogueStore();
128
129 struct LoopContext {
130 std::vector<int> header_stack;
131 };
132
133 [[nodiscard]] const std::vector<analysis::CFGBlock>&
134 getCodegenCfgBlocks() const;
135 [[nodiscard]] const std::vector<int>& getCodegenStackDepthIn() const;
136 [[nodiscard]] int computeBranchJoin(int t, int f, int stop_block) const;
137
138 [[nodiscard]] std::string getLoopBreakFlag(int header);
139 [[nodiscard]] std::optional<int>
140 findEnclosingLoopForFollow(int target_block,
141 const LoopContext& loop_ctx) const;
142 void emitUnwindBreakIfNeeded(const LoopContext& loop_ctx);
143
144 bool structured_exit_enabled = false;
145
146 template <typename Visitor>
147 bool traverseStructure(int start_block, int stop_block,
148 LoopContext& loop_ctx, Visitor& visitor) const {
149 const auto& cfg_blocks = getCodegenCfgBlocks();
150 const auto& structurize = analysis.getStructurizeCFGResult();
151
152 std::set<int> visited_in_region;
153 int block = start_block;
154 while (block != stop_block) {
155 if (block < 0 || static_cast<size_t>(block) >= cfg_blocks.size()) {
156 return false;
157 }
158
159 // Loop header handling
160 if (structurize.isLoopHeader(block) &&
161 !isLoopHeaderActive(block, loop_ctx)) {
162 int follow = -1;
163 auto it = structurize.loop_follow.find(block);
164 if (it != structurize.loop_follow.end()) {
165 follow = it->second;
166 }
167 if (!visitor.handleLoop(block, follow, loop_ctx)) {
168 return false;
169 }
170 block = follow;
171 continue;
172 }
173
174 if (visited_in_region.contains(block)) {
175 return false;
176 }
177 visited_in_region.insert(block);
178
179 if (!visitor.visitBlock(block)) {
180 return false;
181 }
182
183 auto succ = cfg_blocks[block].successors;
184 // Treat duplicated successors as a single edge.
185 if (succ.size() == 2 && succ[0] == succ[1]) {
186 succ.pop_back();
187 }
188
189 if (succ.empty()) {
190 return visitor.handleNoSuccessors(block);
191 }
192
193 if (succ.size() == 1) {
194 int next = succ[0];
195 int current_header = loop_ctx.header_stack.empty()
196 ? -1
197 : loop_ctx.header_stack.back();
198 if (current_header != -1) {
199 int follow = -1;
200 auto it = structurize.loop_follow.find(current_header);
201 if (it != structurize.loop_follow.end()) {
202 follow = it->second;
203 }
204 if (next == current_header || next == follow) {
205 return visitor.handleLoopExitOrContinue(
206 block, next, current_header, follow);
207 }
208 if (!structurize.inLoop(current_header, next)) {
209 return visitor.handleNonlocalEdge(block, next,
210 stop_block, loop_ctx);
211 }
212 }
213 if (next == stop_block) {
214 return true;
215 }
216
217 if (!visitor.handleSimpleEdge(block, next)) {
218 return false;
219 }
220 block = next;
221 continue;
222 }
223
224 if (succ.size() == 2) {
225 int t = succ[0];
226 int f = succ[1];
227 // Conditional goto:
228 // if (cond > 0) goto t;
229 // fallthrough continues at f.
230 int join = computeBranchJoin(t, f, stop_block);
231
232 if (!visitor.handleBranch(block, t, f, join, stop_block,
233 loop_ctx)) {
234 return false;
235 }
236
237 int current_header = loop_ctx.header_stack.empty()
238 ? -1
239 : loop_ctx.header_stack.back();
240 if (current_header != -1) {
241 int follow = -1;
242 auto it = structurize.loop_follow.find(current_header);
243 if (it != structurize.loop_follow.end()) {
244 follow = it->second;
245 }
246 if (join == stop_block && stop_block == follow) {
247 return visitor.handleLoopBreak(join);
248 }
249 }
250
251 block = join;
252 continue;
253 }
254
255 return false;
256 }
257 return true;
258 }
259
260 void emitStructuredFrom(int start_block, int stop_block,
261 LoopContext& loop_ctx, bool& ok);
262 void emitEdgeToBlock(int target_block, int stop_block,
263 LoopContext& loop_ctx, bool& ok);
264 void emitStackToEntrySlots(int target_block);
265
266 [[nodiscard]] bool canStructureFrom(int start_block, int stop_block,
267 LoopContext& loop_ctx) const;
268 [[nodiscard]] bool canEdgeToBlock(int target_block, int stop_block,
269 LoopContext& loop_ctx) const;
270
271 [[nodiscard]] int lcaPostdom(int a, int b,
272 const std::vector<int>& ipdom) const;
273 [[nodiscard]] bool isLoopHeaderActive(int header,
274 const LoopContext& loop_ctx) const;
275
276 void processToken(const Token& token);
277
278 [[nodiscard]] std::string newTemp();
279 [[nodiscard]] std::string newSlot();
280 [[nodiscard]] std::string pop();
281 void push(const std::string& val);
282 [[nodiscard]] std::string peek(int offset = 0) const;
283
284 [[nodiscard]] static std::string floatLiteral(double val);
285 [[nodiscard]] std::string binaryOp(const std::string& op);
286 [[nodiscard]] std::string binaryCmp(const std::string& op);
287 [[nodiscard]] std::string unaryFn(const std::string& fn);
288 [[nodiscard]] std::string binaryFn(const std::string& fn);
289
290 [[nodiscard]] std::string emitClampCoord(const std::string& coord,
291 const std::string& max_dim);
292 [[nodiscard]] std::string emitMirrorCoord(const std::string& coord,
293 const std::string& max_dim);
294 [[nodiscard]] std::string emitFinalCoord(const std::string& coord,
295 const std::string& max_dim,
296 bool use_mirror);
297
298 [[nodiscard]] std::string emitPixelLoad(int clip_idx, const std::string& x,
299 const std::string& y,
300 bool use_mirror);
301 [[nodiscard]] std::string emitPixelIndex(const std::string& x,
302 const std::string& y);
303};
304
305#endif // LLVMEXPR_CODEGEN_LLVM_GLSL_GENERATOR_HPP
std::string generate()
GLSLGenerator(const std::vector< Token > &tokens, int num_inputs, int num_intermediate_inputs, int width, int height, bool mirror_boundary, const std::map< std::pair< int, std::string >, int > &prop_map, const analysis::ExpressionAnalysisResults &analysis_results)