hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
IROptimizer.cpp
Go to the documentation of this file.
1//
2// Created by Jerry Chou on 10/2/2024.
3//
4
5#include "IROptimizer.hpp"
7#include "compiler/ir/IR.h"
9#include "share/def.hpp"
10
11#include <cmath>
12#include <cstdint>
13#include <memory>
14#include <queue>
15
16namespace yoi {
18 const std::set<yoi::indexT> &instructions)
19 : codeBlockIndex(codeBlockIndex), instructions(instructions) {}
20
22 const std::set<yoi::indexT> &instructions,
23 bool optimizable)
24 : codeBlockIndex(codeBlockIndex), instructions(instructions), optimizable(optimizable) {}
25
31
34 ContributedInstructionSet result{*this};
35 for (auto &i : other.instructions) {
36 result.insert(i);
37 }
38 result.optimizable = result.optimizable && other.optimizable;
39 return result;
40 }
41
43
45
49
53
59
64
69
71
73
75
76 IRFunctionOptimizer::SimulationStack::Item::PossibleValue::PossibleValue(yoi::indexT stringConstIndex) : stringConstIndex(stringConstIndex) {}
77
79
81
82 void IRFunctionOptimizer::SimulationStack::push(const std::shared_ptr<IRValueType> &type,
84 items.emplace_back(Item{type, false, {}, {}, contributedInstructions});
85 }
86
87 void IRFunctionOptimizer::SimulationStack::push(const std::shared_ptr<IRValueType> &type,
89 Item::PossibleValue value) {
90 items.emplace_back(Item{type, true, value, {}, contributedInstructions});
91 }
92
94 items.push_back(item);
95 }
96
100
102 assert(index < items.size());
103 return items[items.size() - 1 - index];
104 }
105
107 yoi::indexT currentIndex) {
108
109 // std::cout << "IROptimizer::reduce() called" << std::endl;s
111 return currentIndex;
112 }
113 for (auto i : contributedInstructions) {
114 targetFunction->codeBlock[contributedInstructions.codeBlockIndex]->getIRArray()[i] = {
115 IR::Opcode::nop, {}, targetFunction->codeBlock[contributedInstructions.codeBlockIndex]->getIRArray()[i].debugInfo};
116 // If the current code block index is greater than the index of the instruction being processed,
117 // decrement it to account for the removal of the instruction.
118 /*
119 if (currentIndex > i) {
120 currentIndex--;
121 }
122 */
123 }
124 return currentIndex;
125 }
126
130 switch (a.type->type) {
132 return {a.type, true, {a.possibleValue.intValue + b.possibleValue.intValue}, {}};
133 }
135 return {a.type, true, {a.possibleValue.unsignedValue + b.possibleValue.unsignedValue}, {}};
136 }
138 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue + b.possibleValue.shortValue)}, {}};
139 }
141 return {a.type, true, {a.possibleValue.deciValue + b.possibleValue.deciValue}, {}};
142 }
144 return {a.type, true, {a.possibleValue.boolValue ? true : false}, {}};
145 }
147 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue + b.possibleValue.charValue)}, {}};
148 }
149 default:
150 panic(0, 0, "IROptimizer::add(): Unsupported type for add operation");
151 return {};
152 }
153 } else {
154 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
155 }
156 }
157
161 switch (a.type->type) {
163 return {a.type, true, {a.possibleValue.intValue - b.possibleValue.intValue}, {}, {}};
164 }
166 return {a.type, true, {a.possibleValue.unsignedValue - b.possibleValue.unsignedValue}, {}, {}};
167 }
169 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue - b.possibleValue.shortValue)}, {}, {}};
170 }
172 return {a.type, true, {a.possibleValue.deciValue - b.possibleValue.deciValue}, {}, {}};
173 }
175 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}};
176 }
178 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue - b.possibleValue.charValue)}, {}, {}};
179 }
180 default:
181 panic(0, 0, "IROptimizer::sub(): Unsupported type for sub operation");
182 return {};
183 }
184 } else {
185 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
186 }
187 }
188
192 switch (a.type->type) {
194 return {a.type, true, {a.possibleValue.intValue * b.possibleValue.intValue}, {}, {}};
195 }
197 return {a.type, true, {a.possibleValue.unsignedValue * b.possibleValue.unsignedValue}, {}, {}};
198 }
200 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue * b.possibleValue.shortValue)}, {}, {}};
201 }
203 return {a.type, true, {a.possibleValue.deciValue * b.possibleValue.deciValue}, {}, {}};
204 }
206 return {a.type, true, {a.possibleValue.boolValue ? b.possibleValue.boolValue : false}, {}, {}};
207 }
209 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue * b.possibleValue.charValue)}, {}, {}};
210 }
211 default:
212 panic(0, 0, "IROptimizer::mul(): Unsupported type for mul operation");
213 return {};
214 }
215 } else {
216 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
217 }
218 }
219
223 switch (a.type->type) {
225 return {a.type, true, {a.possibleValue.intValue / b.possibleValue.intValue}, {}, {}};
226 }
228 return {a.type, true, {a.possibleValue.unsignedValue / b.possibleValue.unsignedValue}, {}, {}};
229 }
231 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue / b.possibleValue.shortValue)}, {}, {}};
232 }
234 return {a.type, true, {a.possibleValue.deciValue / b.possibleValue.deciValue}, {}, {}};
235 }
237 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}, {}};
238 }
240 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue / b.possibleValue.charValue)}, {}, {}};
241 }
242 default:
243 panic(0, 0, "IROptimizer::div(): Unsupported type for div operation");
244 return {};
245 }
246 } else {
247 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
248 }
249 }
250
254 switch (a.type->type) {
256 return {a.type, true, {a.possibleValue.intValue % b.possibleValue.intValue}, {}, {}};
257 }
259 return {a.type, true, {a.possibleValue.unsignedValue % b.possibleValue.unsignedValue}, {}, {}};
260 }
262 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue % b.possibleValue.shortValue)}, {}, {}};
263 }
265 return {a.type, true, {std::fmod(a.possibleValue.deciValue, b.possibleValue.deciValue)}, {}, {}};
266 }
268 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}, {}};
269 }
271 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue % b.possibleValue.charValue)}, {}, {}};
272 }
273 default:
274 panic(0, 0, "IROptimizer::mod(): Unsupported type for mod operation");
275 return {};
276 }
277 } else {
278 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
279 }
280 }
281
283 if (a.hasPossibleValue) {
284 switch (a.type->type) {
286 return {a.type, true, {-a.possibleValue.intValue}, {}, {}};
287 }
289 return {a.type, true, {static_cast<uint64_t>(-a.possibleValue.unsignedValue)}, {}, {}};
290 }
292 return {a.type, true, {static_cast<short>(-a.possibleValue.shortValue)}, {}, {}};
293 }
295 return {a.type, true, {-a.possibleValue.deciValue}, {}, {}};
296 }
298 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}, {}};
299 }
301 return {a.type, true, char{static_cast<char>(-a.possibleValue.charValue)}, {}, {}};
302 }
303 default:
304 panic(0, 0, "IROptimizer::negate(): Unsupported type for negate operation");
305 return {};
306 }
307 } else {
308 return {a.type, false, {}, {}, a.contributedInstructions};
309 }
310 }
311
313 if (a.hasPossibleValue) {
314 switch (a.type->type) {
316 return {a.type, true, {~a.possibleValue.intValue}, {}, {}};
317 }
319 return {a.type, true, {~a.possibleValue.unsignedValue}, {}, {}};
320 }
322 return {a.type, true, {static_cast<short>(~a.possibleValue.shortValue)}, {}, {}};
323 }
325 return {a.type, true, {~int64_t(a.possibleValue.deciValue)}, {}, {}};
326 }
328 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}, {}};
329 }
331 return {a.type, true, char{static_cast<char>(~a.possibleValue.charValue)}, {}, {}};
332 }
333 default:
334 panic(0, 0, "IROptimizer::bitwiseNot(): Unsupported type for bitwise not operation");
335 return {};
336 }
337 } else {
338 return {a.type, false, {}, {}, a.contributedInstructions};
339 }
340 }
341
345 switch (a.type->type) {
347 return {a.type, true, {a.possibleValue.intValue & b.possibleValue.intValue}, {}, {}};
348 }
350 return {a.type, true, {a.possibleValue.unsignedValue & b.possibleValue.unsignedValue}, {}, {}};
351 }
353 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue & b.possibleValue.shortValue)}, {}, {}};
354 }
356 return {a.type, true, {int64_t(a.possibleValue.deciValue) & int64_t(b.possibleValue.deciValue)}, {}, {}};
357 }
359 return {a.type, true, {a.possibleValue.boolValue ? b.possibleValue.boolValue : false}, {}, {}};
360 }
362 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue & b.possibleValue.charValue)}, {}, {}};
363 }
364 default:
365 panic(0, 0, "IROptimizer::bitwiseAnd(): Unsupported type for bitwise and operation");
366 return {};
367 }
368 } else {
369 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
370 }
371 }
372
376 switch (a.type->type) {
378 return {a.type, true, {a.possibleValue.intValue | b.possibleValue.intValue}, {}, {}};
379 }
381 return {a.type, true, {a.possibleValue.unsignedValue | b.possibleValue.unsignedValue}, {}, {}};
382 }
384 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue | b.possibleValue.shortValue)}, {}, {}};
385 }
387 return {a.type, true, {int64_t(a.possibleValue.deciValue) | int64_t(b.possibleValue.deciValue)}, {}, {}};
388 }
390 return {a.type, true, {a.possibleValue.boolValue ? true : b.possibleValue.boolValue}, {}, {}};
391 }
393 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue | b.possibleValue.charValue)}, {}, {}};
394 }
395 default:
396 panic(0, 0, "IROptimizer::bitwiseOr(): Unsupported type for bitwise or operation");
397 return {};
398 }
399 } else {
400 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
401 }
402 }
403
407 switch (a.type->type) {
409 return {a.type, true, {a.possibleValue.intValue ^ b.possibleValue.intValue}, {}, {}};
410 }
412 return {a.type, true, {a.possibleValue.unsignedValue ^ b.possibleValue.unsignedValue}, {}, {}};
413 }
415 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue ^ b.possibleValue.shortValue)}, {}, {}};
416 }
418 return {a.type, true, {int64_t(a.possibleValue.deciValue) ^ int64_t(b.possibleValue.deciValue)}, {}, {}};
419 }
421 return {a.type, true, {a.possibleValue.boolValue ? b.possibleValue.boolValue : true}, {}, {}};
422 }
424 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue ^ b.possibleValue.charValue)}, {}, {}};
425 }
426 default:
427 panic(0, 0, "IROptimizer::bitwiseXor(): Unsupported type for bitwise xor operation");
428 return {};
429 }
430 } else {
431 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
432 }
433 }
434
438 switch (a.type->type) {
440 return {a.type, true, {a.possibleValue.intValue << b.possibleValue.intValue}, {}, {}};
441 }
443 return {a.type, true, {a.possibleValue.unsignedValue << b.possibleValue.intValue}, {}, {}};
444 }
446 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue << b.possibleValue.intValue)}, {}, {}};
447 }
449 return {a.type, true, {int64_t(a.possibleValue.deciValue) << b.possibleValue.intValue}, {}, {}};
450 }
452 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}, {}};
453 }
455 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue << b.possibleValue.intValue)}, {}, {}};
456 }
457 default:
458 panic(0, 0, "IROptimizer::bitwiseShiftLeft(): Unsupported type for bitwise shift left operation");
459 return {};
460 }
461 } else {
462 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
463 }
464 }
465
469 switch (a.type->type) {
471 return {a.type, true, {a.possibleValue.intValue >> b.possibleValue.intValue}, {}, {}};
472 }
474 return {a.type, true, {a.possibleValue.unsignedValue >> b.possibleValue.intValue}, {}, {}};
475 }
477 return {a.type, true, {static_cast<short>(a.possibleValue.shortValue >> b.possibleValue.intValue)}, {}, {}};
478 }
480 return {a.type, true, {int64_t(a.possibleValue.deciValue) >> b.possibleValue.intValue}, {}, {}};
481 }
483 return {a.type, true, {a.possibleValue.boolValue ? false : true}, {}};
484 }
486 return {a.type, true, char{static_cast<char>(a.possibleValue.charValue >> b.possibleValue.intValue)}, {}, {}};
487 }
488 default:
489 panic(0, 0, "IROptimizer::bitwiseShiftRight(): Unsupported type for bitwise shift right operation");
490 return {};
491 }
492 } else {
493 return {a.type, false, {}, {}, a.contributedInstructions + b.contributedInstructions};
494 }
495 }
496
498 auto &IRArr = targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray();
499 switch (item.type->type) {
501 IRArr.insert(IRArr.begin() + index + 1,
502 IR{IR::Opcode::push_integer,
503 {IROperand{IROperand::operandType::integer, {item.possibleValue.intValue}}},
504 IRArr[index].debugInfo});
505 break;
507 IRArr.insert(IRArr.begin() + index + 1,
508 IR{IR::Opcode::push_unsigned,
509 {{IROperand::operandType::unsignedInt, IROperand::operandValue{item.possibleValue.unsignedValue}}},
510 IRArr[index].debugInfo});
511 break;
513 IRArr.insert(IRArr.begin() + index + 1,
514 IR{IR::Opcode::push_short,
515 {{IROperand::operandType::shortInt, IROperand::operandValue{static_cast<int64_t>(item.possibleValue.shortValue)}}},
516 IRArr[index].debugInfo});
517 break;
519 IRArr.insert(IRArr.begin() + index + 1,
520 IR{IR::Opcode::push_decimal,
521 {IROperand{IROperand::operandType::decimal, {item.possibleValue.deciValue}}},
522 IRArr[index].debugInfo});
523 break;
525 IRArr.insert(IRArr.begin() + index + 1,
526 IR{IR::Opcode::push_boolean,
527 {{IROperand::operandType::boolean, IROperand::operandValue{item.possibleValue.boolValue}}},
528 IRArr[index].debugInfo});
529 break;
531 IRArr.insert(IRArr.begin() + index + 1,
532 IR{IR::Opcode::push_string,
533 {{IROperand::operandType::index, IROperand::operandValue{irModule->identifier}},
534 {IROperand::operandType::stringLiteral, IROperand::operandValue{item.possibleValue.stringConstIndex}}},
535 IRArr[index].debugInfo});
536 break;
538 IRArr.insert(IRArr.begin() + index + 1,
539 IR{IR::Opcode::push_character,
540 {{IROperand::operandType::character, IROperand::operandValue{static_cast<int64_t>(item.possibleValue.charValue)}}},
541 IRArr[index].debugInfo});
542 break;
543 default:
544 panic(0, 0, "IROptimizer::generatePushOp: Unsupported value type for push constant operation");
545 break;
546 }
547 // simulationStack.push()IRArr.insert(IRArr.begin() + index, IR
548 // IR traverse will handle the simulation stack by executing the push operation
549 // simulationStack.push({item.type, true, item.possibleValue, {currentCodeBlockIndex, {index}}});
550 return index;
551 }
552
553 IRFunctionOptimizer::IRFunctionOptimizer(const std::shared_ptr<compilerContext> &compilerCtx,
554 const std::shared_ptr<IRModule> &irModule,
555 std::map<CallGraph::FuncIdentifier, FunctionAnalysisInfo> &globalResults)
557
558 IRFunctionOptimizer &IRFunctionOptimizer::setTargetFunction(const std::shared_ptr<IRFunctionDefinition> &targetFunction,
560 this->targetFunction = targetFunction;
561 this->currentFuncId = funcId;
563 return *this;
564 }
565
567 if (item.hasPossibleValue && right.hasPossibleValue) {
568 switch (item.type->type) {
570 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue < right.possibleValue.intValue}, {}};
571 }
573 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue < right.possibleValue.unsignedValue}, {}};
574 }
576 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue < right.possibleValue.shortValue}, {}};
577 }
579 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue < right.possibleValue.deciValue}, {}};
580 }
582 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue < right.possibleValue.boolValue}, {}};
583 }
585 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue < right.possibleValue.charValue}, {}};
586 }
587 default:
588 panic(0, 0, "IROptimizer::lessThan(): Unsupported type for less than operation");
589 return {};
590 }
591 } else {
592 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
593 }
594 }
595
597 const SimulationStack::Item &right) {
598 if (item.hasPossibleValue && right.hasPossibleValue) {
599 switch (item.type->type) {
601 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue <= right.possibleValue.intValue}, {}};
602 }
604 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue <= right.possibleValue.unsignedValue}, {}};
605 }
607 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue <= right.possibleValue.shortValue}, {}};
608 }
610 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue <= right.possibleValue.deciValue}, {}};
611 }
613 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue <= right.possibleValue.boolValue}, {}};
614 }
616 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue <= right.possibleValue.charValue}, {}};
617 }
618 default:
619 panic(0, 0, "IROptimizer::lessThanOrEqual(): Unsupported type for less than or equal operation");
620 return {};
621 }
622 } else {
623 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
624 }
625 }
626
628 const SimulationStack::Item &right) {
629 if (item.hasPossibleValue && right.hasPossibleValue) {
630 switch (item.type->type) {
632 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue > right.possibleValue.intValue}, {}};
633 }
635 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue > right.possibleValue.unsignedValue}, {}};
636 }
638 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue > right.possibleValue.shortValue}, {}};
639 }
641 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue > right.possibleValue.deciValue}, {}};
642 }
644 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue > right.possibleValue.boolValue}, {}};
645 }
647 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue > right.possibleValue.charValue}, {}};
648 }
649 default:
650 panic(0, 0, "IROptimizer::greaterThan(): Unsupported type for greater than operation");
651 return {};
652 }
653 } else {
654 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
655 }
656 }
657
659 const SimulationStack::Item &right) {
660 if (item.hasPossibleValue && right.hasPossibleValue) {
661 switch (item.type->type) {
663 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue >= right.possibleValue.intValue}, {}};
664 }
666 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue >= right.possibleValue.unsignedValue}, {}};
667 }
669 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue >= right.possibleValue.shortValue}, {}};
670 }
672 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue >= right.possibleValue.deciValue}, {}};
673 }
675 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue >= right.possibleValue.boolValue}, {}};
676 }
678 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue >= right.possibleValue.charValue}, {}};
679 }
680 default:
681 panic(0, 0, "IROptimizer::greaterThanOrEqual(): Unsupported type for greater than or equal operation");
682 return {};
683 }
684 } else {
685 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
686 }
687 }
688
690 if (item.hasPossibleValue && right.hasPossibleValue) {
691 switch (item.type->type) {
693 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue == right.possibleValue.intValue}, {}};
694 }
696 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue == right.possibleValue.unsignedValue}, {}};
697 }
699 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue == right.possibleValue.shortValue}, {}};
700 }
702 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue == right.possibleValue.deciValue}, {}};
703 }
705 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue == right.possibleValue.boolValue}, {}};
706 }
708 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue == right.possibleValue.charValue}, {}};
709 }
710 default:
711 panic(0, 0, "IROptimizer::equal(): Unsupported type for equal operation");
712 return {};
713 }
714 } else {
715 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
716 }
717 }
718
720 if (item.hasPossibleValue && right.hasPossibleValue) {
721 switch (item.type->type) {
723 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.intValue != right.possibleValue.intValue}, {}};
724 }
726 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.unsignedValue != right.possibleValue.unsignedValue}, {}};
727 }
729 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.shortValue != right.possibleValue.shortValue}, {}};
730 }
732 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.deciValue != right.possibleValue.deciValue}, {}};
733 }
735 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.boolValue != right.possibleValue.boolValue}, {}};
736 }
738 return {compilerCtx->getBoolObjectType(), true, {item.possibleValue.charValue != right.possibleValue.charValue}, {}};
739 }
740 default:
741 panic(0, 0, "IROptimizer::notEqual(): Unsupported type for not equal operation");
742 return {};
743 }
744 } else {
745 return {compilerCtx->getBoolObjectType(), false, {}, {}, item.contributedInstructions + right.contributedInstructions};
746 }
747 }
748
750 for (yoi::indexT insIndex = 0; insIndex < targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().size(); ++insIndex) {
751 auto &ins = targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray()[insIndex];
752 // std::cout << "ins " << insIndex << " " << wstring2string(ins.to_string()) << std::endl;
753 // std::cout << currentCodeBlockIndex << " " << insIndex << " " << wstring2string(ins.to_string()) <<
754 // std::endl;
755 switch (ins.opcode) {
757 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.boolean);
758 break;
759 }
761 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.integer);
762 break;
763 }
765 simulationStack.push(compilerCtx->getDeciObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.decimal);
766 break;
767 }
769 simulationStack.push(compilerCtx->getShortObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.shortV);
770 break;
771 }
773 simulationStack.push(compilerCtx->getUnsignedObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.unsignedV);
774 break;
775 }
777 simulationStack.push(compilerCtx->getStrObjectType(), {currentCodeBlockIndex, {insIndex}});
778 break;
779 }
781 simulationStack.push(
782 compilerCtx->getCharObjectType(), {currentCodeBlockIndex, {insIndex}}, static_cast<char>(ins.operands[0].value.character));
783 break;
784 }
786 auto value = simulationStack.peek(0);
787 simulationStack.pop();
788 if (value.hasPossibleValue) {
789 switch (value.type->type) {
791 value.possibleValue.charValue = static_cast<char>(value.possibleValue.deciValue);
792 break;
794 value.possibleValue.charValue = value.possibleValue.boolValue ? 1 : 0;
795 break;
797 value.possibleValue.charValue = static_cast<char>(value.possibleValue.intValue);
798 break;
800 value.possibleValue.charValue = static_cast<char>(value.possibleValue.shortValue);
801 break;
803 value.possibleValue.charValue = static_cast<char>(value.possibleValue.unsignedValue);
804 break;
805 default:
806 break;
807 }
808 value.type = compilerCtx->getCharObjectType();
809 insIndex = reduce(value.contributedInstructions, insIndex);
810 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
811 insIndex = generatePushOp(value, insIndex);
812 } else {
813 simulationStack.push(compilerCtx->getCharObjectType(),
814 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
815 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
816 }
817 break;
818 }
820 auto value = simulationStack.peek(0);
821 simulationStack.pop();
822 // judge whether this is evaluable
823 if (value.hasPossibleValue) {
824 switch (value.type->type) {
826 value.possibleValue.boolValue = value.possibleValue.intValue != 0;
827 break;
829 value.possibleValue.boolValue = value.possibleValue.deciValue != 0.0;
830 break;
832 value.possibleValue.boolValue = value.possibleValue.charValue != 0;
833 break;
835 value.possibleValue.boolValue = value.possibleValue.shortValue != 0;
836 break;
838 value.possibleValue.boolValue = value.possibleValue.unsignedValue != 0;
839 break;
840 default:
841 break;
842 }
843 value.type = compilerCtx->getBoolObjectType();
844 insIndex = reduce(value.contributedInstructions, insIndex);
845 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
846 insIndex = generatePushOp(value, insIndex);
847 } else {
848 simulationStack.push(compilerCtx->getBoolObjectType(),
849 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
850 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
851 }
852 break;
853 }
855 auto value = simulationStack.peek(0);
856 simulationStack.pop();
857 if (value.hasPossibleValue) {
858 switch (value.type->type) {
860 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.deciValue);
861 break;
863 value.possibleValue.intValue = value.possibleValue.boolValue ? 1 : 0;
864 break;
866 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.charValue);
867 break;
869 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.shortValue);
870 break;
872 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.unsignedValue);
873 break;
874 default:
875 break;
876 }
877 value.type = compilerCtx->getIntObjectType();
878 insIndex = reduce(value.contributedInstructions, insIndex);
879 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
880 insIndex = generatePushOp(value, insIndex);
881 } else {
882 simulationStack.push(compilerCtx->getIntObjectType(),
883 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
884 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
885 }
886 break;
887 }
889 auto value = simulationStack.peek(0);
890 simulationStack.pop();
891 // std::cout << "simulate basic_cast_deci " << value.hasPossibleValue << std::endl;
892 if (value.hasPossibleValue) {
893 switch (value.type->type) {
895 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.intValue);
896 break;
898 value.possibleValue.deciValue = value.possibleValue.boolValue ? 1.0 : 0.0;
899 break;
901 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.charValue);
902 break;
904 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.unsignedValue);
905 break;
907 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.shortValue);
908 break;
909 default:
910 break;
911 }
912 value.type = compilerCtx->getDeciObjectType();
913 insIndex = reduce(value.contributedInstructions, insIndex);
914 ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
915 insIndex = generatePushOp(value, insIndex);
916 } else {
917 simulationStack.push(compilerCtx->getDeciObjectType(),
918 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
919 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
920 }
921 break;
922 }
924 auto value = simulationStack.peek(0);
925 simulationStack.pop();
926 // std::cout << "simulate basic_cast_deci " << value.hasPossibleValue << std::endl;
927 if (value.hasPossibleValue) {
928 switch (value.type->type) {
930 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.intValue);
931 break;
933 value.possibleValue.shortValue = value.possibleValue.boolValue;
934 break;
936 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.charValue);
937 break;
939 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.unsignedValue);
940 break;
942 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.deciValue);
943 break;
944 default:
945 break;
946 }
947 value.type = compilerCtx->getShortObjectType();
948 insIndex = reduce(value.contributedInstructions, insIndex);
949 ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
950 insIndex = generatePushOp(value, insIndex);
951 } else {
952 simulationStack.push(compilerCtx->getShortObjectType(),
953 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
954 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
955 }
956 break;
957 }
959 auto value = simulationStack.peek(0);
960 simulationStack.pop();
961 // std::cout << "simulate basic_cast_deci " << value.hasPossibleValue << std::endl;
962 if (value.hasPossibleValue) {
963 switch (value.type->type) {
965 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.intValue);
966 break;
968 value.possibleValue.unsignedValue = value.possibleValue.boolValue;
969 break;
971 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.charValue);
972 break;
974 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.shortValue);
975 break;
977 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.deciValue);
978 break;
979 default:
980 break;
981 }
982 value.type = compilerCtx->getUnsignedObjectType();
983 insIndex = reduce(value.contributedInstructions, insIndex);
984 ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
985 insIndex = generatePushOp(value, insIndex);
986 } else {
987 simulationStack.push(compilerCtx->getUnsignedObjectType(),
988 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
989 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
990 }
991 break;
992 }
993 case IR::Opcode::add: {
994 auto right = simulationStack.peek(0);
995 auto left = simulationStack.peek(1);
996 simulationStack.pop();
997 simulationStack.pop();
998 // simulate
999 auto result = add(left, right);
1000 // std::cout << "simulate add " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
1001 // result.hasPossibleValue << std::endl;
1002 if (result.hasPossibleValue) {
1003 insIndex = reduce(left.contributedInstructions, insIndex);
1004 insIndex = reduce(right.contributedInstructions, insIndex);
1005 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1006 insIndex = generatePushOp(result, insIndex);
1007 } else {
1008 // lost information, push back
1009 simulationStack.push(
1010 result.type,
1011 left.contributedInstructions + right.contributedInstructions +
1012 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1013 }
1014 break;
1015 }
1016 case IR::Opcode::sub: {
1017 auto right = simulationStack.peek(0);
1018 auto left = simulationStack.peek(1);
1019 simulationStack.pop();
1020 simulationStack.pop();
1021 // simulate
1022 auto result = sub(left, right);
1023 // std::cout << "simulate sub " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
1024 // result.hasPossibleValue << std::endl;
1025 if (result.hasPossibleValue) {
1026 insIndex = reduce(left.contributedInstructions, insIndex);
1027 insIndex = reduce(right.contributedInstructions, insIndex);
1028 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1029 insIndex = generatePushOp(result, insIndex);
1030 } else {
1031 // lost information, push back
1032 simulationStack.push(
1033 result.type,
1034 left.contributedInstructions + right.contributedInstructions +
1035 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1036 }
1037 break;
1038 }
1039 case IR::Opcode::mul: {
1040 auto right = simulationStack.peek(0);
1041 auto left = simulationStack.peek(1);
1042 simulationStack.pop();
1043 simulationStack.pop();
1044 // simulate
1045 auto result = mul(left, right);
1046 // std::cout << "simulate mul " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
1047 // result.hasPossibleValue << std::endl;
1048 if (result.hasPossibleValue) {
1049 insIndex = reduce(left.contributedInstructions, insIndex);
1050 insIndex = reduce(right.contributedInstructions, insIndex);
1051 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1052 insIndex = generatePushOp(result, insIndex);
1053 } else {
1054 // lost information, push back
1055 simulationStack.push(
1056 result.type,
1057 left.contributedInstructions + right.contributedInstructions +
1058 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1059 }
1060 break;
1061 }
1062 case IR::Opcode::div: {
1063 auto right = simulationStack.peek(0);
1064 auto left = simulationStack.peek(1);
1065 simulationStack.pop();
1066 simulationStack.pop();
1067 // simulate
1068 auto result = div(left, right);
1069 // std::cout << "simulate div " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
1070 // result.hasPossibleValue << std::endl;
1071 if (result.hasPossibleValue) {
1072 insIndex = reduce(left.contributedInstructions, insIndex);
1073 insIndex = reduce(right.contributedInstructions, insIndex);
1074 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1075 insIndex = generatePushOp(result, insIndex);
1076 } else {
1077 // lost information, push back
1078 simulationStack.push(
1079 result.type,
1080 left.contributedInstructions + right.contributedInstructions +
1081 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1082 }
1083 break;
1084 }
1085 case IR::Opcode::mod: {
1086 auto right = simulationStack.peek(0);
1087 auto left = simulationStack.peek(1);
1088 simulationStack.pop();
1089 simulationStack.pop();
1090 // simulate
1091 auto result = mod(left, right);
1092 if (result.hasPossibleValue) {
1093 insIndex = reduce(left.contributedInstructions, insIndex);
1094 insIndex = reduce(right.contributedInstructions, insIndex);
1095 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1096 insIndex = generatePushOp(result, insIndex);
1097 } else {
1098 // lost information, push back
1099 simulationStack.push(
1100 result.type,
1101 left.contributedInstructions + right.contributedInstructions +
1102 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1103 }
1104 break;
1105 }
1106 case IR::Opcode::negate: {
1107 auto value = simulationStack.peek(0);
1108 simulationStack.pop();
1109 // simulate
1110 auto result = negate(value);
1111 if (result.hasPossibleValue) {
1112 insIndex = reduce(value.contributedInstructions, insIndex);
1113 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1114 insIndex = generatePushOp(result, insIndex);
1115 } else {
1116 // lost information, push back
1117 simulationStack.push(result.type,
1118 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
1119 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1120 }
1121 break;
1122 }
1124 auto right = simulationStack.peek(0);
1125 auto left = simulationStack.peek(1);
1126 simulationStack.pop();
1127 simulationStack.pop();
1128 // simulate
1129 auto result = bitwiseAnd(left, right);
1130 if (result.hasPossibleValue) {
1131 insIndex = reduce(left.contributedInstructions, insIndex);
1132 insIndex = reduce(right.contributedInstructions, insIndex);
1133 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1134 insIndex = generatePushOp(result, insIndex);
1135 } else {
1136 // lost information, push back
1137 simulationStack.push(
1138 result.type,
1139 left.contributedInstructions + right.contributedInstructions +
1140 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1141 }
1142 break;
1143 }
1145 auto right = simulationStack.peek(0);
1146 auto left = simulationStack.peek(1);
1147 simulationStack.pop();
1148 simulationStack.pop();
1149 // simulate
1150 auto result = bitwiseOr(left, right);
1151 if (result.hasPossibleValue) {
1152 insIndex = reduce(left.contributedInstructions, insIndex);
1153 insIndex = reduce(right.contributedInstructions, insIndex);
1154 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1155 insIndex = generatePushOp(result, insIndex);
1156 } else {
1157 // lost information, push back
1158 simulationStack.push(
1159 result.type,
1160 left.contributedInstructions + right.contributedInstructions +
1161 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1162 }
1163 break;
1164 }
1166 auto right = simulationStack.peek(0);
1167 auto left = simulationStack.peek(1);
1168 simulationStack.pop();
1169 simulationStack.pop();
1170 // simulate
1171 auto result = bitwiseXor(left, right);
1172 if (result.hasPossibleValue) {
1173 insIndex = reduce(left.contributedInstructions, insIndex);
1174 insIndex = reduce(right.contributedInstructions, insIndex);
1175 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1176 insIndex = generatePushOp(result, insIndex);
1177 } else {
1178 // lost information, push back
1179 simulationStack.push(
1180 result.type,
1181 left.contributedInstructions + right.contributedInstructions +
1182 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1183 }
1184 break;
1185 }
1187 auto value = simulationStack.peek(0);
1188 simulationStack.pop();
1189 // simulate
1190 auto result = bitwiseNot(value);
1191 if (result.hasPossibleValue) {
1192 insIndex = reduce(value.contributedInstructions, insIndex);
1193 insIndex = generatePushOp(result, insIndex);
1194 } else {
1195 // lost information, push back
1196 simulationStack.push(result.type,
1197 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
1198 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1199 }
1200 break;
1201 }
1203 auto shift = simulationStack.peek(0);
1204 auto value = simulationStack.peek(1);
1205 simulationStack.pop();
1206 simulationStack.pop();
1207 // simulate
1208 auto result = bitwiseShiftLeft(value, shift);
1209 if (result.hasPossibleValue) {
1210 insIndex = reduce(value.contributedInstructions, insIndex);
1211 insIndex = reduce(shift.contributedInstructions, insIndex);
1212 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1213 insIndex = generatePushOp(result, insIndex);
1214 } else {
1215 // lost information, push back
1216 simulationStack.push(
1217 result.type,
1218 value.contributedInstructions + shift.contributedInstructions +
1219 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1220 }
1221 break;
1222 }
1224 auto shift = simulationStack.peek(0);
1225 auto value = simulationStack.peek(1);
1226 simulationStack.pop();
1227 simulationStack.pop();
1228 // simulate
1229 auto result = bitwiseShiftRight(value, shift);
1230 if (result.hasPossibleValue) {
1231 insIndex = reduce(value.contributedInstructions, insIndex);
1232 insIndex = reduce(shift.contributedInstructions, insIndex);
1233 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1234 insIndex = generatePushOp(result, insIndex);
1235 } else {
1236 // lost information, push back
1237 simulationStack.push(
1238 result.type,
1239 value.contributedInstructions + shift.contributedInstructions +
1240 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1241 }
1242 break;
1243 }
1244 case IR::Opcode::less_than: {
1245 auto right = simulationStack.peek(0);
1246 auto left = simulationStack.peek(1);
1247 simulationStack.pop();
1248 simulationStack.pop();
1249 // simulate
1250 auto result = lessThan(left, right);
1251 if (result.hasPossibleValue) {
1252 insIndex = reduce(left.contributedInstructions, insIndex);
1253 insIndex = reduce(right.contributedInstructions, insIndex);
1254 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1255 insIndex = generatePushOp(result, insIndex);
1256 } else {
1257 // lost information, push back
1258 simulationStack.push(
1259 result.type,
1260 left.contributedInstructions + right.contributedInstructions +
1261 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1262 }
1263 break;
1264 }
1266 auto right = simulationStack.peek(0);
1267 auto left = simulationStack.peek(1);
1268 simulationStack.pop();
1269 simulationStack.pop();
1270 // simulate
1271 auto result = greaterThan(left, right);
1272 if (result.hasPossibleValue) {
1273 insIndex = reduce(left.contributedInstructions, insIndex);
1274 insIndex = reduce(right.contributedInstructions, insIndex);
1275 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1276 insIndex = generatePushOp(result, insIndex);
1277 } else {
1278 // lost information, push back
1279 simulationStack.push(
1280 result.type,
1281 left.contributedInstructions + right.contributedInstructions +
1282 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1283 }
1284 break;
1285 }
1287 auto right = simulationStack.peek(0);
1288 auto left = simulationStack.peek(1);
1289 simulationStack.pop();
1290 simulationStack.pop();
1291 // simulate
1292 auto result = greaterThanOrEqual(left, right);
1293 if (result.hasPossibleValue) {
1294 insIndex = reduce(left.contributedInstructions, insIndex);
1295 insIndex = reduce(right.contributedInstructions, insIndex);
1296 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1297 insIndex = generatePushOp(result, insIndex);
1298 } else {
1299 // lost information, push back
1300 simulationStack.push(
1301 result.type,
1302 left.contributedInstructions + right.contributedInstructions +
1303 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1304 }
1305 break;
1306 }
1308 auto right = simulationStack.peek(0);
1309 auto left = simulationStack.peek(1);
1310 simulationStack.pop();
1311 simulationStack.pop();
1312 // simulate
1313 auto result = greaterThanOrEqual(left, right);
1314 if (result.hasPossibleValue) {
1315 insIndex = reduce(left.contributedInstructions, insIndex);
1316 insIndex = reduce(right.contributedInstructions, insIndex);
1317 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1318 insIndex = generatePushOp(result, insIndex);
1319 } else {
1320 // lost information, push back
1321 simulationStack.push(
1322 result.type,
1323 left.contributedInstructions + right.contributedInstructions +
1324 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1325 }
1326 break;
1327 }
1328 case IR::Opcode::equal: {
1329 auto right = simulationStack.peek(0);
1330 auto left = simulationStack.peek(1);
1331 simulationStack.pop();
1332 simulationStack.pop();
1333 // simulate
1334 auto result = equal(left, right);
1335 if (result.hasPossibleValue) {
1336 insIndex = reduce(left.contributedInstructions, insIndex);
1337 insIndex = reduce(right.contributedInstructions, insIndex);
1338 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1339 insIndex = generatePushOp(result, insIndex);
1340 } else {
1341 // lost information, push back
1342 simulationStack.push(
1343 result.type,
1344 left.contributedInstructions + right.contributedInstructions +
1345 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1346 }
1347 break;
1348 }
1349 case IR::Opcode::not_equal: {
1350 auto right = simulationStack.peek(0);
1351 auto left = simulationStack.peek(1);
1352 simulationStack.pop();
1353 simulationStack.pop();
1354 // simulate
1355 auto result = notEqual(left, right);
1356 if (result.hasPossibleValue) {
1357 insIndex = reduce(left.contributedInstructions, insIndex);
1358 insIndex = reduce(right.contributedInstructions, insIndex);
1359 ins = ins = IR{IR::Opcode::nop, {}, ins.debugInfo};
1360 insIndex = generatePushOp(result, insIndex);
1361 } else {
1362 // lost information, push back
1363 simulationStack.push(
1364 result.type,
1365 left.contributedInstructions + right.contributedInstructions +
1366 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
1367 }
1368 break;
1369 }
1371 if (auto it = variablesExtraInfo.find(ins.operands[0].value.symbolIndex); it != variablesExtraInfo.end()) {
1372 // if exists, use the extra information
1373 if (it->second.hasPossibleValue && it->second.possibleValue.contributedInstructions.codeBlockIndex == currentCodeBlockIndex) {
1374 // inherit the possible value onto the stack
1375 simulationStack.push(it->second.possibleValue.type,
1376 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
1377 it->second.isReadAfterStore = false;
1378 } else {
1379 // if we can't guess the value, we can't optimize it
1380 // find the local variable definition
1381 auto type = targetFunction->getVariableTable().get(ins.operands[0].value.symbolIndex);
1382 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}});
1383 it->second.isReadAfterStore = true;
1384 }
1385 } else {
1386 // well there does be a possibility that the variable will be not initialized, and this is
1387 // parameter passing in this case, we can't optimize it
1388 auto type = targetFunction->getVariableTable().get(ins.operands[0].value.symbolIndex);
1389 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}});
1390 variablesExtraInfo[ins.operands[0].value.symbolIndex] = {false, true, {}};
1391 }
1392 break;
1393 }
1395 // store the value to the variable
1396 // if this is the twice or more times, we can ignore the previous store command and use the new
1397 // value directly
1398
1399 // check the definition type and value type here
1400 auto definitionType = targetFunction->getVariableTable().get(ins.operands[0].value.symbolIndex);
1401 auto value = simulationStack.peek(0);
1402
1403 if (value.type->isForeignBasicType()) {
1404 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
1405 }
1406
1407 if (*definitionType != *value.type && value.type->type == IRValueType::valueType::null) {
1408 // type mismatch, panic
1409 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::reduceRedundantConstantExpr(): store_local: type mismatch");
1410 }
1411
1412 simulationStack.pop();
1413 if (auto it = variablesExtraInfo.find(ins.operands[0].value.symbolIndex);
1414 it != variablesExtraInfo.end() && it->second.hasPossibleValue && !it->second.isReadAfterStore &&
1415 it->second.possibleValue.contributedInstructions.codeBlockIndex == currentCodeBlockIndex) {
1416 // reduce previous redundant store
1417 insIndex = reduce(it->second.possibleValue.contributedInstructions, insIndex);
1418 variablesExtraInfo[ins.operands[0].value.symbolIndex] = {value.hasPossibleValue, false, value};
1419 } else {
1420 value.contributedInstructions =
1421 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}};
1422 variablesExtraInfo[ins.operands[0].value.symbolIndex] = {value.hasPossibleValue, false, value};
1423 }
1424 break;
1425 }
1427 // as for global variables, we can't optimize it
1428 auto moduleIndex = ins.operands[0].value.symbolIndex;
1429 auto type = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
1430 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}});
1431 break;
1432 }
1434 // check the definition type and value type here
1435 auto moduleIndex = ins.operands[0].value.symbolIndex;
1436 auto definitionType = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
1437 auto value = simulationStack.peek(0);
1438
1439 if (value.type->isForeignBasicType()) {
1440 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
1441 }
1442
1443 if (*definitionType != *value.type && value.type->type == IRValueType::valueType::null &&
1445 // type mismatch, panic
1446 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::reduceRedundantConstantExpr(): store_global: type mismatch");
1447 }
1448
1449 simulationStack.pop();
1450 break;
1451 }
1453 // we can't optimize it
1454 auto value = simulationStack.peek(0);
1455 auto type = value.type->typeIndex;
1456 auto targetModule = compilerCtx->getImportedModule(value.type->typeAffiliateModule);
1457 auto structDef = targetModule->structTable[type];
1458 auto memberIndex = ins.operands[0].value.symbolIndex;
1459 auto memberDef = managedPtr(*structDef->fieldTypes[memberIndex]);
1460 simulationStack.pop();
1461 simulationStack.push(memberDef,
1462 value.contributedInstructions +
1463 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
1464 break;
1465 }
1467 // we can't optimize it
1468 auto value = simulationStack.peek(1);
1469 auto type = simulationStack.peek(0).type->typeIndex;
1470 auto structDef = compilerCtx->getImportedModule(simulationStack.peek(0).type->typeAffiliateModule)->structTable[type];
1471 auto memberIndex = ins.operands[0].value.symbolIndex;
1472 auto memberDef = structDef->fieldTypes[memberIndex];
1473
1474 if (value.type->isForeignBasicType()) {
1475 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
1476 }
1477
1478 if (*memberDef != *value.type && value.type->type != IRValueType::valueType::pointerObject &&
1479 value.type->type != IRValueType::valueType::null) {
1480 // type mismatch, panic
1481 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::reduceRedundantConstantExpr(): store_member: type mismatch");
1482 }
1483
1484 simulationStack.pop();
1485 simulationStack.pop();
1486 break;
1487 }
1489 case IR::Opcode::invoke: {
1490 // we can't optimize it
1491 // in case of which this got optimized in tempVar reduction, we set optimizable flag to false
1492 auto moduleIndex = ins.operands[0].value.symbolIndex;
1493 auto function = compilerCtx->getImportedModule(moduleIndex)->functionTable[ins.operands[1].value.symbolIndex];
1494 auto returnType = managedPtr((*function->returnType).removeAttribute(IRValueType::ValueAttr::Borrow));
1495 auto argTypes = function->argumentTypes;
1496 auto argCount = function->argumentTypes.size();
1497 SimulationStack::Item::ContributedInstructionSet contributedInstructions = {currentCodeBlockIndex, {insIndex}, false};
1498 for (int i = 0; i < argCount; i++) {
1499 contributedInstructions = contributedInstructions + simulationStack.peek(0).contributedInstructions;
1500 simulationStack.pop();
1501 }
1502 simulationStack.push(returnType, contributedInstructions);
1503 break;
1504 }
1506 auto function = compilerCtx->getIRFFITable()
1507 ->importedLibraries[ins.operands[0].value.symbolIndex]
1508 .importedFunctionTable[ins.operands[1].value.symbolIndex];
1509 auto returnType = function->returnType;
1510 auto argTypes = function->argumentTypes;
1511 auto argCount = function->argumentTypes.size();
1512 for (int i = 0; i < argCount; i++) {
1513 simulationStack.pop();
1514 }
1515 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
1516 break;
1517 }
1519 auto argCount = ins.operands[3].value.symbolIndex;
1520 for (int i = 0; i < argCount - 1; i++) {
1521 simulationStack.pop();
1522 }
1523 auto returnType = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
1524 ->interfaceTable[ins.operands[1].value.symbolIndex]
1525 ->methodMap[ins.operands[2].value.symbolIndex]
1526 ->returnType;
1527 returnType = managedPtr((*returnType).removeAttribute(IRValueType::ValueAttr::Borrow));
1528 simulationStack.pop();
1529 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
1530 break;
1531 }
1533 auto moduleIndex = ins.operands[0].value.symbolIndex;
1534 auto structDef = compilerCtx->getImportedModule(moduleIndex)->structTable[ins.operands[1].value.symbolIndex];
1535 simulationStack.push(
1536 managedPtr(IRValueType{IRValueType::valueType::structObject, moduleIndex, ins.operands[1].value.symbolIndex}),
1537 {currentCodeBlockIndex, {insIndex}, false});
1538 break;
1539 }
1541 auto moduleIndex = ins.operands[0].value.symbolIndex;
1542 auto interfaceImplDef =
1543 compilerCtx->getImportedModule(moduleIndex)->interfaceImplementationTable[ins.operands[1].value.symbolIndex];
1544 auto returnType = managedPtr(IRValueType{IRValueType::valueType::interfaceObject,
1545 interfaceImplDef->implInterfaceIndex.first,
1546 interfaceImplDef->implInterfaceIndex.second});
1547 simulationStack.pop();
1548 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
1549 break;
1550 }
1552 auto condition = simulationStack.peek(0);
1553 simulationStack.pop();
1554 if (condition.hasPossibleValue) {
1555 if (condition.possibleValue.boolValue) {
1556 // if the condition is true, we can jump to the target block directly
1557 // reduce redundant condition
1558 insIndex = reduce(condition.contributedInstructions, insIndex);
1559 ins.opcode = IR::Opcode::jump;
1560 } else {
1561 // if the condition is false, we can ignore the jump instruction
1562 insIndex = reduce(condition.contributedInstructions, insIndex);
1563 insIndex = reduce(SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}}, insIndex);
1564 insIndex -= 1; // reduce the index by 1, to make sure we don't skip the next instruction,
1565 // since we have reduced the instruction before
1566 }
1567 } else {
1568 // if we can't guess the value, we can't optimize it, thus we do nothing
1569 }
1570 break;
1571 }
1573 auto condition = simulationStack.peek(0);
1574 simulationStack.pop();
1575 if (condition.hasPossibleValue) {
1576 if (!condition.possibleValue.boolValue) {
1577 // if the condition is false, we can jump to the target block directly
1578 // reduce redundant condition
1579 insIndex = reduce(condition.contributedInstructions, insIndex);
1580 ins.opcode = IR::Opcode::jump;
1581 } else {
1582 // if the condition is true, we can ignore the jump instruction
1583 insIndex = reduce(condition.contributedInstructions, insIndex);
1584 insIndex = reduce(SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}}, insIndex);
1585 insIndex -= 1; // reduce the index by 1, to make sure we don't skip the next instruction,
1586 // since we have reduced the instruction before
1587 }
1588 } else {
1589 // if we can't guess the value, we can't optimize it, thus we do nothing
1590 }
1591 break;
1592 }
1593 case IR::Opcode::ret: {
1594 // just pop the return value
1595 simulationStack.pop();
1596 break;
1597 }
1605 // we can't optimize it
1606 // dims in operands
1607 std::shared_ptr<IRValueType> baseType;
1608 switch (ins.opcode) {
1610 baseType = compilerCtx->getIntObjectType();
1611 break;
1613 baseType = compilerCtx->getBoolObjectType();
1614 break;
1616 baseType = compilerCtx->getCharObjectType();
1617 break;
1619 baseType = compilerCtx->getDeciObjectType();
1620 break;
1622 baseType = compilerCtx->getStrObjectType();
1623 break;
1625 baseType = compilerCtx->getShortObjectType();
1626 break;
1628 baseType = compilerCtx->getUnsignedObjectType();
1629 break;
1630 default:
1631 break;
1632 }
1633
1634 yoi::indexT size = 1;
1636
1637 for (auto i = 1; i < ins.operands.size(); i++) {
1638 size *= ins.operands[i].value.symbolIndex;
1639 dims.push_back(ins.operands[i].value.symbolIndex);
1640 }
1641 for (yoi::indexT i = 0; i < ins.operands[0].value.symbolIndex; i++) {
1642 simulationStack.pop();
1643 }
1644 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
1645 break;
1646 }
1649 auto moduleIndex = ins.operands[0].value.symbolIndex;
1650 auto typeIndex = ins.operands[1].value.symbolIndex;
1651 yoi::indexT size = 1;
1653
1656 moduleIndex,
1657 typeIndex});
1658
1659 for (yoi::indexT i = 3; i < ins.operands.size(); i++) {
1660 size *= ins.operands[i].value.symbolIndex;
1661 dims.push_back(ins.operands[i].value.symbolIndex);
1662 }
1663 for (yoi::indexT i = 0; i < ins.operands[2].value.symbolIndex; i++) {
1664 simulationStack.pop();
1665 }
1666 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
1667 break;
1668 }
1670 // we can't optimize it
1671 auto index = simulationStack.peek(0);
1672 auto array = simulationStack.peek(1);
1673 simulationStack.pop();
1674 simulationStack.pop();
1675 simulationStack.push(managedPtr(array.type->getElementType()),
1676 array.contributedInstructions + index.contributedInstructions +
1677 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
1678 break;
1679 }
1681 // we can't optimize it
1682 auto index = simulationStack.peek(0);
1683 auto value = simulationStack.peek(1);
1684 auto array = simulationStack.peek(2);
1685 simulationStack.pop();
1686 simulationStack.pop();
1687 simulationStack.pop();
1688 break;
1689 }
1691 auto rhs = simulationStack.peek(0);
1692 auto lhs = simulationStack.peek(1);
1693 simulationStack.pop();
1694 simulationStack.pop();
1695 simulationStack.push(lhs.type,
1696 lhs.contributedInstructions + rhs.contributedInstructions +
1697 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
1698 break;
1699 }
1701 auto array = simulationStack.peek(0);
1702 simulationStack.pop();
1703 if (array.type->isArrayType()) {
1704 yoi::indexT size = 1;
1705 for (auto &dim : array.type->dimensions) {
1706 size *= dim;
1707 }
1708 // reduce
1709 insIndex = reduce(array.contributedInstructions, insIndex);
1710 // generate push op
1711 ins = {IR::Opcode::nop, {}, ins.debugInfo};
1712 insIndex = generatePushOp({compilerCtx->getIntObjectType(), true, static_cast<int64_t>(size)}, insIndex);
1713 break;
1714 } else if (array.type->isDynamicArrayType()) {
1715 // not even optimizable
1716 simulationStack.push(compilerCtx->getIntObjectType(),
1717 array.contributedInstructions +
1718 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
1719 break;
1720 }
1721 }
1722 case IR::Opcode::pop: {
1723 auto rhs = simulationStack.peek(0);
1724 if (rhs.contributedInstructions.optimizable) {
1725 insIndex = reduce(rhs.contributedInstructions, insIndex);
1726 ins = {IR::Opcode::nop, {}, ins.debugInfo};
1727 }
1728 simulationStack.pop();
1729 break;
1730 }
1732 auto type = managedPtr(IRValueType{static_cast<IRValueType::valueType>(ins.operands[0].value.symbolIndex),
1733 ins.operands[1].value.symbolIndex,
1734 ins.operands[2].value.symbolIndex,
1735 ins.operands[3].value.symbolIndex
1736 ? yoi::vec<yoi::indexT>{ins.operands[3].value.symbolIndex}
1738 simulationStack.pop();
1739 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
1740 break;
1741 }
1743 simulationStack.pop();
1744 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, false});
1745 break;
1746 }
1747 case IR::Opcode::push_null: {
1748 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, true});
1749 break;
1750 }
1760 std::shared_ptr<IRValueType> baseType;
1761 switch (ins.opcode) {
1763 baseType = compilerCtx->getIntObjectType();
1764 break;
1766 baseType = compilerCtx->getBoolObjectType();
1767 break;
1769 baseType = compilerCtx->getCharObjectType();
1770 break;
1772 baseType = compilerCtx->getDeciObjectType();
1773 break;
1775 baseType = compilerCtx->getShortObjectType();
1776 break;
1778 baseType = compilerCtx->getUnsignedObjectType();
1779 break;
1781 baseType = compilerCtx->getStrObjectType();
1782 break;
1784 baseType = managedPtr(IRValueType{
1785 IRValueType::valueType::interfaceObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
1786 break;
1788 baseType = managedPtr(IRValueType{
1789 IRValueType::valueType::structObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
1790 break;
1791 default:
1792 break;
1793 }
1794
1795 for (yoi::indexT i = 0; i < ins.operands.back().value.symbolIndex; i++) {
1796 simulationStack.pop();
1797 }
1798 simulationStack.push(managedPtr(baseType->getDynamicArrayType()), {currentCodeBlockIndex, {insIndex}, false});
1799 break;
1800 }
1802 // pop two values and push one boolean value
1803 auto interfaceType = simulationStack.peek(0).type;
1804 auto objectType = simulationStack.peek(1).type;
1805 simulationStack.pop();
1806 simulationStack.pop();
1807 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}, false});
1808 break;
1809 }
1811 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}, false});
1812 break;
1813 }
1814 default: {
1815 handleInstruction(ins, insIndex, currentCodeBlockIndex);
1816 break;
1817 }
1818 }
1819 }
1820 return *this;
1821 }
1822
1824 for (auto &i : simulationStack.items) {
1825 reduce(i.contributedInstructions, 0);
1826 }
1827 return *this;
1828 }
1829
1831 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
1832 auto &codeBlock = targetFunction->codeBlock[i]->getIRArray();
1833 std::erase_if(codeBlock, [](const IR &ins) { return ins.opcode == IR::Opcode::nop; });
1834 }
1835 return *this;
1836 }
1837
1839 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
1840 auto &codeBlock = targetFunction->codeBlock[i]->getIRArray();
1841 if (codeBlock.empty()) {
1842 continue;
1843 }
1844 // if the above instruction of current jump is a jump, we can ignore the current jump
1845 for (auto it = codeBlock.begin() + 1; it != codeBlock.end();) {
1846 if ((it - 1)->opcode == IR::Opcode::jump && it->opcode == IR::Opcode::jump) {
1847 it = codeBlock.erase(it);
1848 } else {
1849 it++;
1850 }
1851 }
1852 }
1853 return *this;
1854 }
1855
1857 auto it = std::find_if(targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().begin(),
1858 targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().end(),
1859 [&](const IR &ins) { return ins.opcode == IR::Opcode::ret; });
1860 if (it != targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().end()) {
1861 // if the last instruction is a ret, we can ignore all the instructions after it
1862 targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().erase(
1863 it + 1, targetFunction->codeBlock[currentCodeBlockIndex]->getIRArray().end());
1864 }
1865 return *this;
1866 }
1867
1869 std::map<yoi::indexT, std::vector<indexT>> G; // graph
1870 std::map<yoi::indexT, std::vector<indexT>> reverseG; // record the predecessors of each block
1871 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
1872 G[i] = {};
1873 if (not reverseG.contains(i))
1874 reverseG[i] = {};
1875 for (auto &ins : targetFunction->codeBlock[i]->getIRArray()) {
1876 switch (ins.opcode) {
1877 case IR::Opcode::jump:
1880 G[i].push_back(ins.operands[0].value.codeBlockIndex);
1881 reverseG[ins.operands[0].value.codeBlockIndex].push_back(i);
1882 break;
1883 default:
1884 break;
1885 }
1886 }
1887 }
1888 // remove the blocks that have no predecessors
1889 for (auto it = reverseG.begin(); it != reverseG.end(); it++) {
1890 if (it->second.empty() && it->first != 0) {
1891 targetFunction->codeBlock[it->first]->getIRArray() = {};
1892 // do not erase it cuz we need to keep the index of the block
1893 }
1894 }
1895 // identify the out block
1896 for (auto it = G.begin(); it != G.end(); it++) {
1897 if (it->second.empty()) {
1898 auto &targetBlock = targetFunction->codeBlock[it->first];
1899 // if the block has no successor, it's the out block
1900 if (targetBlock->getIRArray().empty()) {
1901 if (reverseG[it->first].empty()) {
1902 // no predecessor, and no successor, and no instructions, it's empty block, do nothing
1903 } else {
1904 // has predecessor, but no successor, it's the out block but with empty instructions
1905 if (targetFunction->returnType->type == IRValueType::valueType::none) {
1906 targetBlock->getIRArray().push_back(IR{IR::Opcode::ret_none, {}, targetFunction->debugInfo});
1908 // do nothing
1909
1911 targetBlock->getIRArray().push_back(
1914 targetFunction->debugInfo});
1915 targetBlock->getIRArray().push_back(IR{IR::Opcode::ret, {}, targetFunction->debugInfo});
1916 } else {
1917 panic(targetFunction->debugInfo.line,
1918 targetFunction->debugInfo.column,
1919 "IROptimizer::controlFlowOptimization(): function " + wstring2string(targetFunction->name) +
1920 " has no return instruction in out block");
1921 }
1922 }
1923 continue;
1924 }
1925 if (targetBlock->getIRArray().back().opcode != IR::Opcode::ret && targetBlock->getIRArray().back().opcode != IR::Opcode::ret_none) {
1926 // there's no return instruction, add a ret instruction at the end of the block if it returns none
1927 if (targetFunction->returnType->type == IRValueType::valueType::none) {
1928 targetBlock->getIRArray().push_back(IR{IR::Opcode::ret_none, {}, targetBlock->getIRArray().back().debugInfo});
1930 // do nothing
1932 targetBlock->getIRArray().push_back(
1935 targetBlock->getIRArray().back().debugInfo});
1936 targetBlock->getIRArray().push_back(IR{IR::Opcode::ret, {}, targetBlock->getIRArray().back().debugInfo});
1937 } else {
1938 panic(targetFunction->debugInfo.line,
1939 targetFunction->debugInfo.column,
1940 "IROptimizer::controlFlowOptimization(): function " + wstring2string(targetFunction->name) +
1941 " has no return instruction in out block");
1942 }
1943 }
1944 }
1945 }
1946 return *this;
1947 }
1948
1950 if (targetFunction->codeBlock.empty())
1951 return *this;
1952
1953 std::map<indexT, std::vector<indexT>> successors;
1954 std::map<indexT, std::vector<indexT>> predecessors;
1955 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
1956 if (successors.find(i) == successors.end())
1957 successors[i] = {};
1958 for (auto &ins : targetFunction->codeBlock[i]->getIRArray()) {
1959 switch (ins.opcode) {
1960 case IR::Opcode::jump: {
1961 indexT target = ins.operands[0].value.codeBlockIndex;
1962 successors[i].push_back(target);
1963 predecessors[target].push_back(i);
1964 break;
1965 }
1968 indexT target = ins.operands[0].value.codeBlockIndex;
1969 successors[i].push_back(target); // Branch target
1970 predecessors[target].push_back(i);
1971 break;
1972 }
1973 case IR::Opcode::ret:
1975 // This block is a graph sink.
1976 break;
1977 default:
1978 break;
1979 }
1980 }
1981 // Add implicit fallthrough for non-terminating blocks
1982 if (!targetFunction->codeBlock[i]->getIRArray().empty()) {
1983 auto &lastIns = targetFunction->codeBlock[i]->getIRArray().back();
1984 bool isTerminator =
1985 (lastIns.opcode == IR::Opcode::jump || lastIns.opcode == IR::Opcode::jump_if_false ||
1986 lastIns.opcode == IR::Opcode::jump_if_true || lastIns.opcode == IR::Opcode::ret || lastIns.opcode == IR::Opcode::ret_none);
1987
1988 if (!isTerminator && (i + 1 < targetFunction->codeBlock.size())) {
1989 successors[i].push_back(i + 1);
1990 predecessors[i + 1].push_back(i);
1991 }
1992 }
1993 }
1994
1995 // data flow analyse
1996 std::map<indexT, AnalysisState> blockInStates;
1997 std::map<indexT, AnalysisState> blockOutStates;
1998 std::queue<indexT> worklist;
1999
2000 // start with the entry block.
2001 worklist.push(0);
2002 blockInStates[0] = AnalysisState{}; // Entry state is empty.
2003
2004 while (!worklist.empty()) {
2005 indexT currentBlockIdx = worklist.front();
2006 worklist.pop();
2007
2008 // merge predecessors' out-states to get the in-state for the current block.
2009 AnalysisState inState;
2010 if (predecessors.count(currentBlockIdx) > 0) {
2011 for (indexT predIdx : predecessors[currentBlockIdx]) {
2012 // Get the last calculated out-state of the predecessor.
2013 inState = mergeStates(inState, blockOutStates[predIdx]);
2014 }
2015 }
2016 blockInStates[currentBlockIdx] = inState;
2017
2018 // analyze the current block to get its new out-state.
2019 AnalysisState newOutState = analyzeBlock(currentBlockIdx, inState);
2020
2021 // if the out-state has changed, we need to re-process successors.
2022 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
2023 blockOutStates[currentBlockIdx] = newOutState;
2024 if (successors.count(currentBlockIdx) > 0) {
2025 for (indexT succIdx : successors[currentBlockIdx]) {
2026 worklist.push(succIdx);
2027 }
2028 }
2029 }
2030 }
2031
2032 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
2033 // printf(wstring2string(targetFunction->to_string(0)).c_str());
2034 if (blockInStates.count(i) > 0) { // Only transform reachable blocks
2035 transformBlock(i, blockInStates[i]);
2036 } else {
2037 // unreachable, clear it.
2038 // printf("Removing unreachable block %d\n", i);
2039 targetFunction->codeBlock[i]->getIRArray().clear();
2040 }
2041 }
2042
2045 this->performNullableCheck();
2046 this->performRawCheck();
2048 return *this;
2049 }
2050
2052 const auto originalSize = targetFunction->codeBlock.size();
2053 if (originalSize == 0) {
2054 return *this;
2055 }
2056
2057 // identify which blocks to keep and create an old-to-new index mapping
2058 std::map<indexT, indexT> oldToNewMap;
2059 std::vector<bool> isBlockKept(originalSize);
2060 indexT newIndexCounter = 0;
2061 for (indexT oldIndex = 0; oldIndex < originalSize; ++oldIndex) {
2062 if (!targetFunction->codeBlock[oldIndex]->getIRArray().empty()) {
2063 isBlockKept[oldIndex] = true;
2064 oldToNewMap[oldIndex] = newIndexCounter++;
2065 } else {
2066 isBlockKept[oldIndex] = false;
2067 }
2068 }
2069
2070 // ff no blocks were empty, no remapping is needed.
2071 if (newIndexCounter == originalSize) {
2072 return *this;
2073 }
2074
2075 // create a fallthrough map for jumps that might target empty blocks
2076 // This map redirects an index to the next non-empty block's original index.
2077 std::vector<indexT> fallthroughTargetMap(originalSize);
2078 const indexT noFallthroughSentinel = originalSize;
2079 indexT nextNonEmptyBlockIndex = noFallthroughSentinel;
2080
2081 // iterate backwards to find the next non-empty block for each position
2082 for (indexT oldIndex = originalSize; oldIndex-- > 0;) {
2083 if (isBlockKept[oldIndex]) {
2084 nextNonEmptyBlockIndex = oldIndex;
2085 }
2086 fallthroughTargetMap[oldIndex] = nextNonEmptyBlockIndex;
2087 }
2088
2089 std::vector<std::shared_ptr<IRCodeBlock>> newCodeBlocks;
2090 newCodeBlocks.reserve(newIndexCounter);
2091
2092 for (indexT oldIndex = 0; oldIndex < originalSize; ++oldIndex) {
2093 if (isBlockKept[oldIndex]) {
2094 auto &block = targetFunction->codeBlock[oldIndex];
2095
2096 for (auto &ins : block->getIRArray()) {
2097 switch (ins.opcode) {
2098 case IR::Opcode::jump:
2101 indexT originalTarget = ins.operands[0].value.codeBlockIndex;
2102
2103 if (originalTarget >= originalSize) {
2104 warning(0,
2105 0,
2106 "IROptimizer::reduceEmptyCodeBlock(): Invalid jump target " + std::to_string(originalTarget) + " in block " +
2107 std::to_string(oldIndex) + " of function " + wstring2string(targetFunction->name) +
2108 ". Replacing with NOP.",
2109 "INTERNAL");
2110 ins.opcode = IR::Opcode::nop;
2111 ins.operands.clear();
2112 continue;
2113 }
2114
2115 indexT resolvedOldTarget = fallthroughTargetMap[originalTarget];
2116
2117 if (resolvedOldTarget == noFallthroughSentinel) {
2118 // This jump targets a region of empty blocks at the end of the function.
2119 // This control flow path becomes undefined after removal.
2120 warning(0,
2121 0,
2122 "IROptimizer::reduceEmptyCodeBlock(): Jump in block " + std::to_string(oldIndex) + " of function " +
2124 " targets an empty region at the function's end. This control flow path is "
2125 "being removed.",
2126 "INTERNAL");
2127 ins.opcode = IR::Opcode::nop;
2128 ins.operands.clear();
2129 } else {
2130 // We found a valid non-empty target block. Convert its old index to the new one.
2131 indexT newTarget = oldToNewMap.at(resolvedOldTarget);
2132 ins.operands[0].value.codeBlockIndex = newTarget;
2133 }
2134 break;
2135 }
2136 default:
2137 // not a jump instruction, no action needed.
2138 break;
2139 }
2140 }
2141 newCodeBlocks.push_back(block);
2142 }
2143 }
2144
2145 targetFunction->codeBlock = std::move(newCodeBlocks);
2146
2147 return *this;
2148 }
2149
2150 bool AnalysisState::operator!=(const AnalysisState &other) const {
2151 if (stack.items.size() != other.stack.items.size() || variableStates.size() != other.variableStates.size()) {
2152 return true;
2153 }
2154
2155 for (size_t i = 0; i < stack.items.size(); ++i) {
2156 const auto &item1 = stack.items[i];
2157 const auto &item2 = other.stack.items[i];
2158 if (item1.hasPossibleValue != item2.hasPossibleValue)
2159 return true;
2160 if (item1.hasPossibleValue) {
2161 if (item1.possibleValue.intValue != item2.possibleValue.intValue)
2162 return true;
2163 }
2164 // Also compare attributes for attribute passes
2165 if (*item1.type != *item2.type || item1.type->attributes != item2.type->attributes) {
2166 return true;
2167 }
2168 }
2169
2170 for (const auto &[varIdx, info1] : variableStates) {
2171 auto it = other.variableStates.find(varIdx);
2172 if (it == other.variableStates.end())
2173 return true;
2174 const auto &info2 = it->second;
2175 if (info1.hasPossibleValue != info2.hasPossibleValue)
2176 return true;
2177 if (info1.hasPossibleValue) {
2178 if (info1.possibleValue.possibleValue.intValue != info2.possibleValue.possibleValue.intValue)
2179 return true;
2180 }
2181 if ((info1.possibleValue.type && info2.possibleValue.type) &&
2182 (*info1.possibleValue.type != *info2.possibleValue.type ||
2183 info1.possibleValue.type->attributes != info2.possibleValue.type->attributes)) {
2184 return true;
2185 }
2186 }
2187
2188 return false;
2189 }
2190
2192 AnalysisState currentState = inState;
2193 simulationStack = currentState.stack;
2194 variablesExtraInfo = currentState.variableStates;
2195 currentCodeBlockIndex = blockIndex;
2196
2197 for (yoi::indexT insIndex = 0; insIndex < targetFunction->codeBlock[blockIndex]->getIRArray().size(); insIndex++) {
2198 const auto &ins = targetFunction->codeBlock[blockIndex]->getIRArray()[insIndex];
2200 }
2201
2202 // Return the final state after all instructions are processed.
2204 }
2205
2207 if (s1.stack.items.empty() && s1.variableStates.empty())
2208 return s2;
2209 if (s2.stack.items.empty() && s2.variableStates.empty())
2210 return s1;
2211
2212 // It's an error if stack sizes don't match at a merge point.
2213 // The IR is likely invalid.
2214 if (s1.stack.items.size() != s2.stack.items.size()) {
2215 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
2216 }
2217
2218 AnalysisState mergedState;
2219
2220 // Merge Stacks
2221 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
2222 const auto &item1 = s1.stack.items[i];
2223 const auto &item2 = s2.stack.items[i];
2224
2225 auto mergedItem =
2226 IRFunctionOptimizer::SimulationStack::Item{item1.type, false, {}, {}, item1.contributedInstructions + item2.contributedInstructions};
2227
2228 if (item1.hasPossibleValue && item2.hasPossibleValue) {
2229 if (item1.type->type == item2.type->type && item1.type->isBasicType() &&
2230 item1.possibleValue.intValue == item2.possibleValue.intValue) {
2231 mergedItem.hasPossibleValue = true;
2232 mergedItem.possibleValue = item1.possibleValue;
2233 }
2234 }
2235 mergedState.stack.push(mergedItem);
2236 }
2237
2238 // Merge Variables
2239 // Create a set of all variable keys from both maps
2240 std::set<indexT> allVarKeys;
2241 for (const auto &[key, val] : s1.variableStates)
2242 allVarKeys.insert(key);
2243 for (const auto &[key, val] : s2.variableStates)
2244 allVarKeys.insert(key);
2245
2246 for (const auto &key : allVarKeys) {
2247 auto it1 = s1.variableStates.find(key);
2248 auto it2 = s2.variableStates.find(key);
2249
2250 if (it1 != s1.variableStates.end() && it2 != s2.variableStates.end()) {
2251 // Variable exists in both paths
2252 const auto &v1 = it1->second;
2253 const auto &v2 = it2->second;
2254
2255 // Default to an unknown value
2256 auto mergedInfo = IRFunctionOptimizer::VariablesExtraInfo{false, true, {}};
2257
2258 if (v1.hasPossibleValue && v2.hasPossibleValue) {
2259 // If known and identical, preserve value
2260 if (v1.possibleValue.possibleValue.intValue == v2.possibleValue.possibleValue.intValue) {
2261 mergedInfo.hasPossibleValue = true;
2262 mergedInfo.possibleValue = v1.possibleValue;
2263 }
2264 }
2265 mergedState.variableStates[key] = mergedInfo;
2266
2267 } else {
2268 mergedState.variableStates[key] = {false, true, {}};
2269 }
2270 }
2271
2272 return mergedState;
2273 }
2274
2276 simulationStack = inState.stack;
2278 currentCodeBlockIndex = blockIndex;
2279
2282 }
2283
2284 // ===================================================================================
2285 // == Nullable Check Pass ==
2286 // ===================================================================================
2287
2289 auto [successors, predecessors] = performCFGAnalysis();
2290
2291 std::map<indexT, AnalysisState> blockInStates;
2292 std::map<indexT, AnalysisState> blockOutStates;
2293 std::queue<indexT> worklist;
2294 bool canReturnNull = false;
2295
2296 AnalysisState entryState;
2297 // Rule 3: Function parameters are nullable
2298 for (yoi::indexT i = 0; i < targetFunction->variableTable.getVariables().size(); ++i) {
2299 auto varType = std::make_shared<IRValueType>(*targetFunction->variableTable.get(i));
2301 varType->addAttribute(IRValueType::ValueAttr::Nullable);
2302 entryState.variableStates[i] = {false, true, {varType, false, {}}};
2303 }
2304
2305 if (!targetFunction->codeBlock.empty())
2306 worklist.push(0);
2307 blockInStates[0] = entryState;
2308
2309 while (!worklist.empty()) {
2310 indexT currentBlockIdx = worklist.front();
2311 worklist.pop();
2312
2313 AnalysisState inState = currentBlockIdx == 0 ? blockInStates[0] : AnalysisState{};
2314 if (predecessors.count(currentBlockIdx) > 0) {
2315 for (indexT predIdx : predecessors[currentBlockIdx]) {
2316 if (blockOutStates.count(predIdx) > 0)
2317 inState = mergeStatesForNullable(inState, blockOutStates[predIdx]);
2318 }
2319 }
2320 blockInStates[currentBlockIdx] = inState;
2321
2322 set_current_file_path(targetFunction->debugInfo.sourceFile);
2323 // warning(targetFunction->debugInfo.line, targetFunction->debugInfo.column, "Performing nullable optmization on " +
2324 // yoi::wstring2string(targetFunction->name));
2325 AnalysisState newOutState = analyzeBlockForNullable(currentBlockIdx, inState);
2326
2327 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
2328 blockOutStates[currentBlockIdx] = newOutState;
2329 if (successors.count(currentBlockIdx) > 0) {
2330 for (indexT succIdx : successors[currentBlockIdx]) {
2331 worklist.push(succIdx);
2332 }
2333 }
2334 }
2335 }
2336
2337 for (indexT blockIdx = 0; blockIdx < targetFunction->codeBlock.size(); ++blockIdx) {
2338 if (targetFunction->codeBlock[blockIdx]->getIRArray().empty())
2339 continue;
2340 const auto &lastIns = targetFunction->codeBlock[blockIdx]->getIRArray().back();
2341 if (lastIns.opcode == IR::Opcode::ret) {
2342 if (blockOutStates.count(blockIdx)) {
2343 auto &finalStack = blockOutStates.at(blockIdx).stack;
2344 if (!finalStack.items.empty()) {
2345 const auto &returnValue = finalStack.items.back();
2346 if (returnValue.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2347 canReturnNull = true;
2348 }
2349 // consume the return value
2350 finalStack.pop();
2351 }
2352 }
2353 }
2354 }
2355
2356 // Apply results
2357 for (const auto &[varIndex, varType] : targetFunction->variableTable.getReversedVariableNameMap()) {
2358 bool isNullable = false;
2359 for (const auto &[blockIndex, outState] : blockOutStates) {
2360 if (outState.variableStates.count(varIndex) &&
2361 outState.variableStates.at(varIndex).possibleValue.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2362 isNullable = true;
2363 break;
2364 }
2365 }
2366 targetFunction->variableTable.get(varIndex) = managedPtr(*targetFunction->variableTable.get(varIndex));
2367 if (isNullable && !targetFunction->variableTable.get(varIndex)->hasAttribute(IRValueType::ValueAttr::Raw)) {
2368 targetFunction->variableTable.get(varIndex)->addAttribute(IRValueType::ValueAttr::Nullable);
2369 }
2370 }
2371
2372 return canReturnNull;
2373 }
2374
2376 simulationStack = inState.stack;
2377 variablesExtraInfo.clear();
2378
2379 for (const auto &[idx, state] : inState.variableStates) {
2380 variablesExtraInfo[idx] = {false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}}};
2381 }
2382
2383 auto getVarType = [&](indexT varIndex) {
2384 if (!variablesExtraInfo.count(varIndex)) {
2385 auto originalType = targetFunction->variableTable.get(varIndex);
2386 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}}};
2387 }
2388 return variablesExtraInfo.at(varIndex).possibleValue.type;
2389 };
2390
2391 auto terminatorFound = [&]() {
2392 AnalysisState outState;
2393 outState.stack = simulationStack;
2394 for (const auto &[idx, state] : variablesExtraInfo) {
2395 outState.variableStates[idx] = {false, true, state.possibleValue};
2396 }
2397 return outState;
2398 };
2399
2400 for (const auto &ins : targetFunction->codeBlock[blockIndex]->getIRArray()) {
2401 switch (ins.opcode) {
2402 // Instructions that push non-nullable values
2404 simulationStack.push(compilerCtx->getIntObjectType(), {});
2405 break;
2407 simulationStack.push(compilerCtx->getDeciObjectType(), {});
2408 break;
2410 simulationStack.push(compilerCtx->getBoolObjectType(), {});
2411 break;
2413 simulationStack.push(compilerCtx->getStrObjectType(), {});
2414 break;
2416 simulationStack.push(compilerCtx->getCharObjectType(), {});
2417 break;
2419 simulationStack.push(compilerCtx->getShortObjectType(), {});
2420 break;
2422 simulationStack.push(compilerCtx->getUnsignedObjectType(), {});
2423 break;
2424 // `push_null` is the source of nullability
2425 case IR::Opcode::push_null: {
2426 auto type = std::make_shared<IRValueType>(IRValueType::valueType::null);
2427 type->addAttribute(IRValueType::ValueAttr::Nullable);
2428 simulationStack.push(type, {});
2429 break;
2430 }
2431 // Data movement
2433 auto value = simulationStack.peek(0);
2435 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2436 // varType->attributes = value.type->attributes; // Rule 1: Direct propagation
2437 // deprecated: we no longer inherit Nullable attributes from parent sign
2438 // unless they are passed to another function as a parameter, or
2439 // they got direct assignment, thus we need another label to mark this para-state.
2440
2441 break;
2442 }
2444 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2445 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
2446 break;
2447 }
2449 auto structObj = simulationStack.peek(0);
2451 auto structDef = compilerCtx->getImportedModule(structObj.type->typeAffiliateModule)->structTable[structObj.type->typeIndex];
2452 auto memberIndex = ins.operands[0].value.symbolIndex;
2453 auto memberType = std::make_shared<IRValueType>(*structDef->fieldTypes[memberIndex]);
2454 if (!memberType->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
2455 memberType->addAttribute(IRValueType::ValueAttr::Nullable);
2456 }
2457 simulationStack.push(memberType, {});
2458 break;
2459 }
2463 break;
2464 }
2465 // Array rules
2467 simulationStack.pop(); // index
2468 auto array = simulationStack.peek(0);
2470 auto elemType = std::make_shared<IRValueType>(array.type->getElementType());
2471 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
2472 elemType->removeAttribute(IRValueType::ValueAttr::Nullable); // Rule 2 special case
2473 } else {
2474 elemType->addAttribute(IRValueType::ValueAttr::Nullable);
2475 }
2476 simulationStack.push(elemType, {});
2477 break;
2478 }
2480 simulationStack.pop(); // value
2481 simulationStack.pop(); // index
2482 simulationStack.pop(); // array
2483 // Rule 1 special case: No propagation
2484 break;
2485 }
2486 // External control flow rules
2488 case IR::Opcode::invoke: {
2489 auto moduleIndex = ins.operands[0].value.symbolIndex;
2490 auto funcIndex = ins.operands[1].value.symbolIndex;
2491 auto func = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
2492
2493 CallGraph::FuncIdentifier calleeId{moduleIndex, funcIndex};
2494
2495 for (size_t i = 0; i < func->argumentTypes.size(); ++i) {
2496 if (globalAnalysisResults.count(calleeId)) {
2497 auto simType = simulationStack.peek(0);
2498 auto paramIndex = func->argumentTypes.size() - i - 1;
2499
2500 if (simType.type->hasAttribute(IRValueType::ValueAttr::Nullable) &&
2501 !globalAnalysisResults.at(calleeId).paramStates.empty()) {
2502 if (globalAnalysisResults.at(calleeId).paramStates[paramIndex] == FunctionAnalysisInfo::ParameterState::Raw) {
2503 warning(ins.debugInfo.line,
2504 ins.debugInfo.column,
2505 "Invoking function with raw parameters but nullable value presents, this "
2506 "may cause unpredictable behavior: affected parameter " +
2507 yoi::wstring2string(func->variableTable.getReversedVariableNameMap()[paramIndex]) + " at " +
2508 yoi::wstring2string(func->name) + " called by " + yoi::wstring2string(targetFunction->name),
2509 "NULLABLE_VALUE_SUPPLY_TO_RAW");
2510 } else {
2511 if (globalAnalysisResults.at(calleeId).paramStates[paramIndex] !=
2513 globalAnalysisResults.at(calleeId).paramStates[paramIndex] = FunctionAnalysisInfo::ParameterState::Nullable;
2514 }
2515 }
2516 }
2517 }
2519 }
2520
2521 auto returnType = std::make_shared<IRValueType>(*func->returnType);
2522 if (returnType->type != IRValueType::valueType::none) {
2523 // Use the globally computed analysis result for the callee.
2524 if (globalAnalysisResults.count(calleeId) && globalAnalysisResults.at(calleeId).isReturnValueNullable) {
2525 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
2526 } else if (!globalAnalysisResults.count(calleeId)) {
2527 // If for some reason it's not in the map (e.g., external function), be conservative.
2528 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
2529 }
2530 }
2531 simulationStack.push(returnType, {});
2532 break;
2533 }
2535 auto argCount = ins.operands[3].value.symbolIndex;
2536 for (int i = 0; i < argCount - 1; i++) {
2538 }
2539 auto returnType = managedPtr(*compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
2540 ->interfaceTable[ins.operands[1].value.symbolIndex]
2541 ->methodMap[ins.operands[2].value.symbolIndex]
2542 ->returnType);
2544 if (returnType->type != IRValueType::valueType::none) {
2545 returnType->addAttribute(IRValueType::ValueAttr::Nullable); // Rule 3
2546 }
2547 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
2548 break;
2549 }
2551 auto function = compilerCtx->getIRFFITable()
2552 ->importedLibraries[ins.operands[0].value.symbolIndex]
2553 .importedFunctionTable[ins.operands[1].value.symbolIndex];
2554 auto returnType = managedPtr(*function->returnType);
2555 auto argTypes = function->argumentTypes;
2556 auto argCount = function->argumentTypes.size();
2557 for (int i = 0; i < argCount; i++) {
2559 }
2560 if (function->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI) &&
2561 !function->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic))
2562 returnType->addAttribute(IRValueType::ValueAttr::Nullable); // Rule 3
2563 else if (function->returnType->isBasicType())
2564 returnType->removeAttribute(IRValueType::ValueAttr::Nullable);
2565 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
2566 break;
2567 }
2568 // Binary Ops: Result is nullable if either operand is.
2569 case IR::Opcode::add:
2570 case IR::Opcode::sub:
2571 case IR::Opcode::mul:
2572 case IR::Opcode::div:
2573 case IR::Opcode::mod:
2579 auto r = simulationStack.peek(0);
2581 auto l = simulationStack.peek(0);
2583 auto resultType = std::make_shared<IRValueType>(*l.type);
2584 resultType->removeAttribute(IRValueType::ValueAttr::Nullable);
2585 simulationStack.push(resultType, {});
2586 break;
2587 }
2588 // Comparison Ops: Result is a non-nullable boolean.
2589 case IR::Opcode::equal:
2597 simulationStack.push(std::make_shared<IRValueType>(*compilerCtx->getBoolObjectType()), {});
2598 break;
2599 }
2600 // Unary Ops: Nullability propagates.
2601 case IR::Opcode::negate:
2603 auto val = simulationStack.peek(0);
2605 simulationStack.push(std::make_shared<IRValueType>(*val.type), {});
2606 break;
2607 }
2608 // Casting: Nullability propagates.
2613 auto val = simulationStack.peek(0);
2615 std::shared_ptr<IRValueType> targetType;
2616 if (ins.opcode == IR::Opcode::basic_cast_int)
2617 targetType = compilerCtx->getIntObjectType();
2618 else if (ins.opcode == IR::Opcode::basic_cast_deci)
2619 targetType = compilerCtx->getDeciObjectType();
2620 else if (ins.opcode == IR::Opcode::basic_cast_bool)
2621 targetType = compilerCtx->getBoolObjectType();
2622 else
2623 targetType = compilerCtx->getCharObjectType();
2624
2625 auto resultType = std::make_shared<IRValueType>(*targetType);
2626 resultType->removeAttribute(IRValueType::ValueAttr::Nullable);
2627 simulationStack.push(resultType, {});
2628 break;
2629 }
2631 auto type = managedPtr(IRValueType{static_cast<IRValueType::valueType>(ins.operands[0].value.symbolIndex),
2632 ins.operands[1].value.symbolIndex,
2633 ins.operands[2].value.symbolIndex,
2634 ins.operands[3].value.symbolIndex ? yoi::vec<yoi::indexT>{ins.operands[3].value.symbolIndex}
2636 type->addAttribute(IRValueType::ValueAttr::Nullable);
2638 simulationStack.push(type, {});
2639 break;
2640 }
2642 simulationStack.pop(); // array
2643 simulationStack.push(std::make_shared<IRValueType>(*compilerCtx->getIntObjectType()), {});
2644 break;
2645 }
2649 simulationStack.push(std::make_shared<IRValueType>(*compilerCtx->getBoolObjectType()), {});
2650 break;
2651 }
2653 auto val = simulationStack.peek(0);
2655 auto resultType = std::make_shared<IRValueType>(IRValueType::valueType::pointerObject);
2656 if (val.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2657 resultType->addAttribute(IRValueType::ValueAttr::Nullable);
2658 }
2659 simulationStack.push(resultType, {});
2660 break;
2661 }
2662 case IR::Opcode::ret:
2663 case IR::Opcode::ret_none: {
2664 return terminatorFound();
2665 }
2666 case IR::Opcode::yield: {
2667 auto item = simulationStack.peek(0);
2670 globalAnalysisResults[currentFuncId].isYieldValueNullable = item.type->hasAttribute(IRValueType::ValueAttr::Nullable);
2671 break;
2672 }
2674 case IR::Opcode::resume: {
2676 break;
2677 }
2678 // Instructions with no stack effect
2679 case IR::Opcode::jump:
2680 case IR::Opcode::nop:
2681 break;
2682 // Other instructions with stack effects
2684 simulationStack.pop(); // rhs
2685 auto lhs = simulationStack.peek(0);
2686 simulationStack.pop(); // lhs
2687 simulationStack.push(std::make_shared<IRValueType>(*lhs.type), {});
2688 break;
2689 }
2691 auto type = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)->globalVariables[ins.operands[1].value.symbolIndex];
2692 auto newType = std::make_shared<IRValueType>(*type);
2693 newType->addAttribute(IRValueType::ValueAttr::Nullable); // Globals are conservatively nullable
2694 simulationStack.push(newType, {});
2695 break;
2696 }
2698 if (!simulationStack.items.empty())
2700 break;
2701 }
2702 default:
2704 }
2705 }
2706 return terminatorFound();
2707 }
2708
2710 if (s1.variableStates.empty())
2711 return s2;
2712 if (s2.variableStates.empty())
2713 return s1;
2714
2715 if (s1.stack.items.size() != s2.stack.items.size()) {
2716 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
2717 }
2718
2719 AnalysisState mergedState;
2720
2721 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
2722 const auto &item1 = s1.stack.items[i];
2723 const auto &item2 = s2.stack.items[i];
2724
2725 auto mergedType = std::make_shared<IRValueType>(*item1.type);
2726 if (item1.type->hasAttribute(IRValueType::ValueAttr::Nullable) || item2.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2727 mergedType->addAttribute(IRValueType::ValueAttr::Nullable);
2728 }
2729
2730 auto mergedItem = IRFunctionOptimizer::SimulationStack::Item{mergedType, false, {}, {}};
2731 mergedState.stack.push(mergedItem);
2732 }
2733
2734 // Merge Variables (Conservative: if nullable in ANY path, it's nullable)
2735 std::set<indexT> allVarKeys;
2736 for (const auto &[key, val] : s1.variableStates)
2737 allVarKeys.insert(key);
2738 for (const auto &[key, val] : s2.variableStates)
2739 allVarKeys.insert(key);
2740
2741 for (const auto &key : allVarKeys) {
2742 auto it1 = s1.variableStates.find(key);
2743 auto it2 = s2.variableStates.find(key);
2744
2745 if (it1 != s1.variableStates.end() && it2 != s2.variableStates.end()) {
2746 auto mergedType = std::make_shared<IRValueType>(*it1->second.possibleValue.type);
2747 if (it2->second.possibleValue.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2748 mergedType->addAttribute(IRValueType::ValueAttr::Nullable);
2749 }
2750 mergedState.variableStates[key] = {false, true, {mergedType, false, {}}};
2751 } else if (it1 != s1.variableStates.end()) {
2752 mergedState.variableStates[key] = it1->second;
2753 } else {
2754 mergedState.variableStates[key] = it2->second;
2755 }
2756 }
2757 return mergedState;
2758 }
2759
2760 // ===================================================================================
2761 // == Raw Check Pass ==
2762 // ===================================================================================
2763
2765 auto [successors, predecessors] = performCFGAnalysis();
2766
2767 std::map<indexT, AnalysisState> blockInStates;
2768 std::map<indexT, AnalysisState> blockOutStates;
2769 std::queue<indexT> worklist;
2770 bool isAlwaysRaw = targetFunction->returnType->isBasicType();
2771 bool hasReturnInstruction = false;
2772
2773 // Rule 5: Parameters are not raw
2774 AnalysisState entryState;
2775 for (yoi::indexT i = 0; i < targetFunction->variableTable.getVariables().size(); ++i) {
2776 auto varType = std::make_shared<IRValueType>(*targetFunction->variableTable.get(i));
2777 if (varType->isBasicRawType() || (varType->isBasicType() && !varType->isArrayType() && !varType->isDynamicArrayType() &&
2779 varType->addAttribute(IRValueType::ValueAttr::Raw);
2780 else
2781 varType->removeAttribute(IRValueType::ValueAttr::Raw);
2782 entryState.variableStates[i] = {false, true, {varType, false, {}}};
2783 }
2784
2785 if (!targetFunction->codeBlock.empty())
2786 worklist.push(0);
2787 blockInStates[0] = entryState;
2788
2789 while (!worklist.empty()) {
2790 indexT currentBlockIdx = worklist.front();
2791 worklist.pop();
2792
2793 AnalysisState inState = currentBlockIdx == 0 ? blockInStates[0] : AnalysisState{};
2794 if (predecessors.count(currentBlockIdx) > 0) {
2795 for (indexT predIdx : predecessors[currentBlockIdx]) {
2796 if (blockOutStates.count(predIdx) > 0)
2797 inState = mergeStatesForRaw(inState, blockOutStates[predIdx]);
2798 }
2799 }
2800 blockInStates[currentBlockIdx] = inState;
2801
2802 AnalysisState newOutState = analyzeBlockForRaw(currentBlockIdx, inState);
2803
2804 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
2805 blockOutStates[currentBlockIdx] = newOutState;
2806 if (successors.count(currentBlockIdx) > 0) {
2807 for (indexT succIdx : successors[currentBlockIdx]) {
2808 worklist.push(succIdx);
2809 }
2810 }
2811 }
2812 }
2813
2814 for (indexT blockIdx = 0; blockIdx < targetFunction->codeBlock.size(); ++blockIdx) {
2815 if (targetFunction->codeBlock[blockIdx]->getIRArray().empty())
2816 continue;
2817
2818 const auto &lastIns = targetFunction->codeBlock[blockIdx]->getIRArray().back();
2819 if (lastIns.opcode == IR::Opcode::ret) {
2820 hasReturnInstruction = true;
2821 if (blockOutStates.count(blockIdx)) {
2822 auto &finalStack = blockOutStates.at(blockIdx).stack;
2823 if (!finalStack.items.empty()) {
2824 const auto &returnValue = finalStack.items.back();
2825 if (!returnValue.type->hasAttribute(IRValueType::ValueAttr::Raw)) {
2826 isAlwaysRaw = false; // if any return path is not raw, the result is not raw.
2827 }
2828 // consume the return value from the stack
2829 finalStack.pop();
2830 } else {
2831 isAlwaysRaw = false; // should not happen with a valid ret
2832 }
2833 } else {
2834 // unreachable block, doesn't affect the outcome
2835 }
2836 }
2837 }
2838
2839 // Apply results
2840 for (const auto &[varIndex, varType] : targetFunction->variableTable.getReversedVariableNameMap()) {
2841 bool isRaw = true; // Assume raw unless proven otherwise
2842 if (blockOutStates.empty())
2843 isRaw = false; // No reachable blocks
2844
2845 for (const auto &[blockIndex, outState] : blockOutStates) {
2846 if (outState.variableStates.count(varIndex) &&
2847 !outState.variableStates.at(varIndex).possibleValue.type->hasAttribute(IRValueType::ValueAttr::Raw)) {
2848 isRaw = false;
2849 break;
2850 }
2851 }
2852
2853 targetFunction->variableTable.get(varIndex) = managedPtr(*targetFunction->variableTable.get(varIndex));
2854 if (isRaw && targetFunction->variableTable.get(varIndex)->isBasicType() &&
2855 !targetFunction->variableTable.get(varIndex)->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2856 targetFunction->variableTable.get(varIndex)->addAttribute(IRValueType::ValueAttr::Raw);
2857 } else {
2858 targetFunction->variableTable.get(varIndex)->removeAttribute(IRValueType::ValueAttr::Raw);
2859 }
2860
2862 globalAnalysisResults[currentFuncId].isYieldValueRaw) {
2863 auto ctxIndex = targetFunction->getVariableTable().lookup(L"__context__");
2864 auto ctxType = targetFunction->getVariableTable().get(ctxIndex);
2865 auto yieldField = compilerCtx->getImportedModule(HOSHI_COMPILER_CTX_GLOB_ID_CONST)->structTable[ctxType->typeIndex]->fieldTypes[1];
2866
2867 if (yieldField->isBasicType() && yieldField->dimensions.empty()) {
2868 yieldField = managedPtr(*yieldField);
2869 yieldField->metadata.setMetadata(L"STRUCT_DATAFIELD", true);
2870 }
2871 }
2872 }
2873
2874 return isAlwaysRaw;
2875 }
2876
2878 simulationStack = inState.stack;
2879 variablesExtraInfo.clear();
2880 for (const auto &[idx, state] : inState.variableStates) {
2881 variablesExtraInfo[idx] = {false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}}};
2882 }
2883
2884 auto getVarType = [&](indexT varIndex) {
2885 if (!variablesExtraInfo.count(varIndex)) {
2886 auto originalType = targetFunction->variableTable.get(varIndex);
2887 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}}};
2888 }
2889 return variablesExtraInfo.at(varIndex).possibleValue.type;
2890 };
2891
2892 auto terminatorFound = [&]() {
2893 AnalysisState outState;
2894 outState.stack = simulationStack;
2895 for (const auto &[idx, state] : variablesExtraInfo) {
2896 outState.variableStates[idx] = {false, true, state.possibleValue};
2897 }
2898 return outState;
2899 };
2900
2901 for (const auto &ins : targetFunction->codeBlock[blockIndex]->getIRArray()) {
2902 switch (ins.opcode) {
2903 // Rule 1: Push instructions for basic types create Raw values.
2905 auto newType = std::make_shared<IRValueType>(*compilerCtx->getIntObjectType());
2906 newType->addAttribute(IRValueType::ValueAttr::Raw);
2907 simulationStack.push(newType, {});
2908 break;
2909 }
2911 auto newType = std::make_shared<IRValueType>(*compilerCtx->getDeciObjectType());
2912 newType->addAttribute(IRValueType::ValueAttr::Raw);
2913 simulationStack.push(newType, {});
2914 break;
2915 }
2917 auto newType = std::make_shared<IRValueType>(*compilerCtx->getBoolObjectType());
2918 newType->addAttribute(IRValueType::ValueAttr::Raw);
2919 simulationStack.push(newType, {});
2920 break;
2921 }
2922 // Other push instructions create non-Raw values.
2924 auto newType = std::make_shared<IRValueType>(*compilerCtx->getStrObjectType());
2925 simulationStack.push(newType, {});
2926 break;
2927 }
2929 auto newType = std::make_shared<IRValueType>(*compilerCtx->getCharObjectType());
2930 newType->addAttribute(IRValueType::ValueAttr::Raw);
2931 simulationStack.push(newType, {});
2932 break;
2933 }
2934 case IR::Opcode::push_null: {
2935 auto newType = std::make_shared<IRValueType>(IRValueType::valueType::null);
2936 simulationStack.push(newType, {});
2937 break;
2938 }
2940 auto newType = std::make_shared<IRValueType>(*compilerCtx->getShortObjectType());
2941 newType->addAttribute(IRValueType::ValueAttr::Raw);
2942 simulationStack.push(newType, {});
2943 break;
2944 }
2946 auto newType = std::make_shared<IRValueType>(*compilerCtx->getUnsignedObjectType());
2947 newType->addAttribute(IRValueType::ValueAttr::Raw);
2948 simulationStack.push(newType, {});
2949 break;
2950 }
2952 auto moduleId = ins.operands[0].value.symbolIndex;
2953 auto typeIndex = ins.operands[1].value.symbolIndex;
2954 auto newType = std::make_shared<IRValueType>(IRValueType::valueType::datastructObject, moduleId, typeIndex);
2955 newType->addAttribute(IRValueType::ValueAttr::Raw);
2956 simulationStack.push(newType, {});
2957 break;
2958 }
2960 auto type = simulationStack.peek(0).type;
2962 for (auto &operand : ins.operands) {
2963 auto def = compilerCtx->getImportedModule(type->typeAffiliateModule)->dataStructTable[type->typeIndex];
2964 type = def->fieldTypes[operand.value.symbolIndex];
2965 }
2966 auto resultType = managedPtr(*type);
2967 if (resultType->isBasicType()) {
2968 resultType->addAttribute(IRValueType::ValueAttr::Raw);
2969 }
2970 simulationStack.push(resultType, {});
2971 break;
2972 }
2974 simulationStack.pop(); // value
2975 simulationStack.pop(); // object
2976 break;
2977 }
2978 // Rule 2: store_local propagates Raw attribute.
2980 auto value = simulationStack.peek(0);
2982 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2983 varType->attributes = value.type->attributes;
2984 break;
2985 }
2986 // Rule 3: load_local propagates Raw attribute.
2988 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2989 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
2990 break;
2991 }
2992 // Rule 4: direct_assign destroys Raw attribute.
2994 simulationStack.pop(); // rhs
2995 auto lhs = simulationStack.peek(0);
2996 simulationStack.pop(); // lhs
2997 auto resultType = std::make_shared<IRValueType>(*lhs.type);
2998 resultType->removeAttribute(IRValueType::ValueAttr::Raw);
2999 simulationStack.push(resultType, {});
3000 break;
3001 }
3003 simulationStack.pop(); // index
3004 auto array = simulationStack.peek(0);
3006 auto elemType = std::make_shared<IRValueType>(array.type->getElementType());
3007 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
3008 elemType->addAttribute(IRValueType::ValueAttr::Raw);
3009 } else {
3010 elemType->removeAttribute(IRValueType::ValueAttr::Raw);
3011 }
3012 simulationStack.push(elemType, {});
3013 break;
3014 }
3017 auto array = simulationStack.peek(0);
3018 auto element_type = managedPtr(array.type->getElementType());
3019 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
3020 element_type->addAttribute(IRValueType::ValueAttr::Raw);
3021 } else {
3022 element_type->removeAttribute(IRValueType::ValueAttr::Raw);
3023 }
3025 for (auto i = 0; i < ins.operands[0].value.symbolIndex; ++i) {
3026 simulationStack.push(element_type, {});
3027 }
3028 break;
3029 }
3031 simulationStack.pop(); // value
3032 simulationStack.pop(); // index
3033 simulationStack.pop(); // array
3034 break;
3035 }
3036 case IR::Opcode::ret:
3037 case IR::Opcode::ret_none: {
3038 return terminatorFound();
3039 }
3040 case IR::Opcode::yield: {
3041 auto item = simulationStack.peek(0);
3044 globalAnalysisResults[currentFuncId].isYieldValueRaw = item.type->hasAttribute(IRValueType::ValueAttr::Raw);
3045 break;
3046 }
3048 case IR::Opcode::resume: {
3050 break;
3051 }
3053 case IR::Opcode::invoke: {
3054 auto moduleIndex = ins.operands[0].value.symbolIndex;
3055 auto funcIndex = ins.operands[1].value.symbolIndex;
3056 auto func = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
3057
3058 for (size_t i = 0; i < func->argumentTypes.size(); ++i)
3060
3061 auto returnType = std::make_shared<IRValueType>(*func->returnType);
3062
3063 // use the globally computed analysis result for the callee.
3064 CallGraph::FuncIdentifier calleeId{moduleIndex, funcIndex};
3065 if (globalAnalysisResults.count(calleeId) && globalAnalysisResults.at(calleeId).isReturnValueRaw) {
3066 returnType->addAttribute(IRValueType::ValueAttr::Raw);
3067 } else {
3068 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
3069 }
3070 simulationStack.push(returnType, {});
3071 break;
3072 }
3074 auto argCount = ins.operands[3].value.symbolIndex;
3075 for (int i = 0; i < argCount - 1; i++) {
3077 }
3078 auto returnType = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
3079 ->interfaceTable[ins.operands[1].value.symbolIndex]
3080 ->methodMap[ins.operands[2].value.symbolIndex]
3081 ->returnType;
3082 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
3084 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
3085 break;
3086 }
3088 auto function = compilerCtx->getIRFFITable()
3089 ->importedLibraries[ins.operands[0].value.symbolIndex]
3090 .importedFunctionTable[ins.operands[1].value.symbolIndex];
3091 auto returnType = managedPtr(*function->returnType);
3092 auto argTypes = function->argumentTypes;
3093 auto argCount = function->argumentTypes.size();
3094 for (int i = 0; i < argCount; i++) {
3096 }
3097 if (!function->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI) ||
3098 function->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic))
3099 returnType->addAttribute(IRValueType::ValueAttr::Raw); // Rule 3
3100 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
3101 break;
3102 }
3109 auto val = simulationStack.peek(0);
3111 std::shared_ptr<IRValueType> targetType;
3112 if (ins.opcode == IR::Opcode::basic_cast_int)
3113 targetType = compilerCtx->getIntObjectType();
3114 else if (ins.opcode == IR::Opcode::basic_cast_deci)
3115 targetType = compilerCtx->getDeciObjectType();
3116 else if (ins.opcode == IR::Opcode::basic_cast_bool)
3117 targetType = compilerCtx->getBoolObjectType();
3118 else if (ins.opcode == IR::Opcode::basic_cast_char)
3119 targetType = compilerCtx->getCharObjectType();
3120 else if (ins.opcode == IR::Opcode::basic_cast_unsigned)
3121 targetType = compilerCtx->getUnsignedObjectType();
3122 else if (ins.opcode == IR::Opcode::basic_cast_short)
3123 targetType = compilerCtx->getShortObjectType();
3124 else
3125 panic(ins.debugInfo.line, ins.debugInfo.column, "Invalid basic cast opcode");
3126
3127 auto resultType = std::make_shared<IRValueType>(*targetType);
3128 resultType->addAttribute(IRValueType::ValueAttr::Raw);
3129 simulationStack.push(resultType, {});
3130 break;
3131 }
3132 case IR::Opcode::less_than: {
3133 auto right = simulationStack.peek(0);
3134 auto left = simulationStack.peek(1);
3137 // simulate
3138 auto result = lessThan(left, right);
3139 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3140 simulationStack.push(result);
3141 break;
3142 }
3144 auto right = simulationStack.peek(0);
3145 auto left = simulationStack.peek(1);
3148 // simulate
3149 auto result = greaterThan(left, right);
3150 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3151 simulationStack.push(result);
3152 break;
3153 }
3155 auto right = simulationStack.peek(0);
3156 auto left = simulationStack.peek(1);
3159 // simulate
3160 auto result = greaterThanOrEqual(left, right);
3161 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3162 simulationStack.push(result);
3163 break;
3164 }
3166 auto right = simulationStack.peek(0);
3167 auto left = simulationStack.peek(1);
3170 // simulate
3171 auto result = greaterThanOrEqual(left, right);
3172 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3173 simulationStack.push(result);
3174 break;
3175 }
3176 case IR::Opcode::equal: {
3177 auto right = simulationStack.peek(0);
3178 auto left = simulationStack.peek(1);
3181 // simulate
3182 auto result = equal(left, right);
3183 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3184 simulationStack.push(result);
3185 break;
3186 }
3187 case IR::Opcode::not_equal: {
3188 auto right = simulationStack.peek(0);
3189 auto left = simulationStack.peek(1);
3192 // simulate
3193 auto result = notEqual(left, right);
3194 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3195 simulationStack.push(result);
3196 break;
3197 }
3198 case IR::Opcode::add:
3199 case IR::Opcode::sub:
3200 case IR::Opcode::mul:
3201 case IR::Opcode::div:
3202 case IR::Opcode::mod:
3208 auto r = simulationStack.peek(0);
3210 auto l = simulationStack.peek(0);
3212 auto resultType = std::make_shared<IRValueType>(*l.type);
3213 resultType->addAttribute(IRValueType::ValueAttr::Raw);
3214 simulationStack.push(resultType, {});
3215 break;
3216 }
3218 // pop two values and push one boolean value
3219 auto interfaceType = simulationStack.peek(0).type;
3220 auto objectType = simulationStack.peek(1).type;
3223 IRValueType result = *compilerCtx->getBoolObjectType();
3224 result = result.getBasicRawType();
3226 break;
3227 }
3228 default: {
3229 handleInstruction(ins, 0, blockIndex);
3230 break;
3231 }
3232 }
3233 }
3234
3235 return terminatorFound();
3236 }
3237
3239 simulationStack = inState.stack;
3240 variablesExtraInfo.clear();
3241 // printf("%s (block %llu): Variable extra infos cleared.\n", wstring2string(targetFunction->name).c_str(), blockIndex);
3242 for (const auto &[idx, state] : inState.variableStates) {
3243 // if (!state.possibleValue.metadata.metadata.empty())
3244 // printf("%llu: persist metadata %s\n", idx, wstring2string(state.possibleValue.metadata.to_string()).c_str());
3245 variablesExtraInfo[idx] = {
3246 false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}, state.possibleValue.metadata}};
3247 }
3248
3249 auto getVarType = [&](indexT varIndex) {
3250 if (!variablesExtraInfo.count(varIndex)) {
3251 auto originalType = targetFunction->variableTable.get(varIndex);
3252 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}, {}}};
3253 }
3254 return variablesExtraInfo.at(varIndex).possibleValue.type;
3255 };
3256
3257 auto terminatorFound = [&]() {
3258 AnalysisState outState;
3259 outState.stack = simulationStack;
3260 for (const auto &[idx, state] : variablesExtraInfo) {
3261 outState.variableStates[idx] = {false, true, state.possibleValue};
3262 }
3263 return outState;
3264 };
3265
3266 auto checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess = [&](yoi::indexT offset = 0) {
3268 simulationStack.peek(offset).metadata.hasMetadata(L"from_load") &&
3269 simulationStack.peek(offset).metadata.hasMetadata(L"delayed_interface_impl") &&
3270 simulationStack.peek(offset).metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl").first != -1) {
3271 // valid metadata, optimized local variable, satisify the reverting requirement
3272 variablesExtraInfo[simulationStack.peek(offset).metadata.getMetadata<yoi::indexT>(L"from_load")].possibleValue.metadata.setMetadata(
3273 L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3274 }
3275 };
3276
3277 for (yoi::indexT insIndex = 0; insIndex < targetFunction->codeBlock[blockIndex]->getIRArray().size(); ++insIndex) {
3278 const auto &ins = targetFunction->codeBlock[blockIndex]->getIRArray()[insIndex];
3279 switch (ins.opcode) {
3281 auto varType = getVarType(ins.operands[0].value.symbolIndex);
3282 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
3283 simulationStack.peek(0).metadata = variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata;
3284 simulationStack.peek(0).metadata.setMetadata(L"from_load", ins.operands[0].value.symbolIndex);
3285 break;
3286 }
3288 auto value = simulationStack.peek(0);
3289 simulationStack.pop();
3290
3291 auto varType = getVarType(ins.operands[0].value.symbolIndex);
3292 // still check incompatible metadatas, if anything go wrong, remove it
3293 if (variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata.hasMetadata(L"delayed_interface_impl") &&
3294 value.metadata.hasMetadata(L"delayed_interface_impl")) {
3295 auto &impl1 = variablesExtraInfo[ins.operands[0].value.symbolIndex]
3296 .possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3297 auto &impl2 = value.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3298 if (impl1 != impl2 || impl2.first == -1) {
3299 impl1 = {-1, -1};
3300 }
3301 } else if (value.metadata.hasMetadata(L"delayed_interface_impl")) {
3302 variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata.setMetadata(
3303 L"delayed_interface_impl", value.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl"));
3304 }
3305 varType->attributes = value.type->attributes;
3306 break;
3307 }
3309 auto moduleIndex = ins.operands[0].value.symbolIndex;
3310 auto interfaceImplDef =
3311 compilerCtx->getImportedModule(moduleIndex)->interfaceImplementationTable[ins.operands[1].value.symbolIndex];
3312 auto returnType = managedPtr(IRValueType{IRValueType::valueType::interfaceObject,
3313 interfaceImplDef->implInterfaceIndex.first,
3314 interfaceImplDef->implInterfaceIndex.second});
3315 simulationStack.pop();
3316 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
3317 simulationStack.peek(0).metadata.setMetadata(L"delayed_interface_impl",
3318 std::pair{moduleIndex, ins.operands[1].value.symbolIndex});
3319 break;
3320 }
3321 case IR::Opcode::invoke:
3323 auto moduleIndex = ins.operands[0].value.symbolIndex;
3324 auto function = compilerCtx->getImportedModule(moduleIndex)->functionTable[ins.operands[1].value.symbolIndex];
3325 auto returnType = managedPtr((*function->returnType).removeAttribute(IRValueType::ValueAttr::Borrow));
3326 auto argTypes = function->argumentTypes;
3327 auto argCount = function->argumentTypes.size();
3328 SimulationStack::Item::ContributedInstructionSet contributedInstructions = {currentCodeBlockIndex, {insIndex}, false};
3329 for (int i = 0; i < argCount; i++) {
3330 checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess();
3331 contributedInstructions = contributedInstructions + simulationStack.peek(0).contributedInstructions;
3332 simulationStack.pop();
3333 }
3334 simulationStack.push(returnType, contributedInstructions);
3335 break;
3336 }
3337 case IR::Opcode::ret: {
3338 checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess();
3339 simulationStack.pop();
3340 break;
3341 }
3342 default: {
3343 handleInstruction(ins, insIndex, currentCodeBlockIndex);
3344 break;
3345 }
3346 }
3347 }
3348
3349 return terminatorFound();
3350 }
3351
3353 if (s1.variableStates.empty())
3354 return s2;
3355 if (s2.variableStates.empty())
3356 return s1;
3357
3358 if (s1.stack.items.size() != s2.stack.items.size()) {
3359 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
3360 }
3361
3362 AnalysisState mergedState;
3363
3364 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
3365 const auto &item1 = s1.stack.items[i];
3366 const auto &item2 = s2.stack.items[i];
3367
3368 auto mergedType = std::make_shared<IRValueType>(*item1.type);
3369 if (item1.type->hasAttribute(IRValueType::ValueAttr::Raw) && item2.type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3370 mergedType->addAttribute(IRValueType::ValueAttr::Raw);
3371 } else {
3372 mergedType->removeAttribute(IRValueType::ValueAttr::Raw);
3373 }
3374
3375 auto mergedItem = IRFunctionOptimizer::SimulationStack::Item{mergedType, false, {}, {}};
3376 mergedState.stack.push(mergedItem);
3377 }
3378
3379 // Merge Variables (Optimistic: must be raw in ALL paths to stay raw)
3380 std::set<indexT> allVarKeys;
3381 for (const auto &[key, val] : s1.variableStates)
3382 allVarKeys.insert(key);
3383 for (const auto &[key, val] : s2.variableStates)
3384 allVarKeys.insert(key);
3385
3386 for (const auto &key : allVarKeys) {
3387 auto it1 = s1.variableStates.find(key);
3388 auto it2 = s2.variableStates.find(key);
3389
3390 bool isRawInS1 = (it1 != s1.variableStates.end() && it1->second.possibleValue.type->hasAttribute(IRValueType::ValueAttr::Raw));
3391 bool isRawInS2 = (it2 != s2.variableStates.end() && it2->second.possibleValue.type->hasAttribute(IRValueType::ValueAttr::Raw));
3392
3393 auto baseType = (it1 != s1.variableStates.end()) ? it1->second.possibleValue.type : it2->second.possibleValue.type;
3394 auto mergedType = std::make_shared<IRValueType>(*baseType);
3395
3396 if ((it1 != s1.variableStates.end() && isRawInS1) && (it2 != s2.variableStates.end() && isRawInS2)) {
3397 // printf("localVar#%lld is raw in both paths\n", key);
3398 mergedType->addAttribute(IRValueType::ValueAttr::Raw);
3399 } else { // only in s2
3400 mergedType->removeAttribute(IRValueType::ValueAttr::Raw);
3401 }
3402 mergedState.variableStates[key] = {false, true, {mergedType, false, {}}};
3403 }
3404 return mergedState;
3405 }
3406
3408 if (s1.variableStates.empty())
3409 return s2;
3410 if (s2.variableStates.empty())
3411 return s1;
3412
3413 if (s1.stack.items.size() != s2.stack.items.size()) {
3414 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
3415 }
3416
3417 AnalysisState mergedState;
3418
3419 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
3420 auto &item1 = s1.stack.items[i];
3421 auto &item2 = s2.stack.items[i];
3422
3423 auto mergedItem = item1;
3424 if (mergedItem.metadata.hasMetadata(L"delayed_interface_impl") && mergedItem.metadata.hasMetadata(L"delayed_interface_impl")) {
3425 auto impl1 = item1.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3426 auto impl2 = item2.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3427 if (impl1 == impl2 && impl1.first != -1) {
3428 // same optimized interface implementation
3429 mergedItem.metadata.setMetadata(L"delayed_interface_impl", impl1);
3430 } else {
3431 // different implementations or one is not optimized, cannot keep optimization
3432 mergedItem.metadata.setMetadata(L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3433 }
3434 }
3435 mergedState.stack.push(mergedItem);
3436 }
3437
3438 // Merge Variables (Optimistic: must be raw in ALL paths to stay raw)
3439 std::set<indexT> allVarKeys;
3440 for (const auto &[key, val] : s1.variableStates)
3441 allVarKeys.insert(key);
3442 for (const auto &[key, val] : s2.variableStates)
3443 allVarKeys.insert(key);
3444
3445 for (const auto &key : allVarKeys) {
3446 auto it1 = s1.variableStates.find(key);
3447 auto it2 = s2.variableStates.find(key);
3448
3449 if (it1 != s1.variableStates.end() && it2 != s2.variableStates.end()) {
3450 auto mergedType = std::make_shared<IRValueType>(*it1->second.possibleValue.type);
3451 // merge metadata
3452 auto mergedMetadata = it1->second.possibleValue.metadata;
3453 if (mergedMetadata.hasMetadata(L"delayed_interface_impl") &&
3454 it2->second.possibleValue.metadata.hasMetadata(L"delayed_interface_impl")) {
3455 auto impl1 = it1->second.possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3456 auto impl2 = it2->second.possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3457 if (impl1 == impl2 && impl1.first != -1) {
3458 // same optimized interface implementation
3459 mergedMetadata.setMetadata(L"delayed_interface_impl", impl1);
3460 } else {
3461 // different implementations or one is not optimized, cannot keep optimization
3462 // printf("(block %llu) %llu: discarded metadata %s\n", currentCodeBlockIndex, key,
3463 // wstring2string(it2->second.possibleValue.metadata.to_string()).c_str());
3464 mergedMetadata.setMetadata(L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3465 }
3466 }
3467 mergedState.variableStates[key] = {false, true, {mergedType, false, {}, mergedMetadata}};
3468 } else if (it1 != s1.variableStates.end()) {
3469 mergedState.variableStates[key] = it1->second;
3470 } else if (it2 != s2.variableStates.end()) {
3471 mergedState.variableStates[key] = it2->second;
3472 }
3473 }
3474 return mergedState;
3475 }
3476
3477 void IRFunctionOptimizer::handleInstruction(const IR &ins, yoi::indexT insIndex, yoi::indexT currentCodeBlockIndex) {
3478 switch (ins.opcode) {
3480 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.boolean);
3481 break;
3482 }
3484 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.integer);
3485 break;
3486 }
3488 simulationStack.push(compilerCtx->getDeciObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.decimal);
3489 break;
3490 }
3492 simulationStack.push(compilerCtx->getShortObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.shortV);
3493 break;
3494 }
3496 simulationStack.push(compilerCtx->getUnsignedObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.unsignedV);
3497 break;
3498 }
3500 simulationStack.push(compilerCtx->getStrObjectType(), {currentCodeBlockIndex, {insIndex}});
3501 break;
3502 }
3504 simulationStack.push(
3505 compilerCtx->getCharObjectType(), {currentCodeBlockIndex, {insIndex}}, static_cast<char>(ins.operands[0].value.character));
3506 break;
3507 }
3509 auto value = simulationStack.peek(0);
3510 simulationStack.pop();
3511 // judge whether this is evaluable
3512 if (value.hasPossibleValue) {
3513 switch (value.type->type) {
3515 value.possibleValue.boolValue = value.possibleValue.intValue != 0;
3516 break;
3518 value.possibleValue.boolValue = value.possibleValue.unsignedValue != 0;
3519 break;
3521 value.possibleValue.boolValue = value.possibleValue.shortValue != 0;
3522 break;
3524 value.possibleValue.boolValue = value.possibleValue.deciValue != 0.0;
3525 break;
3527 value.possibleValue.boolValue = value.possibleValue.charValue != 0;
3528 break;
3529 default:
3530 break;
3531 }
3532 value.type = compilerCtx->getBoolObjectType();
3533 simulationStack.push(value);
3534 } else {
3535 simulationStack.push(compilerCtx->getBoolObjectType(),
3536 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3537 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3538 }
3539 break;
3540 }
3542 auto value = simulationStack.peek(0);
3543 simulationStack.pop();
3544 if (value.hasPossibleValue) {
3545 switch (value.type->type) {
3547 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.deciValue);
3548 break;
3550 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.unsignedValue);
3551 break;
3553 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.shortValue);
3554 break;
3556 value.possibleValue.intValue = value.possibleValue.boolValue ? 1 : 0;
3557 break;
3559 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.charValue);
3560 break;
3561 default:
3562 break;
3563 }
3564 value.type = compilerCtx->getIntObjectType();
3565 simulationStack.push(value);
3566 } else {
3567 simulationStack.push(compilerCtx->getIntObjectType(),
3568 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3569 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3570 }
3571 break;
3572 }
3574 auto value = simulationStack.peek(0);
3575 simulationStack.pop();
3576 // std::cout << "simulate basic_cast_deci " << value.hasPossibleValue << std::endl;
3577 if (value.hasPossibleValue) {
3578 switch (value.type->type) {
3580 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.intValue);
3581 break;
3583 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.unsignedValue);
3584 break;
3586 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.shortValue);
3587 break;
3589 value.possibleValue.deciValue = value.possibleValue.boolValue ? 1.0 : 0.0;
3590 break;
3592 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.charValue);
3593 break;
3594 default:
3595 break;
3596 }
3597 value.type = compilerCtx->getDeciObjectType();
3598 simulationStack.push(value);
3599 } else {
3600 simulationStack.push(compilerCtx->getDeciObjectType(),
3601 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3602 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3603 }
3604 break;
3605 }
3607 auto value = simulationStack.peek(0);
3608 simulationStack.pop();
3609 if (value.hasPossibleValue) {
3610 switch (value.type->type) {
3612 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.intValue);
3613 break;
3615 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.unsignedValue);
3616 break;
3618 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.deciValue);
3619 break;
3621 value.possibleValue.shortValue = value.possibleValue.boolValue ? 1 : 0;
3622 break;
3624 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.charValue);
3625 break;
3626 default:
3627 break;
3628 }
3629 value.type = compilerCtx->getShortObjectType();
3630 simulationStack.push(value);
3631 } else {
3632 simulationStack.push(compilerCtx->getShortObjectType(),
3633 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3634 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3635 }
3636 break;
3637 }
3639 auto value = simulationStack.peek(0);
3640 simulationStack.pop();
3641 if (value.hasPossibleValue) {
3642 switch (value.type->type) {
3644 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.intValue);
3645 break;
3647 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.shortValue);
3648 break;
3650 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.deciValue);
3651 break;
3653 value.possibleValue.unsignedValue = value.possibleValue.boolValue ? 1 : 0;
3654 break;
3656 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.charValue);
3657 break;
3658 default:
3659 break;
3660 }
3661 value.type = compilerCtx->getUnsignedObjectType();
3662 simulationStack.push(value);
3663 } else {
3664 simulationStack.push(compilerCtx->getUnsignedObjectType(),
3665 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3666 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3667 }
3668 break;
3669 }
3671 auto value = simulationStack.peek(0);
3672 simulationStack.pop();
3673 if (value.hasPossibleValue) {
3674 switch (value.type->type) {
3676 value.possibleValue.charValue = static_cast<char>(value.possibleValue.deciValue);
3677 break;
3679 value.possibleValue.charValue = static_cast<char>(value.possibleValue.unsignedValue);
3680 break;
3682 value.possibleValue.charValue = static_cast<char>(value.possibleValue.shortValue);
3683 break;
3685 value.possibleValue.charValue = value.possibleValue.boolValue ? 1 : 0;
3686 break;
3688 value.possibleValue.charValue = static_cast<char>(value.possibleValue.intValue);
3689 break;
3690 default:
3691 break;
3692 }
3693 value.type = compilerCtx->getCharObjectType();
3694 simulationStack.push(value);
3695 } else {
3696 simulationStack.push(compilerCtx->getCharObjectType(),
3697 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3698 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3699 }
3700 break;
3701 }
3704 auto array = simulationStack.peek(0);
3705 simulationStack.pop();
3706 for (auto i = 0; i < ins.operands[0].value.symbolIndex; ++i) {
3707 simulationStack.push(managedPtr(array.type->getElementType()),
3708 array.contributedInstructions +
3709 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3710 }
3711 break;
3712 }
3715 auto structVal = simulationStack.peek(0);
3716 simulationStack.pop();
3717 auto structDef = compilerCtx->getImportedModule(structVal.type->typeAffiliateModule)->structTable[structVal.type->typeIndex];
3718 yoi::indexT startPos =
3719 ins.opcode == IR::Opcode::bind_fields_post ? 0 : structDef->fieldTypes.size() - ins.operands[0].value.symbolIndex;
3720 yoi::indexT endPos = startPos + ins.operands[0].value.symbolIndex;
3721 for (auto i = startPos; i < endPos; ++i) {
3722 auto fieldType = structDef->fieldTypes[i];
3723 simulationStack.push(fieldType,
3724 structVal.contributedInstructions +
3725 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3726 }
3727 break;
3728 }
3729 case IR::Opcode::add: {
3730 auto right = simulationStack.peek(0);
3731 auto left = simulationStack.peek(1);
3732 simulationStack.pop();
3733 simulationStack.pop();
3734 // simulate
3735 auto result = add(left, right);
3736 // std::cout << "simulate add " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
3737 // result.hasPossibleValue << std::endl;
3738 simulationStack.push(result);
3739 break;
3740 }
3741 case IR::Opcode::sub: {
3742 auto right = simulationStack.peek(0);
3743 auto left = simulationStack.peek(1);
3744 simulationStack.pop();
3745 simulationStack.pop();
3746 // simulate
3747 auto result = sub(left, right);
3748 simulationStack.push(result);
3749 break;
3750 }
3751 case IR::Opcode::mul: {
3752 auto right = simulationStack.peek(0);
3753 auto left = simulationStack.peek(1);
3754 simulationStack.pop();
3755 simulationStack.pop();
3756 // simulate
3757 auto result = mul(left, right);
3758 simulationStack.push(result);
3759 break;
3760 }
3761 case IR::Opcode::div: {
3762 auto right = simulationStack.peek(0);
3763 auto left = simulationStack.peek(1);
3764 simulationStack.pop();
3765 simulationStack.pop();
3766 // simulate
3767 auto result = div(left, right);
3768 // std::cout << "simulate div " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
3769 // result.hasPossibleValue << std::endl;
3770 simulationStack.push(result);
3771 break;
3772 }
3773 case IR::Opcode::mod: {
3774 auto right = simulationStack.peek(0);
3775 auto left = simulationStack.peek(1);
3776 simulationStack.pop();
3777 simulationStack.pop();
3778 // simulate
3779 auto result = mod(left, right);
3780 simulationStack.push(result);
3781 break;
3782 }
3783 case IR::Opcode::negate: {
3784 auto value = simulationStack.peek(0);
3785 simulationStack.pop();
3786 // simulate
3787 auto result = negate(value);
3788 simulationStack.push(result);
3789 break;
3790 }
3792 auto right = simulationStack.peek(0);
3793 auto left = simulationStack.peek(1);
3794 simulationStack.pop();
3795 simulationStack.pop();
3796 // simulate
3797 auto result = bitwiseAnd(left, right);
3798 simulationStack.push(result);
3799 break;
3800 }
3802 auto right = simulationStack.peek(0);
3803 auto left = simulationStack.peek(1);
3804 simulationStack.pop();
3805 simulationStack.pop();
3806 // simulate
3807 auto result = bitwiseOr(left, right);
3808 simulationStack.push(result);
3809 break;
3810 }
3812 auto right = simulationStack.peek(0);
3813 auto left = simulationStack.peek(1);
3814 simulationStack.pop();
3815 simulationStack.pop();
3816 // simulate
3817 auto result = bitwiseXor(left, right);
3818 simulationStack.push(result);
3819 break;
3820 }
3822 auto value = simulationStack.peek(0);
3823 simulationStack.pop();
3824 // simulate
3825 auto result = bitwiseNot(value);
3826 // lost information, push back
3827 simulationStack.push(result.type, value.contributedInstructions);
3828 break;
3829 }
3831 auto shift = simulationStack.peek(0);
3832 auto value = simulationStack.peek(1);
3833 simulationStack.pop();
3834 simulationStack.pop();
3835 // simulate
3836 auto result = bitwiseShiftLeft(value, shift);
3837 simulationStack.push(result);
3838 break;
3839 }
3841 auto shift = simulationStack.peek(0);
3842 auto value = simulationStack.peek(1);
3843 simulationStack.pop();
3844 simulationStack.pop();
3845 // simulate
3846 auto result = bitwiseShiftRight(value, shift);
3847 simulationStack.push(result);
3848 break;
3849 }
3850 case IR::Opcode::less_than: {
3851 auto right = simulationStack.peek(0);
3852 auto left = simulationStack.peek(1);
3853 simulationStack.pop();
3854 simulationStack.pop();
3855 // simulate
3856 auto result = lessThan(left, right);
3857 simulationStack.push(result);
3858 break;
3859 }
3861 auto right = simulationStack.peek(0);
3862 auto left = simulationStack.peek(1);
3863 simulationStack.pop();
3864 simulationStack.pop();
3865 // simulate
3866 auto result = greaterThan(left, right);
3867 simulationStack.push(result);
3868 break;
3869 }
3871 auto right = simulationStack.peek(0);
3872 auto left = simulationStack.peek(1);
3873 simulationStack.pop();
3874 simulationStack.pop();
3875 // simulate
3876 auto result = greaterThanOrEqual(left, right);
3877 simulationStack.push(result);
3878 break;
3879 }
3881 auto right = simulationStack.peek(0);
3882 auto left = simulationStack.peek(1);
3883 simulationStack.pop();
3884 simulationStack.pop();
3885 // simulate
3886 auto result = greaterThanOrEqual(left, right);
3887 simulationStack.push(result);
3888 break;
3889 }
3890 case IR::Opcode::equal: {
3891 auto right = simulationStack.peek(0);
3892 auto left = simulationStack.peek(1);
3893 simulationStack.pop();
3894 simulationStack.pop();
3895 // simulate
3896 auto result = equal(left, right);
3897 simulationStack.push(result);
3898 break;
3899 }
3900 case IR::Opcode::not_equal: {
3901 auto right = simulationStack.peek(0);
3902 auto left = simulationStack.peek(1);
3903 simulationStack.pop();
3904 simulationStack.pop();
3905 // simulate
3906 auto result = notEqual(left, right);
3907 simulationStack.push(result);
3908 break;
3909 }
3911 auto varIndex = ins.operands[0].value.symbolIndex;
3912 auto varType = targetFunction->getVariableTable().get(varIndex);
3913
3914 if (auto it = variablesExtraInfo.find(varIndex); it != variablesExtraInfo.end() && it->second.hasPossibleValue) {
3915 simulationStack.push(it->second.possibleValue);
3916 } else {
3917 simulationStack.push(varType, {currentCodeBlockIndex, {insIndex}});
3918 }
3919
3920 if (auto it = variablesExtraInfo.find(varIndex); it != variablesExtraInfo.end()) {
3921 it->second.isReadAfterStore = true;
3922 } else {
3923 variablesExtraInfo[varIndex] = {false, true, {}};
3924 }
3925 break;
3926 }
3928 auto varIndex = ins.operands[0].value.symbolIndex;
3929 auto value = simulationStack.peek(0);
3930 simulationStack.pop();
3931
3932 variablesExtraInfo[varIndex] = {value.hasPossibleValue, false, value};
3933 break;
3934 }
3936 // as for global variables, we can't optimize it
3937 auto moduleIndex = ins.operands[0].value.symbolIndex;
3938 auto type = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
3939 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}});
3940 break;
3941 }
3943 // check the definition type and value type here
3944 auto moduleIndex = ins.operands[0].value.symbolIndex;
3945 auto definitionType = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
3946 auto value = simulationStack.peek(0);
3947
3948 if (value.type->isForeignBasicType()) {
3949 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
3950 }
3951
3952 if (*definitionType != *value.type && value.type->type == IRValueType::valueType::null &&
3954 // type mismatch, panic
3955 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::analyzeBlock(): store_global: type mismatch");
3956 }
3957
3958 simulationStack.pop();
3959 break;
3960 }
3962 // we can't optimize it
3963 auto value = simulationStack.peek(0);
3964 auto type = value.type->typeIndex;
3965 auto targetModule = compilerCtx->getImportedModule(value.type->typeAffiliateModule);
3966 auto structDef = targetModule->structTable[type];
3967 auto memberIndex = ins.operands[0].value.symbolIndex;
3968 auto memberDef = managedPtr(*structDef->fieldTypes[memberIndex]);
3969 if (memberDef->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
3970 memberDef->addAttribute(IRValueType::ValueAttr::Raw);
3971 }
3972 simulationStack.pop();
3973 simulationStack.push(
3974 memberDef, value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3975 break;
3976 }
3978 // we can't optimize it
3979 auto value = simulationStack.peek(1);
3980 auto type = simulationStack.peek(0).type->typeIndex;
3981 auto structDef = compilerCtx->getImportedModule(simulationStack.peek(0).type->typeAffiliateModule)->structTable[type];
3982 auto memberIndex = ins.operands[0].value.symbolIndex;
3983 auto memberDef = structDef->fieldTypes[memberIndex];
3984
3985 if (value.type->isForeignBasicType()) {
3986 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
3987 }
3988
3989 if (*memberDef != *value.type && value.type->type != IRValueType::valueType::pointerObject &&
3990 value.type->type != IRValueType::valueType::null) {
3991 // type mismatch, panic
3992 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::analyzeBlock(): store_member: type mismatch");
3993 }
3994
3995 simulationStack.pop();
3996 simulationStack.pop();
3997 break;
3998 }
4000 case IR::Opcode::invoke: {
4001 // we can't optimize it
4002 // in case of which this got optimized in tempVar reduction, we set optimizable flag to false
4003 auto moduleIndex = ins.operands[0].value.symbolIndex;
4004 auto function = compilerCtx->getImportedModule(moduleIndex)->functionTable[ins.operands[1].value.symbolIndex];
4005 auto returnType = function->returnType;
4006 auto argTypes = function->argumentTypes;
4007 auto argCount = function->argumentTypes.size();
4008 SimulationStack::Item::ContributedInstructionSet contributedInstructions = {currentCodeBlockIndex, {insIndex}, false};
4009 for (int i = 0; i < argCount; i++) {
4010 contributedInstructions = contributedInstructions + simulationStack.peek(0).contributedInstructions;
4011 simulationStack.pop();
4012 }
4013 simulationStack.push(returnType, contributedInstructions);
4014 break;
4015 }
4017 auto function = compilerCtx->getIRFFITable()
4018 ->importedLibraries[ins.operands[0].value.symbolIndex]
4019 .importedFunctionTable[ins.operands[1].value.symbolIndex];
4020 auto returnType = function->returnType;
4021 auto argTypes = function->argumentTypes;
4022 auto argCount = function->argumentTypes.size();
4023 for (int i = 0; i < argCount; i++) {
4024 simulationStack.pop();
4025 }
4026 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4027 break;
4028 }
4030 auto argCount = ins.operands[3].value.symbolIndex;
4031 for (int i = 0; i < argCount - 1; i++) {
4032 simulationStack.pop();
4033 }
4034 auto returnType = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
4035 ->interfaceTable[ins.operands[1].value.symbolIndex]
4036 ->methodMap[ins.operands[2].value.symbolIndex]
4037 ->returnType;
4038 simulationStack.pop();
4039 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4040 break;
4041 }
4043 auto moduleIndex = ins.operands[0].value.symbolIndex;
4044 auto structDef = compilerCtx->getImportedModule(moduleIndex)->structTable[ins.operands[1].value.symbolIndex];
4045 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::structObject, moduleIndex, ins.operands[1].value.symbolIndex}),
4046 {currentCodeBlockIndex, {insIndex}, false});
4047 break;
4048 }
4050 auto moduleIndex = ins.operands[0].value.symbolIndex;
4051 auto interfaceImplDef = compilerCtx->getImportedModule(moduleIndex)->interfaceImplementationTable[ins.operands[1].value.symbolIndex];
4052 auto returnType = managedPtr(IRValueType{IRValueType::valueType::interfaceObject,
4053 interfaceImplDef->implInterfaceIndex.first,
4054 interfaceImplDef->implInterfaceIndex.second});
4055 simulationStack.pop();
4056 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4057 break;
4058 }
4061 case IR::Opcode::ret: {
4062 simulationStack.pop();
4063 break;
4064 }
4072 // we can't optimize it
4073 // dims in operands
4074 std::shared_ptr<IRValueType> baseType;
4075 switch (ins.opcode) {
4077 baseType = compilerCtx->getIntObjectType();
4078 break;
4080 baseType = compilerCtx->getBoolObjectType();
4081 break;
4083 baseType = compilerCtx->getCharObjectType();
4084 break;
4086 baseType = compilerCtx->getDeciObjectType();
4087 break;
4089 baseType = compilerCtx->getStrObjectType();
4090 break;
4092 baseType = compilerCtx->getShortObjectType();
4093 break;
4095 baseType = compilerCtx->getUnsignedObjectType();
4096 break;
4097 default:
4098 break;
4099 }
4100
4101 yoi::indexT size = 1;
4103 for (auto i = 1; i < ins.operands.size(); i++) {
4104 size *= ins.operands[i].value.symbolIndex;
4105 dims.push_back(ins.operands[i].value.symbolIndex);
4106 }
4107 for (yoi::indexT i = 0; i < ins.operands[0].value.symbolIndex; i++) {
4108 simulationStack.pop();
4109 }
4110 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
4111 break;
4112 }
4115 auto moduleIndex = ins.operands[0].value.symbolIndex;
4116 auto typeIndex = ins.operands[1].value.symbolIndex;
4117 yoi::indexT size = 1;
4119
4122 moduleIndex,
4123 typeIndex});
4124
4125 for (yoi::indexT i = 3; i < ins.operands.size(); i++) {
4126 size *= ins.operands[i].value.symbolIndex;
4127 dims.push_back(ins.operands[i].value.symbolIndex);
4128 }
4129 for (yoi::indexT i = 0; i < ins.operands[2].value.symbolIndex; i++) {
4130 simulationStack.pop();
4131 }
4132 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
4133 break;
4134 }
4144 std::shared_ptr<IRValueType> baseType;
4145 switch (ins.opcode) {
4147 baseType = compilerCtx->getIntObjectType();
4148 break;
4150 baseType = compilerCtx->getBoolObjectType();
4151 break;
4153 baseType = compilerCtx->getCharObjectType();
4154 break;
4156 baseType = compilerCtx->getDeciObjectType();
4157 break;
4159 baseType = compilerCtx->getShortObjectType();
4160 break;
4162 baseType = compilerCtx->getUnsignedObjectType();
4163 break;
4165 baseType = compilerCtx->getStrObjectType();
4166 break;
4168 baseType = managedPtr(IRValueType{
4169 IRValueType::valueType::interfaceObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
4170 break;
4172 baseType = managedPtr(
4173 IRValueType{IRValueType::valueType::structObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
4174 break;
4175 default:
4176 break;
4177 }
4178
4179 for (yoi::indexT i = 0; i < ins.operands.back().value.symbolIndex; i++) {
4180 simulationStack.pop();
4181 }
4182 simulationStack.pop(); // fuck array length
4183 simulationStack.push(managedPtr(baseType->getDynamicArrayType()), {currentCodeBlockIndex, {insIndex}, false});
4184 break;
4185 }
4187 // we can't optimize it
4188 auto index = simulationStack.peek(0);
4189 auto array = simulationStack.peek(1);
4190 simulationStack.pop();
4191 simulationStack.pop();
4192 simulationStack.push(managedPtr(array.type->getElementType()),
4193 array.contributedInstructions + index.contributedInstructions +
4194 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4195 break;
4196 }
4198 // we can't optimize it
4199 auto index = simulationStack.peek(0);
4200 auto value = simulationStack.peek(1);
4201 auto array = simulationStack.peek(2);
4202 simulationStack.pop();
4203 simulationStack.pop();
4204 simulationStack.pop();
4205 break;
4206 }
4207 case IR::Opcode::pop: {
4208 simulationStack.pop();
4209 break;
4210 }
4212 auto rhs = simulationStack.peek(0);
4213 auto lhs = simulationStack.peek(1);
4214
4215 if (rhs.type->isForeignBasicType()) {
4216 *rhs.type = rhs.type->getNormalizedForeignBasicType();
4217 }
4218 yoi_assert(*lhs.type == *rhs.type, 0, 0, "IROptimizer::analyzeBlock(): direct_assign: type mismatch");
4219 simulationStack.pop();
4220 simulationStack.pop();
4221 simulationStack.push(lhs.type,
4222 lhs.contributedInstructions + rhs.contributedInstructions +
4223 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4224 break;
4225 }
4227 auto type = managedPtr(IRValueType{static_cast<IRValueType::valueType>(ins.operands[0].value.symbolIndex),
4228 ins.operands[1].value.symbolIndex,
4229 ins.operands[2].value.symbolIndex,
4230 ins.operands[3].value.symbolIndex ? yoi::vec<yoi::indexT>{ins.operands[3].value.symbolIndex}
4232 type->addAttribute(IRValueType::ValueAttr::Nullable);
4233 simulationStack.pop();
4234 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4235 break;
4236 }
4238 simulationStack.pop();
4239 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, false});
4240 break;
4241 }
4242 case IR::Opcode::push_null: {
4243 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, true});
4244 break;
4245 }
4247 auto array = simulationStack.peek(0);
4248 simulationStack.pop();
4249 if (array.type->isArrayType()) {
4250 yoi::indexT size = 1;
4251 for (auto dim : array.type->dimensions) {
4252 size *= dim;
4253 }
4254 simulationStack.push(compilerCtx->getIntObjectType(),
4255 array.contributedInstructions +
4256 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}},
4257 static_cast<int64_t>(size));
4258 } else if (array.type->isDynamicArrayType()) {
4259 simulationStack.push(compilerCtx->getIntObjectType(),
4260 array.contributedInstructions +
4261 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4262 }
4263 break;
4264 }
4266 // pop two values and push one boolean value
4267 auto interfaceType = simulationStack.peek(0).type;
4268 auto objectType = simulationStack.peek(1).type;
4269 simulationStack.pop();
4270 simulationStack.pop();
4271 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}, false});
4272 break;
4273 }
4275 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}, false});
4276 break;
4277 }
4279 auto typeMod = ins.operands[0].value.symbolIndex;
4280 auto typeIndex = ins.operands[1].value.symbolIndex;
4281 auto type = managedPtr(IRValueType{IRValueType::valueType::datastructObject, typeMod, typeIndex});
4282 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4283 break;
4284 }
4286 for (yoi::indexT i = 0; i < ins.operands[0].value.symbolIndex; i++) {
4287 simulationStack.pop();
4288 }
4289 break;
4290 }
4292 simulationStack.pop();
4293 simulationStack.pop();
4294 break;
4295 }
4297 auto type = simulationStack.peek(0).type;
4298 simulationStack.pop();
4299 for (auto &operand : ins.operands) {
4300 auto def = compilerCtx->getImportedModule(type->typeAffiliateModule)->dataStructTable[type->typeIndex];
4301 type = def->fieldTypes[operand.value.symbolIndex];
4302 }
4303 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4304 break;
4305 }
4306 case IR::Opcode::yield: {
4307 simulationStack.pop();
4308 simulationStack.pop();
4309 break;
4310 }
4312 simulationStack.pop();
4313 break;
4314 }
4315 case IR::Opcode::resume: {
4316 simulationStack.pop();
4317 break;
4318 }
4319 default: {
4320 // pass
4321 break;
4322 }
4323 }
4324 }
4325
4326 IROptimizer::IROptimizer(const std::shared_ptr<compilerContext> &compilerCtx, yoi::indexT entryModuleIndex)
4327 : compilerCtx(compilerCtx), entryModuleIndex(entryModuleIndex) {}
4328
4330 callGraph[caller].insert(callee);
4331 callerGraph[callee].insert(caller);
4332 }
4333
4336 std::set<CallGraph::FuncIdentifier> visitedFunctions;
4337 std::queue<CallGraph::FuncIdentifier> q;
4338
4339 // build function set
4340 for (auto &module : compilerCtx->getCompiledModules()) {
4341 for (yoi::indexT i = 0; i < module.second->functionTable.size(); i++) {
4342 callGraph.functions.insert(CallGraph::FuncIdentifier{module.first, i});
4343 if (compilerCtx->getImportedModule(module.first)->functionTable[i]->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve))
4344 q.emplace(module.first, i);
4345 }
4346 }
4347
4348 for (auto &exportedFunction : compilerCtx->getIRFFITable()->exportedFunctionTable) {
4349 q.emplace(std::get<0>(exportedFunction.second), std::get<1>(exportedFunction.second));
4350 // add Preserve attribute to exported functions
4351 auto &func = compilerCtx->getImportedModule(std::get<0>(exportedFunction.second))->functionTable[std::get<1>(exportedFunction.second)];
4354 }
4355
4356 if (compilerCtx->getBuildConfig()->buildType == IRBuildConfig::BuildType::executable) {
4357 try {
4358 q.emplace(entryModuleIndex, compilerCtx->getImportedModule(entryModuleIndex)->functionTable.getIndex(L"main#"));
4359 // add Preserve attribute to main function
4360 auto &func = compilerCtx->getImportedModule(entryModuleIndex)
4361 ->functionTable[compilerCtx->getImportedModule(entryModuleIndex)->functionTable.getIndex(L"main#")];
4363 } catch (const std::out_of_range &) {
4364 panic(0, 0, "IROptimizer::buildCallGraph(): entry point not found");
4365 }
4366 }
4367
4368 while (!q.empty()) {
4369 auto function = q.front();
4370 q.pop();
4371
4372 if (visitedFunctions.contains(function))
4373 continue;
4374
4375 for (auto &i : compilerCtx->getImportedModule(function.first)->functionTable[function.second]->codeBlock) {
4376 for (auto &ins : i->getIRArray()) {
4377 if (ins.opcode == IR::Opcode::invoke || ins.opcode == IR::Opcode::invoke_dangling) {
4378 CallGraph::FuncIdentifier callee{ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex};
4379 callGraph.addCall(function, callee);
4380 q.push(callee);
4381 }
4382 }
4383 }
4384
4385 visitedFunctions.insert(function);
4386 }
4387
4389 }
4390
4392 for (const auto &function : functions) {
4393 if (callerGraph[function].size() == 0) {
4394 unreachableFunctions.insert(function);
4395 }
4396 }
4397 }
4398
4404
4406 bool is_param_equal = other.paramStates.size() == paramStates.size();
4407 for (yoi::indexT i = 0; i < other.paramStates.size() && is_param_equal; i++)
4408 is_param_equal = other.paramStates[i] == paramStates[i];
4409 return isReturnValueNullable != other.isReturnValueNullable || isReturnValueRaw != other.isReturnValueRaw || !is_param_equal;
4410 }
4411
4413 auto &varTable = targetFunction->variableTable.getVariables();
4414 if (targetFunction->argumentTypes.size()) {
4415 for (yoi::indexT paramIdx = 0; paramIdx < targetFunction->argumentTypes.size(); paramIdx++) {
4416 // capture the first scope which is full of parameters
4417 if (targetFunction->getVariableTable().scopeIndex(paramIdx) > 0)
4418 break;
4419 auto copied = managedPtr(*varTable[paramIdx]);
4420 if (!copied->hasAttribute(IRValueType::ValueAttr::NoBorrow) &&
4421 (!targetFunction->hasAttribute(IRFunctionDefinition::FunctionAttrs::Variadic) ||
4422 paramIdx != targetFunction->argumentTypes.size() - 1) &&
4423 !copied->hasAttribute(IRValueType::ValueAttr::Raw))
4424 copied->addAttribute(IRValueType::ValueAttr::Borrow);
4425 targetFunction->argumentTypes[paramIdx] = copied;
4426 varTable[paramIdx] = copied;
4427 }
4428 }
4429 return true;
4430 }
4431
4433 auto [successors, predecessors] = performCFGAnalysis();
4434
4435 std::map<indexT, AnalysisState> blockInStates;
4436 std::map<indexT, AnalysisState> blockOutStates;
4437 std::queue<indexT> worklist;
4438
4439 AnalysisState entryState;
4440
4441 if (!targetFunction->codeBlock.empty())
4442 worklist.push(0);
4443 blockInStates[0] = entryState;
4444
4445 while (!worklist.empty()) {
4446 indexT currentBlockIdx = worklist.front();
4447 worklist.pop();
4448
4449 AnalysisState inState = currentBlockIdx == 0 ? blockInStates[0] : AnalysisState{};
4450 if (predecessors.count(currentBlockIdx) > 0) {
4451 for (indexT predIdx : predecessors[currentBlockIdx]) {
4452 if (blockOutStates.count(predIdx) > 0)
4453 inState = mergeStatesForInterfaceAllocationReduction(inState, blockOutStates[predIdx]);
4454 }
4455 }
4456 blockInStates[currentBlockIdx] = inState;
4457
4458 AnalysisState newOutState = analyzeBlockForInterfaceAllocationReduction(currentBlockIdx, inState);
4459
4460 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
4461 blockOutStates[currentBlockIdx] = newOutState;
4462 if (successors.count(currentBlockIdx) > 0) {
4463 for (indexT succIdx : successors[currentBlockIdx]) {
4464 worklist.push(succIdx);
4465 }
4466 }
4467 }
4468 }
4469
4470 // Apply results
4471 for (const auto &[varIndex, varType] : targetFunction->variableTable.getReversedVariableNameMap()) {
4472 bool isDelayedInterfaceImplInfoInitialized = false;
4473 std::pair<yoi::indexT, yoi::indexT> delayedInterfaceImplInfo{-1, -1};
4474 for (const auto &[blockIndex, outState] : blockOutStates) {
4475 if (outState.variableStates.count(varIndex) &&
4476 outState.variableStates.at(varIndex).possibleValue.metadata.hasMetadata(L"delayed_interface_impl")) {
4477 auto impl = outState.variableStates.at(varIndex).possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(
4478 L"delayed_interface_impl");
4479 if (!isDelayedInterfaceImplInfoInitialized) {
4480 delayedInterfaceImplInfo = impl;
4481 isDelayedInterfaceImplInfoInitialized = true;
4482 } else {
4483 if (delayedInterfaceImplInfo != impl || impl.first == -1 || delayedInterfaceImplInfo.first == -1) {
4484 delayedInterfaceImplInfo = {-1, -1};
4485 break;
4486 }
4487 }
4488 }
4489 }
4490 if (auto var = targetFunction->variableTable.getVariables()[varIndex]; delayedInterfaceImplInfo.first != -1) {
4491 var->metadata.setMetadata(L"regressed_interface_impl", delayedInterfaceImplInfo);
4492 } else if (isDelayedInterfaceImplInfoInitialized) {
4493 // printf("invalid metadata, detected, eliminating...\n");
4494 var->metadata.eraseMetadata(L"regressed_interface_impl");
4495 }
4496 }
4497
4498 return true;
4499 }
4500
4502 for (auto &[_, irModule] : compilerCtx->getCompiledModules()) {
4503 for (auto &struct_type : irModule->structTable) {
4504 for (auto &field : struct_type.second->fieldTypes) {
4505 field = managedPtr(*field);
4506 if (field->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
4507 field->addAttribute(IRValueType::ValueAttr::Raw);
4508 } else {
4509 field->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Nullable);
4510 }
4511 }
4512 }
4513 }
4514 return true;
4515 }
4516
4518 // patch the original definition with corresponding Raw and Nullable tag.
4519 for (auto &module : compilerCtx->getCompiledModules()) {
4520 for (auto &interface : module.second->interfaceTable) {
4521 // retarded abuse of auto pointers brought about many problems, including
4522 // where-the-heck-knows-where-the-nullable-is-coming-from things. Thus we completely erase all the tags
4523 // attached on the interface definitions before we took action.
4524 for (auto &method : interface.second->methodMap) {
4525 method.second->returnType = managedPtr(*method.second->returnType);
4526 method.second->returnType->attributes.clear();
4527 for (auto &arg : method.second->argumentTypes) {
4528 arg = managedPtr(*arg);
4529 arg->attributes.clear();
4530 }
4531 }
4532 for (auto &impl : interface.second->implementations) {
4533 auto targetedModule = compilerCtx->getImportedModule(std::get<1>(impl));
4534 auto implName = visitor::getInterfaceImplName(std::pair{module.first, module.second->interfaceTable.getIndex(interface.first)},
4535 managedPtr(IRValueType{std::get<0>(impl), std::get<1>(impl), std::get<2>(impl)}));
4536 auto implDef = targetedModule->interfaceImplementationTable[implName];
4537 for (yoi::indexT virtIndex = 0; virtIndex < implDef->virtualMethods.size(); virtIndex++) {
4538 auto virtDef = compilerCtx->getImportedModule(implDef->virtualMethods[virtIndex]->typeAffiliateModule)
4539 ->functionTable[implDef->virtualMethods[virtIndex]->typeIndex];
4540 auto targetDef = interface.second->methodMap[virtIndex];
4541 virtDef->argumentTypes[0]->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Borrow);
4542 for (yoi::indexT paramIndex = 0; paramIndex < targetDef->argumentTypes.size(); paramIndex++) {
4543 if (virtDef->argumentTypes[paramIndex]->isBasicType() && !virtDef->argumentTypes[paramIndex]->isArrayType() &&
4544 !virtDef->argumentTypes[paramIndex]->isDynamicArrayType() &&
4545 !virtDef->argumentTypes[paramIndex + 1]->hasAttribute(IRValueType::ValueAttr::Raw)) {
4546 targetDef->argumentTypes[paramIndex]
4547 ->removeAttribute(IRValueType::ValueAttr::Raw)
4548 .addAttribute(IRValueType::ValueAttr::Nullable);
4549 }
4550 }
4551 // patch the function return type as well
4552 if (virtDef->returnType->isBasicType() && !virtDef->returnType->isArrayType() && !virtDef->returnType->isDynamicArrayType() &&
4553 !virtDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw)) {
4554 targetDef->returnType->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Nullable);
4555 }
4556 }
4557 }
4558 }
4559
4560 // add Raw tag if no Nullable tag present for basic types in interface methods.
4561 for (auto &interface : module.second->interfaceTable) {
4562 for (auto &virtIndex : interface.second->methodMap) {
4563 for (auto &i : virtIndex.second->argumentTypes) {
4564 if (!i->hasAttribute(IRValueType::ValueAttr::Nullable) && i->isBasicType() && i->dimensions.empty())
4565 i->addAttribute(IRValueType::ValueAttr::Raw);
4566 }
4567 if (!virtIndex.second->returnType->hasAttribute(IRValueType::ValueAttr::Nullable) &&
4568 virtIndex.second->returnType->isBasicType() && virtIndex.second->returnType->dimensions.empty()) {
4569 virtIndex.second->returnType->addAttribute(IRValueType::ValueAttr::Raw);
4570 }
4571 }
4572 }
4573
4574 auto interface_wrapper_generator = [&](yoi::indexT moduleIndex,
4575 yoi::indexT funcIndex,
4576 const yoi::vec<std::shared_ptr<IRValueType>> &targetTypes,
4577 const std::shared_ptr<IRValueType> &returnType) -> yoi::indexT {
4578 auto originalFunc = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
4579 auto builder = IRFunctionDefinition::Builder()
4580 .setReturnType(returnType)
4581 .setDebugInfo(originalFunc->debugInfo)
4582 .setName(originalFunc->name + L"#wrapper");
4583 builder.addArgument(L"this", managedPtr(IRValueType{*originalFunc->argumentTypes[0]}.addAttribute(IRValueType::ValueAttr::Borrow)));
4584 for (yoi::indexT index = 0; index < targetTypes.size(); index++)
4585 builder.addArgument(L"param" + std::to_wstring(index),
4586 managedPtr(IRValueType{*targetTypes[index]}.addAttribute(IRValueType::ValueAttr::Borrow)));
4588 auto index = compilerCtx->getImportedModule(moduleIndex)->functionTable.put(originalFunc->name + L"wrapper", builder.yield());
4589 auto moduleCtx = compilerCtx->getModuleContext(moduleIndex);
4590 moduleCtx->pushIRBuilder(IRBuilder(
4591 compilerCtx, compilerCtx->getImportedModule(moduleIndex), compilerCtx->getImportedModule(moduleIndex)->functionTable[index]));
4592 moduleCtx->getIRBuilder().switchCodeBlock(moduleCtx->getIRBuilder().createCodeBlock());
4593 moduleCtx->getIRBuilder().loadOp(IR::Opcode::load_local,
4594 IROperand{IROperand::operandType::index, IROperand::operandValue{yoi::indexT{0}}},
4595 originalFunc->argumentTypes[0]);
4596 for (yoi::indexT index = 1; index < originalFunc->argumentTypes.size(); index++) {
4597 moduleCtx->getIRBuilder().loadOp(IR::Opcode::load_local, IROperand{IROperand::operandType::index, index}, targetTypes[index - 1]);
4598 }
4599 moduleCtx->getIRBuilder().invokeMethodOp(funcIndex, targetTypes.size(), originalFunc->returnType, false, true, moduleIndex);
4600 moduleCtx->getIRBuilder().retOp(returnType->type == IRValueType::valueType::none);
4601 moduleCtx->getIRBuilder().yield();
4602 moduleCtx->popIRBuilder();
4603 return index;
4604 };
4605
4606 // check the implementation of interface methods again for any misalignment, if presents, generate a wrapper
4607 // method to fix it.
4608 for (auto &interface : module.second->interfaceTable) {
4609 for (auto &impl : interface.second->implementations) {
4610 // module.second->interfaceImplementationTable.contains()
4611 auto targetedModule = compilerCtx->getImportedModule(std::get<1>(impl));
4612 auto implName = visitor::getInterfaceImplName(std::pair{module.first, module.second->interfaceTable.getIndex(interface.first)},
4613 managedPtr(IRValueType{std::get<0>(impl), std::get<1>(impl), std::get<2>(impl)}));
4614 auto implDef = targetedModule->interfaceImplementationTable[implName];
4615 for (yoi::indexT virtIndex = 0; virtIndex < implDef->virtualMethods.size(); virtIndex++) {
4616 auto virtDef = compilerCtx->getImportedModule(implDef->virtualMethods[virtIndex]->typeAffiliateModule)
4617 ->functionTable[implDef->virtualMethods[virtIndex]->typeIndex];
4618 auto targetDef = interface.second->methodMap[virtIndex];
4619 bool needWrapper = false;
4620 for (yoi::indexT paramIndex = 0; paramIndex < targetDef->argumentTypes.size(); paramIndex++) {
4621 if (virtDef->argumentTypes[paramIndex + 1]->hasAttribute(IRValueType::ValueAttr::Raw) &&
4622 !targetDef->argumentTypes[paramIndex]->hasAttribute(IRValueType::ValueAttr::Raw)) {
4623 needWrapper = true;
4624 }
4625 }
4626 if (virtDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw) &&
4627 !targetDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw)) {
4628 needWrapper = true;
4629 }
4630 if (needWrapper)
4631 implDef->virtualMethods[virtIndex]->typeIndex =
4632 interface_wrapper_generator(implDef->virtualMethods[virtIndex]->typeAffiliateModule,
4633 implDef->virtualMethods[virtIndex]->typeIndex,
4634 targetDef->argumentTypes,
4635 targetDef->returnType);
4636 }
4637 }
4638 }
4639 }
4640 return true;
4641 }
4642
4643 std::pair<std::map<indexT, std::vector<indexT>>, std::map<indexT, std::vector<indexT>>> IRFunctionOptimizer::performCFGAnalysis() {
4644 std::map<indexT, std::vector<indexT>> successors;
4645 std::map<indexT, std::vector<indexT>> predecessors;
4646
4647 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
4648 if (successors.find(i) == successors.end())
4649 successors[i] = {};
4650 if (predecessors.find(i) == predecessors.end())
4651 predecessors[i] = {};
4652 if (!targetFunction->codeBlock[i]->getIRArray().empty()) {
4653 auto &lastIns = targetFunction->codeBlock[i]->getIRArray().back();
4654 bool isTerminator =
4655 (lastIns.opcode == IR::Opcode::jump || lastIns.opcode == IR::Opcode::jump_if_false ||
4656 lastIns.opcode == IR::Opcode::jump_if_true || lastIns.opcode == IR::Opcode::ret || lastIns.opcode == IR::Opcode::ret_none);
4657 if (!isTerminator && (i + 1 < targetFunction->codeBlock.size())) {
4658 successors[i].push_back(i + 1);
4659 predecessors[i + 1].push_back(i);
4660 }
4661 }
4662 for (auto &ins : targetFunction->codeBlock[i]->getIRArray()) {
4663 switch (ins.opcode) {
4664 case IR::Opcode::jump: {
4665 indexT target = ins.operands[0].value.codeBlockIndex;
4666 successors[i].push_back(target);
4667 predecessors[target].push_back(i);
4668 break;
4669 }
4672 indexT target = ins.operands[0].value.codeBlockIndex;
4673 successors[i].push_back(target);
4674 if (i + 1 < targetFunction->codeBlock.size())
4675 successors[i].push_back(i + 1);
4676 predecessors[target].push_back(i);
4677 if (i + 1 < targetFunction->codeBlock.size())
4678 predecessors[i + 1].push_back(i);
4679 break;
4680 }
4681 default:
4682 break;
4683 }
4684 }
4685 }
4686 return std::make_pair(successors, predecessors);
4687 }
4688
4690 std::queue<CallGraph::FuncIdentifier> worklist;
4691 for (const auto &funcId : callGraph.functions) {
4693 worklist.push(funcId);
4694 }
4695
4696 auto paramStateToString = [](const yoi::vec<FunctionAnalysisInfo::ParameterState> &a) {
4697 yoi::wstr result = L"(";
4698 for (auto &i : a) {
4699 switch (i) {
4701 result += L"Raw ";
4702 break;
4704 result += L"Nullable ";
4705 break;
4707 result += L"Plain ";
4708 break;
4709 }
4710 }
4711 result.back() = ')';
4712 return result;
4713 };
4714
4715 auto paramStateComparator = [](const yoi::vec<FunctionAnalysisInfo::ParameterState> &a,
4717 bool result = a.size() == b.size();
4718 for (yoi::indexT i = 0; i < a.size() && result; i++)
4719 result = a[i] == b[i];
4720 return result;
4721 };
4722
4723 while (!worklist.empty()) {
4724 auto funcId = worklist.front();
4725 worklist.pop();
4726
4727 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4728 auto &func = targetedModule->functionTable[funcId.second];
4729
4730 // skip unreachable functions during analysis phase, unless they are preserved.
4731 if (callGraph.unreachableFunctions.count(funcId) && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve)) {
4732 continue;
4733 }
4734
4735 bool oldYieldValueNullable = functionAnalysisResults.at(funcId).isYieldValueNullable;
4736 bool oldYieldValueRaw = functionAnalysisResults.at(funcId).isYieldValueRaw;
4737
4738 IRFunctionOptimizer analyzer{compilerCtx, targetedModule, functionAnalysisResults};
4739 analyzer.setTargetFunction(func, funcId);
4740
4741 // Re-analyze the function to get its new properties
4742 bool newIsNullable = analyzer.performNullableCheck();
4743 bool newIsRaw = analyzer.performRawCheck();
4744
4745 FunctionAnalysisInfo &currentInfo = functionAnalysisResults.at(funcId);
4746
4748 for (yoi::indexT index = 0; index < func->argumentTypes.size(); index++) {
4749 auto &i = func->argumentTypes[index];
4750 if (i->hasAttribute(IRValueType::ValueAttr::Nullable)) {
4752 } else if (i->hasAttribute(IRValueType::ValueAttr::Raw)) {
4753 paramStates.push_back(FunctionAnalysisInfo::ParameterState::Raw);
4754 } else if (currentInfo.paramStates.empty() || currentInfo.paramStates[index] != FunctionAnalysisInfo::ParameterState::Nullable) {
4755 // we only add plain tag when both branch is plain to prevent loop
4756 paramStates.push_back(FunctionAnalysisInfo::ParameterState::Plain);
4757 } else {
4759 }
4760 }
4761
4762 if (currentInfo.isReturnValueNullable != newIsNullable || currentInfo.isReturnValueRaw != newIsRaw ||
4763 !paramStateComparator(paramStates, currentInfo.paramStates) || oldYieldValueNullable != currentInfo.isYieldValueNullable ||
4764 oldYieldValueRaw != currentInfo.isYieldValueRaw) {
4765 // update the global results
4766 currentInfo.isReturnValueNullable = newIsNullable;
4767 currentInfo.isReturnValueRaw = newIsRaw;
4768
4769 // if they changed, add all CALLERS of this function back to the worklist
4770 // because their analysis might now be incorrect.
4771 if (callGraph.callerGraph.count(funcId)) {
4772 for (const auto &callerId : callGraph.callerGraph.at(funcId)) {
4773 set_current_file_path(func->debugInfo.sourceFile);
4774 // warning(func->debugInfo.line, func->debugInfo.column, "Function " + wstring2string(func->name) + " has been updated, adding
4775 // its callers back to the worklist. \nBefore:" + wstring2string(paramStateToString(currentInfo.paramStates)) + "\nAfter :" +
4776 // wstring2string(paramStateToString(paramStates)) + "\n");
4777 worklist.push(callerId);
4778 }
4779 }
4780
4781 currentInfo.paramStates = paramStates;
4782 }
4783 }
4784
4785 for (const auto &[funcId, analysisInfo] : functionAnalysisResults) {
4786 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4787 auto &func = targetedModule->functionTable[funcId.second];
4788
4789 // get a mutable reference to the function's return type
4790 auto returnType = managedPtr(*func->returnType);
4791
4792 // synchronize the Nullable attribute
4793 if (analysisInfo.isReturnValueNullable && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoRawAndNullOptimization)) {
4794 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
4795 } else {
4796 // crucially, remove the attribute if the analysis proved non-nullability.
4797 returnType->removeAttribute(IRValueType::ValueAttr::Nullable);
4798 }
4799
4800 auto statesBefore = analysisInfo.paramStates;
4801
4802 for (yoi::indexT i = 0; i < analysisInfo.paramStates.size(); i++) {
4803 auto &paramType = func->argumentTypes[i];
4804 // the only shift that may take place is the plain to nullable.
4805 // we DO NOT modify the original Raw tag.
4806 switch (analysisInfo.paramStates[i]) {
4808 break;
4809 }
4811 paramType->addAttribute(IRValueType::ValueAttr::Nullable);
4812 break;
4813 }
4815 break;
4816 }
4817 }
4818 }
4819
4820 // synchronize the Raw attribute
4821 if (analysisInfo.isReturnValueRaw && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoRawAndNullOptimization)) {
4822 returnType->addAttribute(IRValueType::ValueAttr::Raw);
4823 } else {
4824 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
4825 }
4826
4827 if (analysisInfo.isYieldValueRaw && func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Generator)) {
4828 auto ctxIndex = func->getVariableTable().lookup(L"__context__");
4829 auto ctxType = func->getVariableTable().get(ctxIndex);
4830 auto &yieldField = compilerCtx->getImportedModule(HOSHI_COMPILER_CTX_GLOB_ID_CONST)->structTable[ctxType->typeIndex]->fieldTypes[1];
4831
4832 if (yieldField->isBasicType() && yieldField->dimensions.empty()) {
4833 yieldField = managedPtr(*yieldField);
4834 yieldField->metadata.setMetadata(L"STRUCT_DATAFIELD", true);
4835 }
4836 }
4837
4838 func->returnType = returnType;
4839 }
4840
4841 for (const auto &funcId : callGraph.functions) {
4842 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4843 auto &func = targetedModule->functionTable[funcId.second];
4844
4845 if (callGraph.unreachableFunctions.count(funcId) && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve)) {
4847 func->codeBlock.clear();
4848 continue;
4849 }
4850
4851 set_current_file_path(func->debugInfo.sourceFile);
4852 IRFunctionOptimizer optimizer{compilerCtx, targetedModule, functionAnalysisResults};
4853 optimizer.setTargetFunction(func, funcId).doOptimizationForCurrentFunction();
4854 }
4855
4856 return true;
4857 }
4858} // namespace yoi
#define HOSHI_COMPILER_CTX_GLOB_ID_CONST
SimulationStack::Item lessThanOrEqual(const SimulationStack::Item &item, const SimulationStack::Item &right)
std::map< CallGraph::FuncIdentifier, FunctionAnalysisInfo > & globalAnalysisResults
AnalysisState mergeStatesForInterfaceAllocationReduction(const AnalysisState &s1, const AnalysisState &s2)
std::map< yoi::indexT, VariablesExtraInfo > variablesExtraInfo
CallGraph::FuncIdentifier currentFuncId
yoi::indexT reduce(const SimulationStack::Item::ContributedInstructionSet &contributedInstructions, yoi::indexT currentIndex)
IRFunctionOptimizer & reduceEmptyCodeBlock()
void handleInstruction(const IR &ins, yoi::indexT insIndex, yoi::indexT currentCodeBlockIndex)
SimulationStack::Item equal(const SimulationStack::Item &item, const SimulationStack::Item &right)
SimulationStack::Item div(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
void transformBlock(indexT blockIndex, const AnalysisState &inState)
std::shared_ptr< IRFunctionDefinition > targetFunction
SimulationStack::Item add(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
IRFunctionOptimizer(const std::shared_ptr< compilerContext > &compilerCtx, const std::shared_ptr< IRModule > &irModule, std::map< CallGraph::FuncIdentifier, FunctionAnalysisInfo > &globalResults)
SimulationStack::Item mul(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
SimulationStack::Item greaterThanOrEqual(const SimulationStack::Item &item, const SimulationStack::Item &right)
SimulationStack::Item sub(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
AnalysisState analyzeBlockForRaw(indexT blockIndex, const AnalysisState &inState)
SimulationStack::Item bitwiseAnd(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
IRFunctionOptimizer & controlFlowOptimization()
struct yoi::IRFunctionOptimizer::SimulationStack simulationStack
SimulationStack::Item negate(const IRFunctionOptimizer::SimulationStack::Item &a)
SimulationStack::Item bitwiseShiftRight(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
IRFunctionOptimizer & doOptimizationForCurrentFunction()
SimulationStack::Item bitwiseNot(const IRFunctionOptimizer::SimulationStack::Item &a)
yoi::indexT currentCodeBlockIndex
std::pair< std::map< indexT, std::vector< indexT > >, std::map< indexT, std::vector< indexT > > > performCFGAnalysis()
AnalysisState mergeStatesForRaw(const AnalysisState &s1, const AnalysisState &s2)
IRFunctionOptimizer & reduceRedundantTempVar()
IRFunctionOptimizer & reduceRedundantNop()
SimulationStack::Item mod(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
yoi::indexT generatePushOp(const SimulationStack::Item &item, yoi::indexT index)
SimulationStack::Item bitwiseShiftLeft(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
AnalysisState analyzeBlockForNullable(indexT blockIndex, const AnalysisState &inState)
AnalysisState analyzeBlock(indexT blockIndex, const AnalysisState &inState)
IRFunctionOptimizer & reduceRedundantJump()
SimulationStack::Item lessThan(const SimulationStack::Item &item, const SimulationStack::Item &right)
IRFunctionOptimizer & reduceRedundantCodeAfterRet()
AnalysisState analyzeBlockForInterfaceAllocationReduction(indexT blockIndex, const AnalysisState &inState)
IRFunctionOptimizer & reduceRedundantConstantExpr()
IRFunctionOptimizer & setTargetFunction(const std::shared_ptr< IRFunctionDefinition > &targetFunction, CallGraph::FuncIdentifier funcId)
SimulationStack::Item bitwiseOr(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
AnalysisState mergeStatesForNullable(const AnalysisState &s1, const AnalysisState &s2)
std::shared_ptr< IRModule > irModule
std::shared_ptr< compilerContext > compilerCtx
SimulationStack::Item notEqual(const SimulationStack::Item &item, const SimulationStack::Item &right)
SimulationStack::Item bitwiseXor(const IRFunctionOptimizer::SimulationStack::Item &a, const IRFunctionOptimizer::SimulationStack::Item &b)
SimulationStack::Item greaterThan(const SimulationStack::Item &item, const SimulationStack::Item &right)
IROptimizer(const std::shared_ptr< compilerContext > &compilerCtx, yoi::indexT entryModuleIndex)
bool performInterfaceWrapperPass()
bool performStructNullablePass()
bool performBaseOptimization()
yoi::indexT entryModuleIndex
std::shared_ptr< compilerContext > compilerCtx
std::map< CallGraph::FuncIdentifier, FunctionAnalysisInfo > functionAnalysisResults
IRValueType getBasicRawType() const
Definition IR.cpp:1376
Definition IR.h:272
IRDebugInfo debugInfo
Definition IR.h:377
yoi::vec< IROperand > operands
Definition IR.h:375
enum yoi::IR::Opcode opcode
static yoi::wstr getInterfaceImplName(const std::pair< yoi::indexT, yoi::indexT > &interfaceSrc, const std::shared_ptr< IRValueType > &typeSrc)
Definition visitor.cpp:2901
@ array
array (ordered collection of values)
constexpr E value(std::size_t i) noexcept
Definition magic_enum.h:668
std::string wstring2string(const std::wstring &v)
Definition def.cpp:230
std::shared_ptr< T > managedPtr(const T &v)
Definition def.hpp:335
void warning(yoi::indexT line, yoi::indexT col, const std::string &msg, const std::string &label)
Definition def.cpp:164
std::vector< t > vec
Definition def.hpp:56
void yoi_assert(bool condition, yoi::indexT line, yoi::indexT col, const std::string &msg)
Asserts a condition that would be true and throws a runtime_error if it is false.
Definition def.cpp:217
void set_current_file_path(const std::wstring &path)
Definition def.cpp:123
std::wstring wstr
Definition def.hpp:51
uint64_t indexT
Definition def.hpp:54
AnalysisState mergeStates(const AnalysisState &s1, const AnalysisState &s2)
void panic(yoi::indexT line, yoi::indexT col, const std::string &msg)
Definition def.cpp:141
IRFunctionOptimizer::SimulationStack stack
std::map< indexT, IRFunctionOptimizer::VariablesExtraInfo > variableStates
bool operator!=(const AnalysisState &other) const
std::map< FuncIdentifier, std::set< FuncIdentifier > > callGraph
std::pair< indexT, indexT > FuncIdentifier
void addCall(FuncIdentifier caller, FuncIdentifier callee)
std::set< FuncIdentifier > functions
yoi::indexT entryModuleIndex
std::map< FuncIdentifier, std::set< FuncIdentifier > > callerGraph
std::set< FuncIdentifier > unreachableFunctions
yoi::vec< ParameterState > paramStates
bool operator!=(const FunctionAnalysisInfo &other) const
ContributedInstructionSet operator+(const ContributedInstructionSet &other) const
union yoi::IRFunctionOptimizer::SimulationStack::Item::PossibleValue possibleValue
void push(const std::shared_ptr< IRValueType > &type, const Item::ContributedInstructionSet &contributedInstructions)
T & getMetadata(const yoi::wstr &key)
Definition IR.h:95
bool hasMetadata(const yoi::wstr &key) const
Definition IR.cpp:1591