37 GLSLGenerator(
const std::vector<Token>& tokens,
int num_inputs,
38 int num_intermediate_inputs,
int width,
int height,
40 const std::map<std::pair<int, std::string>,
int>& prop_map,
43 [[nodiscard]] std::string
generate();
46 const std::vector<Token>& tokens;
48 int num_intermediate_inputs;
50 const std::map<std::pair<int, std::string>,
int>& prop_map;
53 std::ostringstream out;
57 std::vector<std::string> stack;
63 std::set<std::string> user_variables;
66 std::map<std::string, int> arrays;
69 std::map<int, std::vector<std::string>> block_entry_stack;
74 std::vector<int> header_stack;
76 bool operator==(
const CanKey&)
const =
default;
80 [[nodiscard]]
size_t operator()(
const CanKey& key)
const noexcept {
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));
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));
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;
105 bool debug_structurize_cfg =
false;
107 void debugEmitCfgComment();
109 int break_flag_counter = 0;
110 std::map<int, std::string> loop_break_flags;
112 void emit(
const std::string& code);
113 void emitLine(
const std::string& code);
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();
130 std::vector<int> header_stack;
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;
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);
144 bool structured_exit_enabled =
false;
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();
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()) {
160 if (structurize.isLoopHeader(block) &&
161 !isLoopHeaderActive(block, loop_ctx)) {
163 auto it = structurize.loop_follow.find(block);
164 if (it != structurize.loop_follow.end()) {
167 if (!visitor.handleLoop(block, follow, loop_ctx)) {
174 if (visited_in_region.contains(block)) {
177 visited_in_region.insert(block);
179 if (!visitor.visitBlock(block)) {
183 auto succ = cfg_blocks[block].successors;
185 if (succ.size() == 2 && succ[0] == succ[1]) {
190 return visitor.handleNoSuccessors(block);
193 if (succ.size() == 1) {
195 int current_header = loop_ctx.header_stack.empty()
197 : loop_ctx.header_stack.back();
198 if (current_header != -1) {
200 auto it = structurize.loop_follow.find(current_header);
201 if (it != structurize.loop_follow.end()) {
204 if (next == current_header || next == follow) {
205 return visitor.handleLoopExitOrContinue(
206 block, next, current_header, follow);
208 if (!structurize.inLoop(current_header, next)) {
209 return visitor.handleNonlocalEdge(block, next,
210 stop_block, loop_ctx);
213 if (next == stop_block) {
217 if (!visitor.handleSimpleEdge(block, next)) {
224 if (succ.size() == 2) {
230 int join = computeBranchJoin(t, f, stop_block);
232 if (!visitor.handleBranch(block, t, f, join, stop_block,
237 int current_header = loop_ctx.header_stack.empty()
239 : loop_ctx.header_stack.back();
240 if (current_header != -1) {
242 auto it = structurize.loop_follow.find(current_header);
243 if (it != structurize.loop_follow.end()) {
246 if (join == stop_block && stop_block == follow) {
247 return visitor.handleLoopBreak(join);
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);
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;
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;
276 void processToken(
const Token& token);
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;
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);
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,
298 [[nodiscard]] std::string emitPixelLoad(
int clip_idx,
const std::string& x,
299 const std::string& y,
301 [[nodiscard]] std::string emitPixelIndex(
const std::string& x,
302 const std::string& y);