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 " + yoi::wstring2string(targetFunction->name));
2324 AnalysisState newOutState = analyzeBlockForNullable(currentBlockIdx, inState);
2325
2326 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
2327 blockOutStates[currentBlockIdx] = newOutState;
2328 if (successors.count(currentBlockIdx) > 0) {
2329 for (indexT succIdx : successors[currentBlockIdx]) {
2330 worklist.push(succIdx);
2331 }
2332 }
2333 }
2334 }
2335
2336 for (indexT blockIdx = 0; blockIdx < targetFunction->codeBlock.size(); ++blockIdx) {
2337 if (targetFunction->codeBlock[blockIdx]->getIRArray().empty())
2338 continue;
2339 const auto &lastIns = targetFunction->codeBlock[blockIdx]->getIRArray().back();
2340 if (lastIns.opcode == IR::Opcode::ret) {
2341 if (blockOutStates.count(blockIdx)) {
2342 auto &finalStack = blockOutStates.at(blockIdx).stack;
2343 if (!finalStack.items.empty()) {
2344 const auto &returnValue = finalStack.items.back();
2345 if (returnValue.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2346 canReturnNull = true;
2347 }
2348 // consume the return value
2349 finalStack.pop();
2350 }
2351 }
2352 }
2353 }
2354
2355 // Apply results
2356 for (const auto &[varIndex, varType] : targetFunction->variableTable.getReversedVariableNameMap()) {
2357 bool isNullable = false;
2358 for (const auto &[blockIndex, outState] : blockOutStates) {
2359 if (outState.variableStates.count(varIndex) &&
2360 outState.variableStates.at(varIndex).possibleValue.type->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2361 isNullable = true;
2362 break;
2363 }
2364 }
2365 targetFunction->variableTable.get(varIndex) = managedPtr(*targetFunction->variableTable.get(varIndex));
2366 if (isNullable && !targetFunction->variableTable.get(varIndex)->hasAttribute(IRValueType::ValueAttr::Raw)) {
2367 targetFunction->variableTable.get(varIndex)->addAttribute(IRValueType::ValueAttr::Nullable);
2368 }
2369 }
2370
2371 return canReturnNull;
2372 }
2373
2375 simulationStack = inState.stack;
2376 variablesExtraInfo.clear();
2377
2378 for (const auto &[idx, state] : inState.variableStates) {
2379 variablesExtraInfo[idx] = {false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}}};
2380 }
2381
2382 auto getVarType = [&](indexT varIndex) {
2383 if (!variablesExtraInfo.count(varIndex)) {
2384 auto originalType = targetFunction->variableTable.get(varIndex);
2385 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}}};
2386 }
2387 return variablesExtraInfo.at(varIndex).possibleValue.type;
2388 };
2389
2390 auto terminatorFound = [&]() {
2391 AnalysisState outState;
2392 outState.stack = simulationStack;
2393 for (const auto &[idx, state] : variablesExtraInfo) {
2394 outState.variableStates[idx] = {false, true, state.possibleValue};
2395 }
2396 return outState;
2397 };
2398
2399 for (const auto &ins : targetFunction->codeBlock[blockIndex]->getIRArray()) {
2400 switch (ins.opcode) {
2401 // Instructions that push non-nullable values
2403 simulationStack.push(compilerCtx->getIntObjectType(), {});
2404 break;
2406 simulationStack.push(compilerCtx->getDeciObjectType(), {});
2407 break;
2409 simulationStack.push(compilerCtx->getBoolObjectType(), {});
2410 break;
2412 simulationStack.push(compilerCtx->getStrObjectType(), {});
2413 break;
2415 simulationStack.push(compilerCtx->getCharObjectType(), {});
2416 break;
2418 simulationStack.push(compilerCtx->getShortObjectType(), {});
2419 break;
2421 simulationStack.push(compilerCtx->getUnsignedObjectType(), {});
2422 break;
2423 // `push_null` is the source of nullability
2424 case IR::Opcode::push_null: {
2425 auto type = std::make_shared<IRValueType>(IRValueType::valueType::null);
2426 type->addAttribute(IRValueType::ValueAttr::Nullable);
2427 simulationStack.push(type, {});
2428 break;
2429 }
2430 // Data movement
2432 auto value = simulationStack.peek(0);
2434 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2435 // varType->attributes = value.type->attributes; // Rule 1: Direct propagation
2436 // deprecated: we no longer inherit Nullable attributes from parent sign
2437 // unless they are passed to another function as a parameter, or
2438 // they got direct assignment, thus we need another label to mark this para-state.
2439
2440 break;
2441 }
2443 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2444 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
2445 break;
2446 }
2448 auto structObj = simulationStack.peek(0);
2450 auto structDef = compilerCtx->getImportedModule(structObj.type->typeAffiliateModule)->structTable[structObj.type->typeIndex];
2451 auto memberIndex = ins.operands[0].value.symbolIndex;
2452 auto memberType = std::make_shared<IRValueType>(*structDef->fieldTypes[memberIndex]);
2453 if (!memberType->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
2454 memberType->addAttribute(IRValueType::ValueAttr::Nullable);
2455 }
2456 simulationStack.push(memberType, {});
2457 break;
2458 }
2462 break;
2463 }
2464 // Array rules
2466 simulationStack.pop(); // index
2467 auto array = simulationStack.peek(0);
2469 auto elemType = std::make_shared<IRValueType>(array.type->getElementType());
2470 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
2471 elemType->removeAttribute(IRValueType::ValueAttr::Nullable); // Rule 2 special case
2472 } else {
2473 elemType->addAttribute(IRValueType::ValueAttr::Nullable);
2474 }
2475 simulationStack.push(elemType, {});
2476 break;
2477 }
2479 simulationStack.pop(); // value
2480 simulationStack.pop(); // index
2481 simulationStack.pop(); // array
2482 // Rule 1 special case: No propagation
2483 break;
2484 }
2485 // External control flow rules
2487 case IR::Opcode::invoke: {
2488 auto moduleIndex = ins.operands[0].value.symbolIndex;
2489 auto funcIndex = ins.operands[1].value.symbolIndex;
2490 auto func = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
2491
2492 CallGraph::FuncIdentifier calleeId{moduleIndex, funcIndex};
2493
2494 for (size_t i = 0; i < func->argumentTypes.size(); ++i) {
2495 if (globalAnalysisResults.count(calleeId)) {
2496 auto simType = simulationStack.peek(0);
2497 auto paramIndex = func->argumentTypes.size() - i - 1;
2498
2499 if (simType.type->hasAttribute(IRValueType::ValueAttr::Nullable) &&
2500 !globalAnalysisResults.at(calleeId).paramStates.empty()) {
2501 if (globalAnalysisResults.at(calleeId).paramStates[paramIndex] == FunctionAnalysisInfo::ParameterState::Raw) {
2502 warning(ins.debugInfo.line,
2503 ins.debugInfo.column,
2504 "Invoking function with raw parameters but nullable value presents, this "
2505 "may cause unpredictable behavior: affected parameter " +
2506 yoi::wstring2string(func->variableTable.getReversedVariableNameMap()[paramIndex]) + " at " +
2507 yoi::wstring2string(func->name) + " called by " + yoi::wstring2string(targetFunction->name),
2508 "NULLABLE_VALUE_SUPPLY_TO_RAW");
2509 } else {
2510 if (globalAnalysisResults.at(calleeId).paramStates[paramIndex] !=
2512 globalAnalysisResults.at(calleeId).paramStates[paramIndex] = FunctionAnalysisInfo::ParameterState::Nullable;
2513 }
2514 }
2515 }
2516 }
2518 }
2519
2520 auto returnType = std::make_shared<IRValueType>(*func->returnType);
2521 if (returnType->type != IRValueType::valueType::none) {
2522 // Use the globally computed analysis result for the callee.
2523 if (globalAnalysisResults.count(calleeId) && globalAnalysisResults.at(calleeId).isReturnValueNullable) {
2524 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
2525 } else if (!globalAnalysisResults.count(calleeId)) {
2526 // If for some reason it's not in the map (e.g., external function), be conservative.
2527 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
2528 }
2529 }
2530 simulationStack.push(returnType, {});
2531 break;
2532 }
2534 auto argCount = ins.operands[3].value.symbolIndex;
2535 for (int i = 0; i < argCount - 1; i++) {
2537 }
2538 auto returnType = managedPtr(*compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
2539 ->interfaceTable[ins.operands[1].value.symbolIndex]
2540 ->methodMap[ins.operands[2].value.symbolIndex]
2541 ->returnType);
2543 if (returnType->type != IRValueType::valueType::none) {
2544 returnType->addAttribute(IRValueType::ValueAttr::Nullable); // Rule 3
2545 }
2546 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
2547 break;
2548 }
2550 auto function = compilerCtx->getIRFFITable()
2551 ->importedLibraries[ins.operands[0].value.symbolIndex]
2552 .importedFunctionTable[ins.operands[1].value.symbolIndex];
2553 auto returnType = managedPtr(*function->returnType);
2554 auto argTypes = function->argumentTypes;
2555 auto argCount = function->argumentTypes.size();
2556 for (int i = 0; i < argCount; i++) {
2558 }
2559 if (function->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI) &&
2560 !function->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic))
2561 returnType->addAttribute(IRValueType::ValueAttr::Nullable); // Rule 3
2562 else if (function->returnType->isBasicType())
2563 returnType->removeAttribute(IRValueType::ValueAttr::Nullable);
2564 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
2565 break;
2566 }
2567 // Binary Ops: Result is nullable if either operand is.
2568 case IR::Opcode::add:
2569 case IR::Opcode::sub:
2570 case IR::Opcode::mul:
2571 case IR::Opcode::div:
2572 case IR::Opcode::mod:
2578 auto r = simulationStack.peek(0);
2580 auto l = simulationStack.peek(0);
2582 auto resultType = std::make_shared<IRValueType>(*l.type);
2583 resultType->removeAttribute(IRValueType::ValueAttr::Nullable);
2584 simulationStack.push(resultType, {});
2585 break;
2586 }
2587 // Comparison Ops: Result is a non-nullable boolean.
2588 case IR::Opcode::equal:
2596 simulationStack.push(std::make_shared<IRValueType>(*compilerCtx->getBoolObjectType()), {});
2597 break;
2598 }
2599 // Unary Ops: Nullability propagates.
2600 case IR::Opcode::negate:
2602 auto val = simulationStack.peek(0);
2604 simulationStack.push(std::make_shared<IRValueType>(*val.type), {});
2605 break;
2606 }
2607 // Casting: Nullability propagates.
2612 auto val = simulationStack.peek(0);
2614 std::shared_ptr<IRValueType> targetType;
2615 if (ins.opcode == IR::Opcode::basic_cast_int)
2616 targetType = compilerCtx->getIntObjectType();
2617 else if (ins.opcode == IR::Opcode::basic_cast_deci)
2618 targetType = compilerCtx->getDeciObjectType();
2619 else if (ins.opcode == IR::Opcode::basic_cast_bool)
2620 targetType = compilerCtx->getBoolObjectType();
2621 else
2622 targetType = compilerCtx->getCharObjectType();
2623
2624 auto resultType = std::make_shared<IRValueType>(*targetType);
2625 resultType->removeAttribute(IRValueType::ValueAttr::Nullable);
2626 simulationStack.push(resultType, {});
2627 break;
2628 }
2630 auto type = managedPtr(IRValueType{static_cast<IRValueType::valueType>(ins.operands[0].value.symbolIndex),
2631 ins.operands[1].value.symbolIndex,
2632 ins.operands[2].value.symbolIndex,
2633 ins.operands[3].value.symbolIndex
2634 ? 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() && !targetFunction->variableTable.get(varIndex)->hasAttribute(IRValueType::ValueAttr::Nullable)) {
2855 targetFunction->variableTable.get(varIndex)->addAttribute(IRValueType::ValueAttr::Raw);
2856 } else {
2857 targetFunction->variableTable.get(varIndex)->removeAttribute(IRValueType::ValueAttr::Raw);
2858 }
2859
2861 auto ctxIndex = targetFunction->getVariableTable().lookup(L"__context__");
2862 auto ctxType = targetFunction->getVariableTable().get(ctxIndex);
2863 auto yieldField = compilerCtx->getImportedModule(HOSHI_COMPILER_CTX_GLOB_ID_CONST)->structTable[ctxType->typeIndex]->fieldTypes[1];
2864
2865 if (yieldField->isBasicType() && yieldField->dimensions.empty()) {
2866 yieldField = managedPtr(*yieldField);
2867 yieldField->metadata.setMetadata(L"STRUCT_DATAFIELD", true);
2868 }
2869 }
2870 }
2871
2872 return isAlwaysRaw;
2873 }
2874
2876 simulationStack = inState.stack;
2877 variablesExtraInfo.clear();
2878 for (const auto &[idx, state] : inState.variableStates) {
2879 variablesExtraInfo[idx] = {false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}}};
2880 }
2881
2882 auto getVarType = [&](indexT varIndex) {
2883 if (!variablesExtraInfo.count(varIndex)) {
2884 auto originalType = targetFunction->variableTable.get(varIndex);
2885 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}}};
2886 }
2887 return variablesExtraInfo.at(varIndex).possibleValue.type;
2888 };
2889
2890 auto terminatorFound = [&]() {
2891 AnalysisState outState;
2892 outState.stack = simulationStack;
2893 for (const auto &[idx, state] : variablesExtraInfo) {
2894 outState.variableStates[idx] = {false, true, state.possibleValue};
2895 }
2896 return outState;
2897 };
2898
2899 for (const auto &ins : targetFunction->codeBlock[blockIndex]->getIRArray()) {
2900 switch (ins.opcode) {
2901 // Rule 1: Push instructions for basic types create Raw values.
2903 auto newType = std::make_shared<IRValueType>(*compilerCtx->getIntObjectType());
2904 newType->addAttribute(IRValueType::ValueAttr::Raw);
2905 simulationStack.push(newType, {});
2906 break;
2907 }
2909 auto newType = std::make_shared<IRValueType>(*compilerCtx->getDeciObjectType());
2910 newType->addAttribute(IRValueType::ValueAttr::Raw);
2911 simulationStack.push(newType, {});
2912 break;
2913 }
2915 auto newType = std::make_shared<IRValueType>(*compilerCtx->getBoolObjectType());
2916 newType->addAttribute(IRValueType::ValueAttr::Raw);
2917 simulationStack.push(newType, {});
2918 break;
2919 }
2920 // Other push instructions create non-Raw values.
2922 auto newType = std::make_shared<IRValueType>(*compilerCtx->getStrObjectType());
2923 simulationStack.push(newType, {});
2924 break;
2925 }
2927 auto newType = std::make_shared<IRValueType>(*compilerCtx->getCharObjectType());
2928 newType->addAttribute(IRValueType::ValueAttr::Raw);
2929 simulationStack.push(newType, {});
2930 break;
2931 }
2932 case IR::Opcode::push_null: {
2933 auto newType = std::make_shared<IRValueType>(IRValueType::valueType::null);
2934 simulationStack.push(newType, {});
2935 break;
2936 }
2938 auto newType = std::make_shared<IRValueType>(*compilerCtx->getShortObjectType());
2939 newType->addAttribute(IRValueType::ValueAttr::Raw);
2940 simulationStack.push(newType, {});
2941 break;
2942 }
2944 auto newType = std::make_shared<IRValueType>(*compilerCtx->getUnsignedObjectType());
2945 newType->addAttribute(IRValueType::ValueAttr::Raw);
2946 simulationStack.push(newType, {});
2947 break;
2948 }
2950 auto moduleId = ins.operands[0].value.symbolIndex;
2951 auto typeIndex = ins.operands[1].value.symbolIndex;
2952 auto newType = std::make_shared<IRValueType>(IRValueType::valueType::datastructObject, moduleId, typeIndex);
2953 newType->addAttribute(IRValueType::ValueAttr::Raw);
2954 simulationStack.push(newType, {});
2955 break;
2956 }
2958 auto type = simulationStack.peek(0).type;
2960 for (auto &operand : ins.operands) {
2961 auto def = compilerCtx->getImportedModule(type->typeAffiliateModule)->dataStructTable[type->typeIndex];
2962 type = def->fieldTypes[operand.value.symbolIndex];
2963 }
2964 auto resultType = managedPtr(*type);
2965 if (resultType->isBasicType()) {
2966 resultType->addAttribute(IRValueType::ValueAttr::Raw);
2967 }
2968 simulationStack.push(resultType, {});
2969 break;
2970 }
2972 simulationStack.pop(); // value
2973 simulationStack.pop(); // object
2974 break;
2975 }
2976 // Rule 2: store_local propagates Raw attribute.
2978 auto value = simulationStack.peek(0);
2980 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2981 varType->attributes = value.type->attributes;
2982 break;
2983 }
2984 // Rule 3: load_local propagates Raw attribute.
2986 auto varType = getVarType(ins.operands[0].value.symbolIndex);
2987 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
2988 break;
2989 }
2990 // Rule 4: direct_assign destroys Raw attribute.
2992 simulationStack.pop(); // rhs
2993 auto lhs = simulationStack.peek(0);
2994 simulationStack.pop(); // lhs
2995 auto resultType = std::make_shared<IRValueType>(*lhs.type);
2996 resultType->removeAttribute(IRValueType::ValueAttr::Raw);
2997 simulationStack.push(resultType, {});
2998 break;
2999 }
3001 simulationStack.pop(); // index
3002 auto array = simulationStack.peek(0);
3004 auto elemType = std::make_shared<IRValueType>(array.type->getElementType());
3005 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
3006 elemType->addAttribute(IRValueType::ValueAttr::Raw);
3007 } else {
3008 elemType->removeAttribute(IRValueType::ValueAttr::Raw);
3009 }
3010 simulationStack.push(elemType, {});
3011 break;
3012 }
3015 auto array = simulationStack.peek(0);
3016 auto element_type = managedPtr(array.type->getElementType());
3017 if (array.type->isBasicType() && (array.type->isArrayType() || array.type->isDynamicArrayType())) {
3018 element_type->addAttribute(IRValueType::ValueAttr::Raw);
3019 } else {
3020 element_type->removeAttribute(IRValueType::ValueAttr::Raw);
3021 }
3023 for (auto i = 0; i < ins.operands[0].value.symbolIndex; ++i) {
3024 simulationStack.push(element_type, {});
3025 }
3026 break;
3027 }
3029 simulationStack.pop(); // value
3030 simulationStack.pop(); // index
3031 simulationStack.pop(); // array
3032 break;
3033 }
3034 case IR::Opcode::ret:
3035 case IR::Opcode::ret_none: {
3036 return terminatorFound();
3037 }
3038 case IR::Opcode::yield: {
3039 auto item = simulationStack.peek(0);
3042 globalAnalysisResults[currentFuncId].isYieldValueRaw = item.type->hasAttribute(IRValueType::ValueAttr::Raw);
3043 break;
3044 }
3046 case IR::Opcode::resume: {
3048 break;
3049 }
3051 case IR::Opcode::invoke: {
3052 auto moduleIndex = ins.operands[0].value.symbolIndex;
3053 auto funcIndex = ins.operands[1].value.symbolIndex;
3054 auto func = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
3055
3056 for (size_t i = 0; i < func->argumentTypes.size(); ++i)
3058
3059 auto returnType = std::make_shared<IRValueType>(*func->returnType);
3060
3061 // use the globally computed analysis result for the callee.
3062 CallGraph::FuncIdentifier calleeId{moduleIndex, funcIndex};
3063 if (globalAnalysisResults.count(calleeId) && globalAnalysisResults.at(calleeId).isReturnValueRaw) {
3064 returnType->addAttribute(IRValueType::ValueAttr::Raw);
3065 } else {
3066 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
3067 }
3068 simulationStack.push(returnType, {});
3069 break;
3070 }
3072 auto argCount = ins.operands[3].value.symbolIndex;
3073 for (int i = 0; i < argCount - 1; i++) {
3075 }
3076 auto returnType = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
3077 ->interfaceTable[ins.operands[1].value.symbolIndex]
3078 ->methodMap[ins.operands[2].value.symbolIndex]
3079 ->returnType;
3080 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
3082 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
3083 break;
3084 }
3086 auto function = compilerCtx->getIRFFITable()
3087 ->importedLibraries[ins.operands[0].value.symbolIndex]
3088 .importedFunctionTable[ins.operands[1].value.symbolIndex];
3089 auto returnType = managedPtr(*function->returnType);
3090 auto argTypes = function->argumentTypes;
3091 auto argCount = function->argumentTypes.size();
3092 for (int i = 0; i < argCount; i++) {
3094 }
3095 if (!function->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI) ||
3096 function->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic))
3097 returnType->addAttribute(IRValueType::ValueAttr::Raw); // Rule 3
3098 simulationStack.push(returnType, {currentCodeBlockIndex, {}, false});
3099 break;
3100 }
3107 auto val = simulationStack.peek(0);
3109 std::shared_ptr<IRValueType> targetType;
3110 if (ins.opcode == IR::Opcode::basic_cast_int)
3111 targetType = compilerCtx->getIntObjectType();
3112 else if (ins.opcode == IR::Opcode::basic_cast_deci)
3113 targetType = compilerCtx->getDeciObjectType();
3114 else if (ins.opcode == IR::Opcode::basic_cast_bool)
3115 targetType = compilerCtx->getBoolObjectType();
3116 else if (ins.opcode == IR::Opcode::basic_cast_char)
3117 targetType = compilerCtx->getCharObjectType();
3118 else if (ins.opcode == IR::Opcode::basic_cast_unsigned)
3119 targetType = compilerCtx->getUnsignedObjectType();
3120 else if (ins.opcode == IR::Opcode::basic_cast_short)
3121 targetType = compilerCtx->getShortObjectType();
3122 else
3123 panic(ins.debugInfo.line, ins.debugInfo.column, "Invalid basic cast opcode");
3124
3125 auto resultType = std::make_shared<IRValueType>(*targetType);
3126 resultType->addAttribute(IRValueType::ValueAttr::Raw);
3127 simulationStack.push(resultType, {});
3128 break;
3129 }
3130 case IR::Opcode::less_than: {
3131 auto right = simulationStack.peek(0);
3132 auto left = simulationStack.peek(1);
3135 // simulate
3136 auto result = lessThan(left, right);
3137 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3138 simulationStack.push(result);
3139 break;
3140 }
3142 auto right = simulationStack.peek(0);
3143 auto left = simulationStack.peek(1);
3146 // simulate
3147 auto result = greaterThan(left, right);
3148 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3149 simulationStack.push(result);
3150 break;
3151 }
3153 auto right = simulationStack.peek(0);
3154 auto left = simulationStack.peek(1);
3157 // simulate
3158 auto result = greaterThanOrEqual(left, right);
3159 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3160 simulationStack.push(result);
3161 break;
3162 }
3164 auto right = simulationStack.peek(0);
3165 auto left = simulationStack.peek(1);
3168 // simulate
3169 auto result = greaterThanOrEqual(left, right);
3170 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3171 simulationStack.push(result);
3172 break;
3173 }
3174 case IR::Opcode::equal: {
3175 auto right = simulationStack.peek(0);
3176 auto left = simulationStack.peek(1);
3179 // simulate
3180 auto result = equal(left, right);
3181 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3182 simulationStack.push(result);
3183 break;
3184 }
3185 case IR::Opcode::not_equal: {
3186 auto right = simulationStack.peek(0);
3187 auto left = simulationStack.peek(1);
3190 // simulate
3191 auto result = notEqual(left, right);
3192 result.type->addAttribute(IRValueType::ValueAttr::Raw);
3193 simulationStack.push(result);
3194 break;
3195 }
3196 case IR::Opcode::add:
3197 case IR::Opcode::sub:
3198 case IR::Opcode::mul:
3199 case IR::Opcode::div:
3200 case IR::Opcode::mod:
3206 auto r = simulationStack.peek(0);
3208 auto l = simulationStack.peek(0);
3210 auto resultType = std::make_shared<IRValueType>(*l.type);
3211 resultType->addAttribute(IRValueType::ValueAttr::Raw);
3212 simulationStack.push(resultType, {});
3213 break;
3214 }
3216 // pop two values and push one boolean value
3217 auto interfaceType = simulationStack.peek(0).type;
3218 auto objectType = simulationStack.peek(1).type;
3221 IRValueType result = *compilerCtx->getBoolObjectType();
3222 result = result.getBasicRawType();
3224 break;
3225 }
3226 default: {
3227 handleInstruction(ins, 0, blockIndex);
3228 break;
3229 }
3230 }
3231 }
3232
3233 return terminatorFound();
3234 }
3235
3237 simulationStack = inState.stack;
3238 variablesExtraInfo.clear();
3239 // printf("%s (block %llu): Variable extra infos cleared.\n", wstring2string(targetFunction->name).c_str(), blockIndex);
3240 for (const auto &[idx, state] : inState.variableStates) {
3241 // if (!state.possibleValue.metadata.metadata.empty())
3242 // printf("%llu: persist metadata %s\n", idx, wstring2string(state.possibleValue.metadata.to_string()).c_str());
3243 variablesExtraInfo[idx] = {false, true, {std::make_shared<IRValueType>(*state.possibleValue.type), false, {}, state.possibleValue.metadata}};
3244 }
3245
3246 auto getVarType = [&](indexT varIndex) {
3247 if (!variablesExtraInfo.count(varIndex)) {
3248 auto originalType = targetFunction->variableTable.get(varIndex);
3249 variablesExtraInfo[varIndex] = {false, true, {std::make_shared<IRValueType>(*originalType), false, {}, {}}};
3250 }
3251 return variablesExtraInfo.at(varIndex).possibleValue.type;
3252 };
3253
3254 auto terminatorFound = [&]() {
3255 AnalysisState outState;
3256 outState.stack = simulationStack;
3257 for (const auto &[idx, state] : variablesExtraInfo) {
3258 outState.variableStates[idx] = {false, true, state.possibleValue};
3259 }
3260 return outState;
3261 };
3262
3263 auto checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess = [&](yoi::indexT offset = 0) {
3265 simulationStack.peek(offset).metadata.hasMetadata(L"from_load") &&
3266 simulationStack.peek(offset).metadata.hasMetadata(L"delayed_interface_impl") &&
3267 simulationStack.peek(offset).metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl").first != -1) {
3268 // valid metadata, optimized local variable, satisify the reverting requirement
3269 variablesExtraInfo[simulationStack.peek(offset).metadata.getMetadata<yoi::indexT>(L"from_load")].possibleValue.metadata.setMetadata(
3270 L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3271 }
3272 };
3273
3274 for (yoi::indexT insIndex = 0; insIndex < targetFunction->codeBlock[blockIndex]->getIRArray().size(); ++insIndex) {
3275 const auto &ins = targetFunction->codeBlock[blockIndex]->getIRArray()[insIndex];
3276 switch (ins.opcode) {
3278 auto varType = getVarType(ins.operands[0].value.symbolIndex);
3279 simulationStack.push(std::make_shared<IRValueType>(*varType), {});
3280 simulationStack.peek(0).metadata = variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata;
3281 simulationStack.peek(0).metadata.setMetadata(L"from_load", ins.operands[0].value.symbolIndex);
3282 break;
3283 }
3285 auto value = simulationStack.peek(0);
3286 simulationStack.pop();
3287
3288 auto varType = getVarType(ins.operands[0].value.symbolIndex);
3289 // still check incompatible metadatas, if anything go wrong, remove it
3290 if (variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata.hasMetadata(L"delayed_interface_impl") &&
3291 value.metadata.hasMetadata(L"delayed_interface_impl")) {
3292 auto &impl1 = variablesExtraInfo[ins.operands[0].value.symbolIndex]
3293 .possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3294 auto &impl2 = value.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3295 if (impl1 != impl2 || impl2.first == -1) {
3296 impl1 = {-1, -1};
3297 }
3298 } else if (value.metadata.hasMetadata(L"delayed_interface_impl")) {
3299 variablesExtraInfo[ins.operands[0].value.symbolIndex].possibleValue.metadata.setMetadata(
3300 L"delayed_interface_impl", value.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl"));
3301 }
3302 varType->attributes = value.type->attributes;
3303 break;
3304 }
3306 auto moduleIndex = ins.operands[0].value.symbolIndex;
3307 auto interfaceImplDef =
3308 compilerCtx->getImportedModule(moduleIndex)->interfaceImplementationTable[ins.operands[1].value.symbolIndex];
3309 auto returnType = managedPtr(IRValueType{IRValueType::valueType::interfaceObject,
3310 interfaceImplDef->implInterfaceIndex.first,
3311 interfaceImplDef->implInterfaceIndex.second});
3312 simulationStack.pop();
3313 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
3314 simulationStack.peek(0).metadata.setMetadata(L"delayed_interface_impl",
3315 std::pair{moduleIndex, ins.operands[1].value.symbolIndex});
3316 break;
3317 }
3318 case IR::Opcode::invoke:
3320 auto moduleIndex = ins.operands[0].value.symbolIndex;
3321 auto function = compilerCtx->getImportedModule(moduleIndex)->functionTable[ins.operands[1].value.symbolIndex];
3322 auto returnType = managedPtr((*function->returnType).removeAttribute(IRValueType::ValueAttr::Borrow));
3323 auto argTypes = function->argumentTypes;
3324 auto argCount = function->argumentTypes.size();
3325 SimulationStack::Item::ContributedInstructionSet contributedInstructions = {currentCodeBlockIndex, {insIndex}, false};
3326 for (int i = 0; i < argCount; i++) {
3327 checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess();
3328 contributedInstructions = contributedInstructions + simulationStack.peek(0).contributedInstructions;
3329 simulationStack.pop();
3330 }
3331 simulationStack.push(returnType, contributedInstructions);
3332 break;
3333 }
3334 case IR::Opcode::ret: {
3335 checkWhetherStackItemIsValidLocalInterfaceObjectAndRecess();
3336 simulationStack.pop();
3337 break;
3338 }
3339 default: {
3340 handleInstruction(ins, insIndex, currentCodeBlockIndex);
3341 break;
3342 }
3343 }
3344 }
3345
3346 return terminatorFound();
3347 }
3348
3350 if (s1.variableStates.empty())
3351 return s2;
3352 if (s2.variableStates.empty())
3353 return s1;
3354
3355 if (s1.stack.items.size() != s2.stack.items.size()) {
3356 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
3357 }
3358
3359 AnalysisState mergedState;
3360
3361 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
3362 const auto &item1 = s1.stack.items[i];
3363 const auto &item2 = s2.stack.items[i];
3364
3365 auto mergedType = std::make_shared<IRValueType>(*item1.type);
3366 if (item1.type->hasAttribute(IRValueType::ValueAttr::Raw) && item2.type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3367 mergedType->addAttribute(IRValueType::ValueAttr::Raw);
3368 } else {
3369 mergedType->removeAttribute(IRValueType::ValueAttr::Raw);
3370 }
3371
3372 auto mergedItem = IRFunctionOptimizer::SimulationStack::Item{mergedType, false, {}, {}};
3373 mergedState.stack.push(mergedItem);
3374 }
3375
3376 // Merge Variables (Optimistic: must be raw in ALL paths to stay raw)
3377 std::set<indexT> allVarKeys;
3378 for (const auto &[key, val] : s1.variableStates)
3379 allVarKeys.insert(key);
3380 for (const auto &[key, val] : s2.variableStates)
3381 allVarKeys.insert(key);
3382
3383 for (const auto &key : allVarKeys) {
3384 auto it1 = s1.variableStates.find(key);
3385 auto it2 = s2.variableStates.find(key);
3386
3387 bool isRawInS1 = (it1 != s1.variableStates.end() && it1->second.possibleValue.type->hasAttribute(IRValueType::ValueAttr::Raw));
3388 bool isRawInS2 = (it2 != s2.variableStates.end() && it2->second.possibleValue.type->hasAttribute(IRValueType::ValueAttr::Raw));
3389
3390 auto baseType = (it1 != s1.variableStates.end()) ? it1->second.possibleValue.type : it2->second.possibleValue.type;
3391 auto mergedType = std::make_shared<IRValueType>(*baseType);
3392
3393 if ((it1 != s1.variableStates.end() && isRawInS1) && (it2 != s2.variableStates.end() && isRawInS2)) {
3394 // printf("localVar#%lld is raw in both paths\n", key);
3395 mergedType->addAttribute(IRValueType::ValueAttr::Raw);
3396 } else { // only in s2
3397 mergedType->removeAttribute(IRValueType::ValueAttr::Raw);
3398 }
3399 mergedState.variableStates[key] = {false, true, {mergedType, false, {}}};
3400 }
3401 return mergedState;
3402 }
3403
3405 if (s1.variableStates.empty())
3406 return s2;
3407 if (s2.variableStates.empty())
3408 return s1;
3409
3410 if (s1.stack.items.size() != s2.stack.items.size()) {
3411 panic(0, 0, "IROptimizer: Incompatible stack depths at merge point.");
3412 }
3413
3414 AnalysisState mergedState;
3415
3416 for (size_t i = 0; i < s1.stack.items.size(); ++i) {
3417 auto &item1 = s1.stack.items[i];
3418 auto &item2 = s2.stack.items[i];
3419
3420 auto mergedItem = item1;
3421 if (mergedItem.metadata.hasMetadata(L"delayed_interface_impl") && mergedItem.metadata.hasMetadata(L"delayed_interface_impl")) {
3422 auto impl1 = item1.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3423 auto impl2 = item2.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3424 if (impl1 == impl2 && impl1.first != -1) {
3425 // same optimized interface implementation
3426 mergedItem.metadata.setMetadata(L"delayed_interface_impl", impl1);
3427 } else {
3428 // different implementations or one is not optimized, cannot keep optimization
3429 mergedItem.metadata.setMetadata(L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3430 }
3431 }
3432 mergedState.stack.push(mergedItem);
3433 }
3434
3435 // Merge Variables (Optimistic: must be raw in ALL paths to stay raw)
3436 std::set<indexT> allVarKeys;
3437 for (const auto &[key, val] : s1.variableStates)
3438 allVarKeys.insert(key);
3439 for (const auto &[key, val] : s2.variableStates)
3440 allVarKeys.insert(key);
3441
3442 for (const auto &key : allVarKeys) {
3443 auto it1 = s1.variableStates.find(key);
3444 auto it2 = s2.variableStates.find(key);
3445
3446 if (it1 != s1.variableStates.end() && it2 != s2.variableStates.end()) {
3447 auto mergedType = std::make_shared<IRValueType>(*it1->second.possibleValue.type);
3448 // merge metadata
3449 auto mergedMetadata = it1->second.possibleValue.metadata;
3450 if (mergedMetadata.hasMetadata(L"delayed_interface_impl") &&
3451 it2->second.possibleValue.metadata.hasMetadata(L"delayed_interface_impl")) {
3452 auto impl1 = it1->second.possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3453 auto impl2 = it2->second.possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"delayed_interface_impl");
3454 if (impl1 == impl2 && impl1.first != -1) {
3455 // same optimized interface implementation
3456 mergedMetadata.setMetadata(L"delayed_interface_impl", impl1);
3457 } else {
3458 // different implementations or one is not optimized, cannot keep optimization
3459 // printf("(block %llu) %llu: discarded metadata %s\n", currentCodeBlockIndex, key, wstring2string(it2->second.possibleValue.metadata.to_string()).c_str());
3460 mergedMetadata.setMetadata(L"delayed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{-1, -1});
3461 }
3462 }
3463 mergedState.variableStates[key] = {false, true, {mergedType, false, {}, mergedMetadata}};
3464 } else if (it1 != s1.variableStates.end()) {
3465 mergedState.variableStates[key] = it1->second;
3466 } else if (it2 != s2.variableStates.end()) {
3467 mergedState.variableStates[key] = it2->second;
3468 }
3469 }
3470 return mergedState;
3471 }
3472
3473 void IRFunctionOptimizer::handleInstruction(const IR &ins, yoi::indexT insIndex, yoi::indexT currentCodeBlockIndex) {
3474 switch (ins.opcode) {
3476 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.boolean);
3477 break;
3478 }
3480 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.integer);
3481 break;
3482 }
3484 simulationStack.push(compilerCtx->getDeciObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.decimal);
3485 break;
3486 }
3488 simulationStack.push(compilerCtx->getShortObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.shortV);
3489 break;
3490 }
3492 simulationStack.push(compilerCtx->getUnsignedObjectType(), {currentCodeBlockIndex, {insIndex}}, ins.operands[0].value.unsignedV);
3493 break;
3494 }
3496 simulationStack.push(compilerCtx->getStrObjectType(), {currentCodeBlockIndex, {insIndex}});
3497 break;
3498 }
3500 simulationStack.push(
3501 compilerCtx->getCharObjectType(), {currentCodeBlockIndex, {insIndex}}, static_cast<char>(ins.operands[0].value.character));
3502 break;
3503 }
3505 auto value = simulationStack.peek(0);
3506 simulationStack.pop();
3507 // judge whether this is evaluable
3508 if (value.hasPossibleValue) {
3509 switch (value.type->type) {
3511 value.possibleValue.boolValue = value.possibleValue.intValue != 0;
3512 break;
3514 value.possibleValue.boolValue = value.possibleValue.unsignedValue != 0;
3515 break;
3517 value.possibleValue.boolValue = value.possibleValue.shortValue != 0;
3518 break;
3520 value.possibleValue.boolValue = value.possibleValue.deciValue != 0.0;
3521 break;
3523 value.possibleValue.boolValue = value.possibleValue.charValue != 0;
3524 break;
3525 default:
3526 break;
3527 }
3528 value.type = compilerCtx->getBoolObjectType();
3529 simulationStack.push(value);
3530 } else {
3531 simulationStack.push(compilerCtx->getBoolObjectType(),
3532 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3533 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3534 }
3535 break;
3536 }
3538 auto value = simulationStack.peek(0);
3539 simulationStack.pop();
3540 if (value.hasPossibleValue) {
3541 switch (value.type->type) {
3543 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.deciValue);
3544 break;
3546 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.unsignedValue);
3547 break;
3549 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.shortValue);
3550 break;
3552 value.possibleValue.intValue = value.possibleValue.boolValue ? 1 : 0;
3553 break;
3555 value.possibleValue.intValue = static_cast<int64_t>(value.possibleValue.charValue);
3556 break;
3557 default:
3558 break;
3559 }
3560 value.type = compilerCtx->getIntObjectType();
3561 simulationStack.push(value);
3562 } else {
3563 simulationStack.push(compilerCtx->getIntObjectType(),
3564 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3565 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3566 }
3567 break;
3568 }
3570 auto value = simulationStack.peek(0);
3571 simulationStack.pop();
3572 // std::cout << "simulate basic_cast_deci " << value.hasPossibleValue << std::endl;
3573 if (value.hasPossibleValue) {
3574 switch (value.type->type) {
3576 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.intValue);
3577 break;
3579 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.unsignedValue);
3580 break;
3582 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.shortValue);
3583 break;
3585 value.possibleValue.deciValue = value.possibleValue.boolValue ? 1.0 : 0.0;
3586 break;
3588 value.possibleValue.deciValue = static_cast<double>(value.possibleValue.charValue);
3589 break;
3590 default:
3591 break;
3592 }
3593 value.type = compilerCtx->getDeciObjectType();
3594 simulationStack.push(value);
3595 } else {
3596 simulationStack.push(compilerCtx->getDeciObjectType(),
3597 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3598 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3599 }
3600 break;
3601 }
3603 auto value = simulationStack.peek(0);
3604 simulationStack.pop();
3605 if (value.hasPossibleValue) {
3606 switch (value.type->type) {
3608 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.intValue);
3609 break;
3611 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.unsignedValue);
3612 break;
3614 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.deciValue);
3615 break;
3617 value.possibleValue.shortValue = value.possibleValue.boolValue ? 1 : 0;
3618 break;
3620 value.possibleValue.shortValue = static_cast<short>(value.possibleValue.charValue);
3621 break;
3622 default:
3623 break;
3624 }
3625 value.type = compilerCtx->getShortObjectType();
3626 simulationStack.push(value);
3627 } else {
3628 simulationStack.push(compilerCtx->getShortObjectType(),
3629 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3630 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3631 }
3632 break;
3633 }
3635 auto value = simulationStack.peek(0);
3636 simulationStack.pop();
3637 if (value.hasPossibleValue) {
3638 switch (value.type->type) {
3640 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.intValue);
3641 break;
3643 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.shortValue);
3644 break;
3646 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.deciValue);
3647 break;
3649 value.possibleValue.unsignedValue = value.possibleValue.boolValue ? 1 : 0;
3650 break;
3652 value.possibleValue.unsignedValue = static_cast<uint64_t>(value.possibleValue.charValue);
3653 break;
3654 default:
3655 break;
3656 }
3657 value.type = compilerCtx->getUnsignedObjectType();
3658 simulationStack.push(value);
3659 } else {
3660 simulationStack.push(compilerCtx->getUnsignedObjectType(),
3661 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3662 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3663 }
3664 break;
3665 }
3667 auto value = simulationStack.peek(0);
3668 simulationStack.pop();
3669 if (value.hasPossibleValue) {
3670 switch (value.type->type) {
3672 value.possibleValue.charValue = static_cast<char>(value.possibleValue.deciValue);
3673 break;
3675 value.possibleValue.charValue = static_cast<char>(value.possibleValue.unsignedValue);
3676 break;
3678 value.possibleValue.charValue = static_cast<char>(value.possibleValue.shortValue);
3679 break;
3681 value.possibleValue.charValue = value.possibleValue.boolValue ? 1 : 0;
3682 break;
3684 value.possibleValue.charValue = static_cast<char>(value.possibleValue.intValue);
3685 break;
3686 default:
3687 break;
3688 }
3689 value.type = compilerCtx->getCharObjectType();
3690 simulationStack.push(value);
3691 } else {
3692 simulationStack.push(compilerCtx->getCharObjectType(),
3693 value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{
3694 currentCodeBlockIndex, std::set{yoi::indexT{insIndex}}});
3695 }
3696 break;
3697 }
3700 auto array = simulationStack.peek(0);
3701 simulationStack.pop();
3702 for (auto i = 0; i < ins.operands[0].value.symbolIndex; ++i) {
3703 simulationStack.push(managedPtr(array.type->getElementType()),
3704 array.contributedInstructions +
3705 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3706 }
3707 break;
3708 }
3711 auto structVal = simulationStack.peek(0);
3712 simulationStack.pop();
3713 auto structDef = compilerCtx->getImportedModule(structVal.type->typeAffiliateModule)->structTable[structVal.type->typeIndex];
3714 yoi::indexT startPos =
3715 ins.opcode == IR::Opcode::bind_fields_post ? 0 : structDef->fieldTypes.size() - ins.operands[0].value.symbolIndex;
3716 yoi::indexT endPos = startPos + ins.operands[0].value.symbolIndex;
3717 for (auto i = startPos; i < endPos; ++i) {
3718 auto fieldType = structDef->fieldTypes[i];
3719 simulationStack.push(fieldType,
3720 structVal.contributedInstructions +
3721 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3722 }
3723 break;
3724 }
3725 case IR::Opcode::add: {
3726 auto right = simulationStack.peek(0);
3727 auto left = simulationStack.peek(1);
3728 simulationStack.pop();
3729 simulationStack.pop();
3730 // simulate
3731 auto result = add(left, right);
3732 // std::cout << "simulate add " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
3733 // result.hasPossibleValue << std::endl;
3734 simulationStack.push(result);
3735 break;
3736 }
3737 case IR::Opcode::sub: {
3738 auto right = simulationStack.peek(0);
3739 auto left = simulationStack.peek(1);
3740 simulationStack.pop();
3741 simulationStack.pop();
3742 // simulate
3743 auto result = sub(left, right);
3744 simulationStack.push(result);
3745 break;
3746 }
3747 case IR::Opcode::mul: {
3748 auto right = simulationStack.peek(0);
3749 auto left = simulationStack.peek(1);
3750 simulationStack.pop();
3751 simulationStack.pop();
3752 // simulate
3753 auto result = mul(left, right);
3754 simulationStack.push(result);
3755 break;
3756 }
3757 case IR::Opcode::div: {
3758 auto right = simulationStack.peek(0);
3759 auto left = simulationStack.peek(1);
3760 simulationStack.pop();
3761 simulationStack.pop();
3762 // simulate
3763 auto result = div(left, right);
3764 // std::cout << "simulate div " << right.hasPossibleValue << " " << left.hasPossibleValue << " " <<
3765 // result.hasPossibleValue << std::endl;
3766 simulationStack.push(result);
3767 break;
3768 }
3769 case IR::Opcode::mod: {
3770 auto right = simulationStack.peek(0);
3771 auto left = simulationStack.peek(1);
3772 simulationStack.pop();
3773 simulationStack.pop();
3774 // simulate
3775 auto result = mod(left, right);
3776 simulationStack.push(result);
3777 break;
3778 }
3779 case IR::Opcode::negate: {
3780 auto value = simulationStack.peek(0);
3781 simulationStack.pop();
3782 // simulate
3783 auto result = negate(value);
3784 simulationStack.push(result);
3785 break;
3786 }
3788 auto right = simulationStack.peek(0);
3789 auto left = simulationStack.peek(1);
3790 simulationStack.pop();
3791 simulationStack.pop();
3792 // simulate
3793 auto result = bitwiseAnd(left, right);
3794 simulationStack.push(result);
3795 break;
3796 }
3798 auto right = simulationStack.peek(0);
3799 auto left = simulationStack.peek(1);
3800 simulationStack.pop();
3801 simulationStack.pop();
3802 // simulate
3803 auto result = bitwiseOr(left, right);
3804 simulationStack.push(result);
3805 break;
3806 }
3808 auto right = simulationStack.peek(0);
3809 auto left = simulationStack.peek(1);
3810 simulationStack.pop();
3811 simulationStack.pop();
3812 // simulate
3813 auto result = bitwiseXor(left, right);
3814 simulationStack.push(result);
3815 break;
3816 }
3818 auto value = simulationStack.peek(0);
3819 simulationStack.pop();
3820 // simulate
3821 auto result = bitwiseNot(value);
3822 // lost information, push back
3823 simulationStack.push(result.type, value.contributedInstructions);
3824 break;
3825 }
3827 auto shift = simulationStack.peek(0);
3828 auto value = simulationStack.peek(1);
3829 simulationStack.pop();
3830 simulationStack.pop();
3831 // simulate
3832 auto result = bitwiseShiftLeft(value, shift);
3833 simulationStack.push(result);
3834 break;
3835 }
3837 auto shift = simulationStack.peek(0);
3838 auto value = simulationStack.peek(1);
3839 simulationStack.pop();
3840 simulationStack.pop();
3841 // simulate
3842 auto result = bitwiseShiftRight(value, shift);
3843 simulationStack.push(result);
3844 break;
3845 }
3846 case IR::Opcode::less_than: {
3847 auto right = simulationStack.peek(0);
3848 auto left = simulationStack.peek(1);
3849 simulationStack.pop();
3850 simulationStack.pop();
3851 // simulate
3852 auto result = lessThan(left, right);
3853 simulationStack.push(result);
3854 break;
3855 }
3857 auto right = simulationStack.peek(0);
3858 auto left = simulationStack.peek(1);
3859 simulationStack.pop();
3860 simulationStack.pop();
3861 // simulate
3862 auto result = greaterThan(left, right);
3863 simulationStack.push(result);
3864 break;
3865 }
3867 auto right = simulationStack.peek(0);
3868 auto left = simulationStack.peek(1);
3869 simulationStack.pop();
3870 simulationStack.pop();
3871 // simulate
3872 auto result = greaterThanOrEqual(left, right);
3873 simulationStack.push(result);
3874 break;
3875 }
3877 auto right = simulationStack.peek(0);
3878 auto left = simulationStack.peek(1);
3879 simulationStack.pop();
3880 simulationStack.pop();
3881 // simulate
3882 auto result = greaterThanOrEqual(left, right);
3883 simulationStack.push(result);
3884 break;
3885 }
3886 case IR::Opcode::equal: {
3887 auto right = simulationStack.peek(0);
3888 auto left = simulationStack.peek(1);
3889 simulationStack.pop();
3890 simulationStack.pop();
3891 // simulate
3892 auto result = equal(left, right);
3893 simulationStack.push(result);
3894 break;
3895 }
3896 case IR::Opcode::not_equal: {
3897 auto right = simulationStack.peek(0);
3898 auto left = simulationStack.peek(1);
3899 simulationStack.pop();
3900 simulationStack.pop();
3901 // simulate
3902 auto result = notEqual(left, right);
3903 simulationStack.push(result);
3904 break;
3905 }
3907 auto varIndex = ins.operands[0].value.symbolIndex;
3908 auto varType = targetFunction->getVariableTable().get(varIndex);
3909
3910 if (auto it = variablesExtraInfo.find(varIndex); it != variablesExtraInfo.end() && it->second.hasPossibleValue) {
3911 simulationStack.push(it->second.possibleValue);
3912 } else {
3913 simulationStack.push(varType, {currentCodeBlockIndex, {insIndex}});
3914 }
3915
3916 if (auto it = variablesExtraInfo.find(varIndex); it != variablesExtraInfo.end()) {
3917 it->second.isReadAfterStore = true;
3918 } else {
3919 variablesExtraInfo[varIndex] = {false, true, {}};
3920 }
3921 break;
3922 }
3924 auto varIndex = ins.operands[0].value.symbolIndex;
3925 auto value = simulationStack.peek(0);
3926 simulationStack.pop();
3927
3928 variablesExtraInfo[varIndex] = {value.hasPossibleValue, false, value};
3929 break;
3930 }
3932 // as for global variables, we can't optimize it
3933 auto moduleIndex = ins.operands[0].value.symbolIndex;
3934 auto type = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
3935 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}});
3936 break;
3937 }
3939 // check the definition type and value type here
3940 auto moduleIndex = ins.operands[0].value.symbolIndex;
3941 auto definitionType = compilerCtx->getImportedModule(moduleIndex)->globalVariables[ins.operands[1].value.symbolIndex];
3942 auto value = simulationStack.peek(0);
3943
3944 if (value.type->isForeignBasicType()) {
3945 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
3946 }
3947
3948 if (*definitionType != *value.type && value.type->type == IRValueType::valueType::null &&
3950 // type mismatch, panic
3951 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::analyzeBlock(): store_global: type mismatch");
3952 }
3953
3954 simulationStack.pop();
3955 break;
3956 }
3958 // we can't optimize it
3959 auto value = simulationStack.peek(0);
3960 auto type = value.type->typeIndex;
3961 auto targetModule = compilerCtx->getImportedModule(value.type->typeAffiliateModule);
3962 auto structDef = targetModule->structTable[type];
3963 auto memberIndex = ins.operands[0].value.symbolIndex;
3964 auto memberDef = managedPtr(*structDef->fieldTypes[memberIndex]);
3965 if (memberDef->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
3966 memberDef->addAttribute(IRValueType::ValueAttr::Raw);
3967 }
3968 simulationStack.pop();
3969 simulationStack.push(
3970 memberDef, value.contributedInstructions + SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}});
3971 break;
3972 }
3974 // we can't optimize it
3975 auto value = simulationStack.peek(1);
3976 auto type = simulationStack.peek(0).type->typeIndex;
3977 auto structDef = compilerCtx->getImportedModule(simulationStack.peek(0).type->typeAffiliateModule)->structTable[type];
3978 auto memberIndex = ins.operands[0].value.symbolIndex;
3979 auto memberDef = structDef->fieldTypes[memberIndex];
3980
3981 if (value.type->isForeignBasicType()) {
3982 *value.type = compilerCtx->normalizeForeignBasicType(value.type);
3983 }
3984
3985 if (*memberDef != *value.type && value.type->type != IRValueType::valueType::pointerObject &&
3986 value.type->type != IRValueType::valueType::null) {
3987 // type mismatch, panic
3988 panic(ins.debugInfo.line, ins.debugInfo.column, "IROptimizer::analyzeBlock(): store_member: type mismatch");
3989 }
3990
3991 simulationStack.pop();
3992 simulationStack.pop();
3993 break;
3994 }
3996 case IR::Opcode::invoke: {
3997 // we can't optimize it
3998 // in case of which this got optimized in tempVar reduction, we set optimizable flag to false
3999 auto moduleIndex = ins.operands[0].value.symbolIndex;
4000 auto function = compilerCtx->getImportedModule(moduleIndex)->functionTable[ins.operands[1].value.symbolIndex];
4001 auto returnType = function->returnType;
4002 auto argTypes = function->argumentTypes;
4003 auto argCount = function->argumentTypes.size();
4004 SimulationStack::Item::ContributedInstructionSet contributedInstructions = {currentCodeBlockIndex, {insIndex}, false};
4005 for (int i = 0; i < argCount; i++) {
4006 contributedInstructions = contributedInstructions + simulationStack.peek(0).contributedInstructions;
4007 simulationStack.pop();
4008 }
4009 simulationStack.push(returnType, contributedInstructions);
4010 break;
4011 }
4013 auto function = compilerCtx->getIRFFITable()
4014 ->importedLibraries[ins.operands[0].value.symbolIndex]
4015 .importedFunctionTable[ins.operands[1].value.symbolIndex];
4016 auto returnType = function->returnType;
4017 auto argTypes = function->argumentTypes;
4018 auto argCount = function->argumentTypes.size();
4019 for (int i = 0; i < argCount; i++) {
4020 simulationStack.pop();
4021 }
4022 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4023 break;
4024 }
4026 auto argCount = ins.operands[3].value.symbolIndex;
4027 for (int i = 0; i < argCount - 1; i++) {
4028 simulationStack.pop();
4029 }
4030 auto returnType = compilerCtx->getImportedModule(ins.operands[0].value.symbolIndex)
4031 ->interfaceTable[ins.operands[1].value.symbolIndex]
4032 ->methodMap[ins.operands[2].value.symbolIndex]
4033 ->returnType;
4034 simulationStack.pop();
4035 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4036 break;
4037 }
4039 auto moduleIndex = ins.operands[0].value.symbolIndex;
4040 auto structDef = compilerCtx->getImportedModule(moduleIndex)->structTable[ins.operands[1].value.symbolIndex];
4041 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::structObject, moduleIndex, ins.operands[1].value.symbolIndex}),
4042 {currentCodeBlockIndex, {insIndex}, false});
4043 break;
4044 }
4046 auto moduleIndex = ins.operands[0].value.symbolIndex;
4047 auto interfaceImplDef = compilerCtx->getImportedModule(moduleIndex)->interfaceImplementationTable[ins.operands[1].value.symbolIndex];
4048 auto returnType = managedPtr(IRValueType{IRValueType::valueType::interfaceObject,
4049 interfaceImplDef->implInterfaceIndex.first,
4050 interfaceImplDef->implInterfaceIndex.second});
4051 simulationStack.pop();
4052 simulationStack.push(returnType, {currentCodeBlockIndex, {insIndex}, false});
4053 break;
4054 }
4057 case IR::Opcode::ret: {
4058 simulationStack.pop();
4059 break;
4060 }
4068 // we can't optimize it
4069 // dims in operands
4070 std::shared_ptr<IRValueType> baseType;
4071 switch (ins.opcode) {
4073 baseType = compilerCtx->getIntObjectType();
4074 break;
4076 baseType = compilerCtx->getBoolObjectType();
4077 break;
4079 baseType = compilerCtx->getCharObjectType();
4080 break;
4082 baseType = compilerCtx->getDeciObjectType();
4083 break;
4085 baseType = compilerCtx->getStrObjectType();
4086 break;
4088 baseType = compilerCtx->getShortObjectType();
4089 break;
4091 baseType = compilerCtx->getUnsignedObjectType();
4092 break;
4093 default:
4094 break;
4095 }
4096
4097 yoi::indexT size = 1;
4099 for (auto i = 1; i < ins.operands.size(); i++) {
4100 size *= ins.operands[i].value.symbolIndex;
4101 dims.push_back(ins.operands[i].value.symbolIndex);
4102 }
4103 for (yoi::indexT i = 0; i < ins.operands[0].value.symbolIndex; i++) {
4104 simulationStack.pop();
4105 }
4106 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
4107 break;
4108 }
4111 auto moduleIndex = ins.operands[0].value.symbolIndex;
4112 auto typeIndex = ins.operands[1].value.symbolIndex;
4113 yoi::indexT size = 1;
4115
4118 moduleIndex,
4119 typeIndex});
4120
4121 for (yoi::indexT i = 3; i < ins.operands.size(); i++) {
4122 size *= ins.operands[i].value.symbolIndex;
4123 dims.push_back(ins.operands[i].value.symbolIndex);
4124 }
4125 for (yoi::indexT i = 0; i < ins.operands[2].value.symbolIndex; i++) {
4126 simulationStack.pop();
4127 }
4128 simulationStack.push(managedPtr(baseType->getArrayType(dims)), {currentCodeBlockIndex, {insIndex}, false});
4129 break;
4130 }
4140 std::shared_ptr<IRValueType> baseType;
4141 switch (ins.opcode) {
4143 baseType = compilerCtx->getIntObjectType();
4144 break;
4146 baseType = compilerCtx->getBoolObjectType();
4147 break;
4149 baseType = compilerCtx->getCharObjectType();
4150 break;
4152 baseType = compilerCtx->getDeciObjectType();
4153 break;
4155 baseType = compilerCtx->getShortObjectType();
4156 break;
4158 baseType = compilerCtx->getUnsignedObjectType();
4159 break;
4161 baseType = compilerCtx->getStrObjectType();
4162 break;
4164 baseType = managedPtr(IRValueType{
4165 IRValueType::valueType::interfaceObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
4166 break;
4168 baseType = managedPtr(
4169 IRValueType{IRValueType::valueType::structObject, ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex});
4170 break;
4171 default:
4172 break;
4173 }
4174
4175 for (yoi::indexT i = 0; i < ins.operands.back().value.symbolIndex; i++) {
4176 simulationStack.pop();
4177 }
4178 simulationStack.pop(); // fuck array length
4179 simulationStack.push(managedPtr(baseType->getDynamicArrayType()), {currentCodeBlockIndex, {insIndex}, false});
4180 break;
4181 }
4183 // we can't optimize it
4184 auto index = simulationStack.peek(0);
4185 auto array = simulationStack.peek(1);
4186 simulationStack.pop();
4187 simulationStack.pop();
4188 simulationStack.push(managedPtr(array.type->getElementType()),
4189 array.contributedInstructions + index.contributedInstructions +
4190 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4191 break;
4192 }
4194 // we can't optimize it
4195 auto index = simulationStack.peek(0);
4196 auto value = simulationStack.peek(1);
4197 auto array = simulationStack.peek(2);
4198 simulationStack.pop();
4199 simulationStack.pop();
4200 simulationStack.pop();
4201 break;
4202 }
4203 case IR::Opcode::pop: {
4204 simulationStack.pop();
4205 break;
4206 }
4208 auto rhs = simulationStack.peek(0);
4209 auto lhs = simulationStack.peek(1);
4210
4211 if (rhs.type->isForeignBasicType()) {
4212 *rhs.type = rhs.type->getNormalizedForeignBasicType();
4213 }
4214 yoi_assert(*lhs.type == *rhs.type, 0, 0, "IROptimizer::analyzeBlock(): direct_assign: type mismatch");
4215 simulationStack.pop();
4216 simulationStack.pop();
4217 simulationStack.push(lhs.type,
4218 lhs.contributedInstructions + rhs.contributedInstructions +
4219 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4220 break;
4221 }
4223 auto type = managedPtr(IRValueType{static_cast<IRValueType::valueType>(ins.operands[0].value.symbolIndex),
4224 ins.operands[1].value.symbolIndex,
4225 ins.operands[2].value.symbolIndex,
4226 ins.operands[3].value.symbolIndex
4227 ? yoi::vec<yoi::indexT>{ins.operands[3].value.symbolIndex}
4229 type->addAttribute(IRValueType::ValueAttr::Nullable);
4230 simulationStack.pop();
4231 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4232 break;
4233 }
4235 simulationStack.pop();
4236 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, false});
4237 break;
4238 }
4239 case IR::Opcode::push_null: {
4240 simulationStack.push(managedPtr(IRValueType{IRValueType::valueType::pointerObject}), {currentCodeBlockIndex, {insIndex}, true});
4241 break;
4242 }
4244 auto array = simulationStack.peek(0);
4245 simulationStack.pop();
4246 if (array.type->isArrayType()) {
4247 yoi::indexT size = 1;
4248 for (auto dim : array.type->dimensions) {
4249 size *= dim;
4250 }
4251 simulationStack.push(compilerCtx->getIntObjectType(),
4252 array.contributedInstructions +
4253 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}},
4254 static_cast<int64_t>(size));
4255 } else if (array.type->isDynamicArrayType()) {
4256 simulationStack.push(compilerCtx->getIntObjectType(),
4257 array.contributedInstructions +
4258 SimulationStack::Item::ContributedInstructionSet{currentCodeBlockIndex, {insIndex}, false});
4259 }
4260 break;
4261 }
4263 // pop two values and push one boolean value
4264 auto interfaceType = simulationStack.peek(0).type;
4265 auto objectType = simulationStack.peek(1).type;
4266 simulationStack.pop();
4267 simulationStack.pop();
4268 simulationStack.push(compilerCtx->getBoolObjectType(), {currentCodeBlockIndex, {insIndex}, false});
4269 break;
4270 }
4272 simulationStack.push(compilerCtx->getIntObjectType(), {currentCodeBlockIndex, {insIndex}, false});
4273 break;
4274 }
4276 auto typeMod = ins.operands[0].value.symbolIndex;
4277 auto typeIndex = ins.operands[1].value.symbolIndex;
4278 auto type = managedPtr(IRValueType{IRValueType::valueType::datastructObject, typeMod, typeIndex});
4279 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4280 break;
4281 }
4283 for (yoi::indexT i = 0; i < ins.operands[0].value.symbolIndex; i++) {
4284 simulationStack.pop();
4285 }
4286 break;
4287 }
4289 simulationStack.pop();
4290 simulationStack.pop();
4291 break;
4292 }
4294 auto type = simulationStack.peek(0).type;
4295 simulationStack.pop();
4296 for (auto &operand : ins.operands) {
4297 auto def = compilerCtx->getImportedModule(type->typeAffiliateModule)->dataStructTable[type->typeIndex];
4298 type = def->fieldTypes[operand.value.symbolIndex];
4299 }
4300 simulationStack.push(type, {currentCodeBlockIndex, {insIndex}, false});
4301 break;
4302 }
4303 case IR::Opcode::yield: {
4304 simulationStack.pop();
4305 simulationStack.pop();
4306 break;
4307 }
4309 simulationStack.pop();
4310 break;
4311 }
4312 case IR::Opcode::resume: {
4313 simulationStack.pop();
4314 break;
4315 }
4316 default: {
4317 // pass
4318 break;
4319 }
4320 }
4321 }
4322
4323 IROptimizer::IROptimizer(const std::shared_ptr<compilerContext> &compilerCtx, yoi::indexT entryModuleIndex)
4324 : compilerCtx(compilerCtx), entryModuleIndex(entryModuleIndex) {}
4325
4327 callGraph[caller].insert(callee);
4328 callerGraph[callee].insert(caller);
4329 }
4330
4333 std::set<CallGraph::FuncIdentifier> visitedFunctions;
4334 std::queue<CallGraph::FuncIdentifier> q;
4335
4336 // build function set
4337 for (auto &module : compilerCtx->getCompiledModules()) {
4338 for (yoi::indexT i = 0; i < module.second->functionTable.size(); i++) {
4339 callGraph.functions.insert(CallGraph::FuncIdentifier{module.first, i});
4340 if (compilerCtx->getImportedModule(module.first)->functionTable[i]->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve))
4341 q.emplace(module.first, i);
4342 }
4343 }
4344
4345 for (auto &exportedFunction : compilerCtx->getIRFFITable()->exportedFunctionTable) {
4346 q.emplace(std::get<0>(exportedFunction.second), std::get<1>(exportedFunction.second));
4347 // add Preserve attribute to exported functions
4348 auto &func = compilerCtx->getImportedModule(std::get<0>(exportedFunction.second))->functionTable[std::get<1>(exportedFunction.second)];
4351 }
4352
4353 if (compilerCtx->getBuildConfig()->buildType == IRBuildConfig::BuildType::executable) {
4354 try {
4355 q.emplace(entryModuleIndex, compilerCtx->getImportedModule(entryModuleIndex)->functionTable.getIndex(L"main#"));
4356 // add Preserve attribute to main function
4357 auto &func = compilerCtx->getImportedModule(entryModuleIndex)
4358 ->functionTable[compilerCtx->getImportedModule(entryModuleIndex)->functionTable.getIndex(L"main#")];
4360 } catch (const std::out_of_range &) {
4361 panic(0, 0, "IROptimizer::buildCallGraph(): entry point not found");
4362 }
4363 }
4364
4365 while (!q.empty()) {
4366 auto function = q.front();
4367 q.pop();
4368
4369 if (visitedFunctions.contains(function))
4370 continue;
4371
4372 for (auto &i : compilerCtx->getImportedModule(function.first)->functionTable[function.second]->codeBlock) {
4373 for (auto &ins : i->getIRArray()) {
4374 if (ins.opcode == IR::Opcode::invoke || ins.opcode == IR::Opcode::invoke_dangling) {
4375 CallGraph::FuncIdentifier callee{ins.operands[0].value.symbolIndex, ins.operands[1].value.symbolIndex};
4376 callGraph.addCall(function, callee);
4377 q.push(callee);
4378 }
4379 }
4380 }
4381
4382 visitedFunctions.insert(function);
4383 }
4384
4386 }
4387
4389 for (const auto &function : functions) {
4390 if (callerGraph[function].size() == 0) {
4391 unreachableFunctions.insert(function);
4392 }
4393 }
4394 }
4395
4401
4403 bool is_param_equal = other.paramStates.size() == paramStates.size();
4404 for (yoi::indexT i = 0;i < other.paramStates.size() && is_param_equal;i++)
4405 is_param_equal = other.paramStates[i] == paramStates[i];
4406 return isReturnValueNullable != other.isReturnValueNullable || isReturnValueRaw != other.isReturnValueRaw || !is_param_equal;
4407 }
4408
4410 auto &varTable = targetFunction->variableTable.getVariables();
4411 if (targetFunction->argumentTypes.size()) {
4412 for (yoi::indexT paramIdx = 0; paramIdx < targetFunction->argumentTypes.size(); paramIdx++) {
4413 // capture the first scope which is full of parameters
4414 if (targetFunction->getVariableTable().scopeIndex(paramIdx) > 0)
4415 break;
4416 auto copied = managedPtr(*varTable[paramIdx]);
4417 if (!copied->hasAttribute(IRValueType::ValueAttr::NoBorrow) &&
4418 (!targetFunction->hasAttribute(IRFunctionDefinition::FunctionAttrs::Variadic) ||
4419 paramIdx != targetFunction->argumentTypes.size() - 1) &&
4420 !copied->hasAttribute(IRValueType::ValueAttr::Raw))
4421 copied->addAttribute(IRValueType::ValueAttr::Borrow);
4422 targetFunction->argumentTypes[paramIdx] = copied;
4423 varTable[paramIdx] = copied;
4424 }
4425 }
4426 return true;
4427 }
4428
4430 auto [successors, predecessors] = performCFGAnalysis();
4431
4432 std::map<indexT, AnalysisState> blockInStates;
4433 std::map<indexT, AnalysisState> blockOutStates;
4434 std::queue<indexT> worklist;
4435
4436 AnalysisState entryState;
4437
4438 if (!targetFunction->codeBlock.empty())
4439 worklist.push(0);
4440 blockInStates[0] = entryState;
4441
4442 while (!worklist.empty()) {
4443 indexT currentBlockIdx = worklist.front();
4444 worklist.pop();
4445
4446 AnalysisState inState = currentBlockIdx == 0 ? blockInStates[0] : AnalysisState{};
4447 if (predecessors.count(currentBlockIdx) > 0) {
4448 for (indexT predIdx : predecessors[currentBlockIdx]) {
4449 if (blockOutStates.count(predIdx) > 0)
4450 inState = mergeStatesForInterfaceAllocationReduction(inState, blockOutStates[predIdx]);
4451 }
4452 }
4453 blockInStates[currentBlockIdx] = inState;
4454
4455 AnalysisState newOutState = analyzeBlockForInterfaceAllocationReduction(currentBlockIdx, inState);
4456
4457 if (blockOutStates.find(currentBlockIdx) == blockOutStates.end() || blockOutStates[currentBlockIdx] != newOutState) {
4458 blockOutStates[currentBlockIdx] = newOutState;
4459 if (successors.count(currentBlockIdx) > 0) {
4460 for (indexT succIdx : successors[currentBlockIdx]) {
4461 worklist.push(succIdx);
4462 }
4463 }
4464 }
4465 }
4466
4467 // Apply results
4468 for (const auto &[varIndex, varType] : targetFunction->variableTable.getReversedVariableNameMap()) {
4469 bool isDelayedInterfaceImplInfoInitialized = false;
4470 std::pair<yoi::indexT, yoi::indexT> delayedInterfaceImplInfo{-1, -1};
4471 for (const auto &[blockIndex, outState] : blockOutStates) {
4472 if (outState.variableStates.count(varIndex) &&
4473 outState.variableStates.at(varIndex).possibleValue.metadata.hasMetadata(L"delayed_interface_impl")) {
4474 auto impl = outState.variableStates.at(varIndex).possibleValue.metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(
4475 L"delayed_interface_impl");
4476 if (!isDelayedInterfaceImplInfoInitialized) {
4477 delayedInterfaceImplInfo = impl;
4478 isDelayedInterfaceImplInfoInitialized = true;
4479 } else {
4480 if (delayedInterfaceImplInfo != impl || impl.first == -1 || delayedInterfaceImplInfo.first == -1) {
4481 delayedInterfaceImplInfo = {-1, -1};
4482 break;
4483 }
4484 }
4485 }
4486 }
4487 if (auto var = targetFunction->variableTable.getVariables()[varIndex]; delayedInterfaceImplInfo.first != -1) {
4488 var->metadata.setMetadata(L"regressed_interface_impl", delayedInterfaceImplInfo);
4489 } else if (isDelayedInterfaceImplInfoInitialized) {
4490 // printf("invalid metadata, detected, eliminating...\n");
4491 var->metadata.eraseMetadata(L"regressed_interface_impl");
4492 }
4493 }
4494
4495 return true;
4496 }
4497
4499 for (auto &[_, irModule] : compilerCtx->getCompiledModules()) {
4500 for (auto &struct_type : irModule->structTable) {
4501 for (auto &field : struct_type.second->fieldTypes) {
4502 field = managedPtr(*field);
4503 if (field->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
4504 field->addAttribute(IRValueType::ValueAttr::Raw);
4505 } else {
4506 field->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Nullable);
4507 }
4508 }
4509 }
4510 }
4511 return true;
4512 }
4513
4515 // patch the original definition with corresponding Raw and Nullable tag.
4516 for (auto &module : compilerCtx->getCompiledModules()) {
4517 for (auto &interface : module.second->interfaceTable) {
4518 // retarded abuse of auto pointers brought about many problems, including
4519 // where-the-heck-knows-where-the-nullable-is-coming-from things. Thus we completely erase all the tags
4520 // attached on the interface definitions before we took action.
4521 for (auto &method : interface.second->methodMap) {
4522 method.second->returnType = managedPtr(*method.second->returnType);
4523 method.second->returnType->attributes.clear();
4524 for (auto &arg : method.second->argumentTypes) {
4525 arg = managedPtr(*arg);
4526 arg->attributes.clear();
4527 }
4528 }
4529 for (auto &impl : interface.second->implementations) {
4530 auto targetedModule = compilerCtx->getImportedModule(std::get<1>(impl));
4531 auto implName = visitor::getInterfaceImplName(std::pair{module.first, module.second->interfaceTable.getIndex(interface.first)},
4532 managedPtr(IRValueType{std::get<0>(impl), std::get<1>(impl), std::get<2>(impl)}));
4533 auto implDef = targetedModule->interfaceImplementationTable[implName];
4534 for (yoi::indexT virtIndex = 0; virtIndex < implDef->virtualMethods.size(); virtIndex++) {
4535 auto virtDef = compilerCtx->getImportedModule(implDef->virtualMethods[virtIndex]->typeAffiliateModule)
4536 ->functionTable[implDef->virtualMethods[virtIndex]->typeIndex];
4537 auto targetDef = interface.second->methodMap[virtIndex];
4538 virtDef->argumentTypes[0]->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Borrow);
4539 for (yoi::indexT paramIndex = 0; paramIndex < targetDef->argumentTypes.size(); paramIndex++) {
4540 if (virtDef->argumentTypes[paramIndex]->isBasicType() && !virtDef->argumentTypes[paramIndex]->isArrayType() &&
4541 !virtDef->argumentTypes[paramIndex]->isDynamicArrayType() &&
4542 !virtDef->argumentTypes[paramIndex + 1]->hasAttribute(IRValueType::ValueAttr::Raw)) {
4543 targetDef->argumentTypes[paramIndex]
4544 ->removeAttribute(IRValueType::ValueAttr::Raw)
4545 .addAttribute(IRValueType::ValueAttr::Nullable);
4546 }
4547 }
4548 // patch the function return type as well
4549 if (virtDef->returnType->isBasicType() && !virtDef->returnType->isArrayType() && !virtDef->returnType->isDynamicArrayType() &&
4550 !virtDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw)) {
4551 targetDef->returnType->removeAttribute(IRValueType::ValueAttr::Raw).addAttribute(IRValueType::ValueAttr::Nullable);
4552 }
4553 }
4554 }
4555 }
4556
4557 // add Raw tag if no Nullable tag present for basic types in interface methods.
4558 for (auto &interface : module.second->interfaceTable) {
4559 for (auto &virtIndex : interface.second->methodMap) {
4560 for (auto &i : virtIndex.second->argumentTypes) {
4561 if (!i->hasAttribute(IRValueType::ValueAttr::Nullable) && i->isBasicType() && i->dimensions.empty())
4562 i->addAttribute(IRValueType::ValueAttr::Raw);
4563 }
4564 if (!virtIndex.second->returnType->hasAttribute(IRValueType::ValueAttr::Nullable) &&
4565 virtIndex.second->returnType->isBasicType() && virtIndex.second->returnType->dimensions.empty()) {
4566 virtIndex.second->returnType->addAttribute(IRValueType::ValueAttr::Raw);
4567 }
4568 }
4569 }
4570
4571 auto interface_wrapper_generator = [&](yoi::indexT moduleIndex,
4572 yoi::indexT funcIndex,
4573 const yoi::vec<std::shared_ptr<IRValueType>> &targetTypes,
4574 const std::shared_ptr<IRValueType> &returnType) -> yoi::indexT {
4575 auto originalFunc = compilerCtx->getImportedModule(moduleIndex)->functionTable[funcIndex];
4576 auto builder = IRFunctionDefinition::Builder()
4577 .setReturnType(returnType)
4578 .setDebugInfo(originalFunc->debugInfo)
4579 .setName(originalFunc->name + L"#wrapper");
4580 builder.addArgument(L"this", managedPtr(IRValueType{*originalFunc->argumentTypes[0]}.addAttribute(IRValueType::ValueAttr::Borrow)));
4581 for (yoi::indexT index = 0; index < targetTypes.size(); index++)
4582 builder.addArgument(L"param" + std::to_wstring(index),
4583 managedPtr(IRValueType{*targetTypes[index]}.addAttribute(IRValueType::ValueAttr::Borrow)));
4585 auto index = compilerCtx->getImportedModule(moduleIndex)->functionTable.put(originalFunc->name + L"wrapper", builder.yield());
4586 auto moduleCtx = compilerCtx->getModuleContext(moduleIndex);
4587 moduleCtx->pushIRBuilder(IRBuilder(
4588 compilerCtx, compilerCtx->getImportedModule(moduleIndex), compilerCtx->getImportedModule(moduleIndex)->functionTable[index]));
4589 moduleCtx->getIRBuilder().switchCodeBlock(moduleCtx->getIRBuilder().createCodeBlock());
4590 moduleCtx->getIRBuilder().loadOp(IR::Opcode::load_local,
4591 IROperand{IROperand::operandType::index, IROperand::operandValue{yoi::indexT{0}}},
4592 originalFunc->argumentTypes[0]);
4593 for (yoi::indexT index = 1; index < originalFunc->argumentTypes.size(); index++) {
4594 moduleCtx->getIRBuilder().loadOp(IR::Opcode::load_local, IROperand{IROperand::operandType::index, index}, targetTypes[index - 1]);
4595 }
4596 moduleCtx->getIRBuilder().invokeMethodOp(funcIndex, targetTypes.size(), originalFunc->returnType, false, true, moduleIndex);
4597 moduleCtx->getIRBuilder().retOp(returnType->type == IRValueType::valueType::none);
4598 moduleCtx->getIRBuilder().yield();
4599 moduleCtx->popIRBuilder();
4600 return index;
4601 };
4602
4603 // check the implementation of interface methods again for any misalignment, if presents, generate a wrapper
4604 // method to fix it.
4605 for (auto &interface : module.second->interfaceTable) {
4606 for (auto &impl : interface.second->implementations) {
4607 // module.second->interfaceImplementationTable.contains()
4608 auto targetedModule = compilerCtx->getImportedModule(std::get<1>(impl));
4609 auto implName = visitor::getInterfaceImplName(std::pair{module.first, module.second->interfaceTable.getIndex(interface.first)},
4610 managedPtr(IRValueType{std::get<0>(impl), std::get<1>(impl), std::get<2>(impl)}));
4611 auto implDef = targetedModule->interfaceImplementationTable[implName];
4612 for (yoi::indexT virtIndex = 0; virtIndex < implDef->virtualMethods.size(); virtIndex++) {
4613 auto virtDef = compilerCtx->getImportedModule(implDef->virtualMethods[virtIndex]->typeAffiliateModule)
4614 ->functionTable[implDef->virtualMethods[virtIndex]->typeIndex];
4615 auto targetDef = interface.second->methodMap[virtIndex];
4616 bool needWrapper = false;
4617 for (yoi::indexT paramIndex = 0; paramIndex < targetDef->argumentTypes.size(); paramIndex++) {
4618 if (virtDef->argumentTypes[paramIndex + 1]->hasAttribute(IRValueType::ValueAttr::Raw) &&
4619 !targetDef->argumentTypes[paramIndex]->hasAttribute(IRValueType::ValueAttr::Raw)) {
4620 needWrapper = true;
4621 }
4622 }
4623 if (virtDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw) &&
4624 !targetDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw)) {
4625 needWrapper = true;
4626 }
4627 if (needWrapper)
4628 implDef->virtualMethods[virtIndex]->typeIndex =
4629 interface_wrapper_generator(implDef->virtualMethods[virtIndex]->typeAffiliateModule,
4630 implDef->virtualMethods[virtIndex]->typeIndex,
4631 targetDef->argumentTypes,
4632 targetDef->returnType);
4633 }
4634 }
4635 }
4636 }
4637 return true;
4638 }
4639
4640 std::pair<std::map<indexT, std::vector<indexT>>, std::map<indexT, std::vector<indexT>>> IRFunctionOptimizer::performCFGAnalysis() {
4641 std::map<indexT, std::vector<indexT>> successors;
4642 std::map<indexT, std::vector<indexT>> predecessors;
4643
4644 for (auto i = 0; i < targetFunction->codeBlock.size(); i++) {
4645 if (successors.find(i) == successors.end())
4646 successors[i] = {};
4647 if (predecessors.find(i) == predecessors.end())
4648 predecessors[i] = {};
4649 if (!targetFunction->codeBlock[i]->getIRArray().empty()) {
4650 auto &lastIns = targetFunction->codeBlock[i]->getIRArray().back();
4651 bool isTerminator =
4652 (lastIns.opcode == IR::Opcode::jump || lastIns.opcode == IR::Opcode::jump_if_false ||
4653 lastIns.opcode == IR::Opcode::jump_if_true || lastIns.opcode == IR::Opcode::ret || lastIns.opcode == IR::Opcode::ret_none);
4654 if (!isTerminator && (i + 1 < targetFunction->codeBlock.size())) {
4655 successors[i].push_back(i + 1);
4656 predecessors[i + 1].push_back(i);
4657 }
4658 }
4659 for (auto &ins : targetFunction->codeBlock[i]->getIRArray()) {
4660 switch (ins.opcode) {
4661 case IR::Opcode::jump: {
4662 indexT target = ins.operands[0].value.codeBlockIndex;
4663 successors[i].push_back(target);
4664 predecessors[target].push_back(i);
4665 break;
4666 }
4669 indexT target = ins.operands[0].value.codeBlockIndex;
4670 successors[i].push_back(target);
4671 if (i + 1 < targetFunction->codeBlock.size())
4672 successors[i].push_back(i + 1);
4673 predecessors[target].push_back(i);
4674 if (i + 1 < targetFunction->codeBlock.size())
4675 predecessors[i + 1].push_back(i);
4676 break;
4677 }
4678 default:
4679 break;
4680 }
4681 }
4682 }
4683 return std::make_pair(successors, predecessors);
4684 }
4685
4687 std::queue<CallGraph::FuncIdentifier> worklist;
4688 for (const auto &funcId : callGraph.functions) {
4690 worklist.push(funcId);
4691 }
4692
4693 auto paramStateToString = [](const yoi::vec<FunctionAnalysisInfo::ParameterState> &a) {
4694 yoi::wstr result = L"(";
4695 for (auto &i : a) {
4696 switch (i) {
4698 result += L"Raw ";
4699 break;
4701 result += L"Nullable ";
4702 break;
4704 result += L"Plain ";
4705 break;
4706 }
4707 }
4708 result.back() = ')';
4709 return result;
4710 };
4711
4712 auto paramStateComparator = [](const yoi::vec<FunctionAnalysisInfo::ParameterState> &a,
4714 bool result = a.size() == b.size();
4715 for (yoi::indexT i = 0; i < a.size() && result; i++)
4716 result = a[i] == b[i];
4717 return result;
4718 };
4719
4720 while (!worklist.empty()) {
4721 auto funcId = worklist.front();
4722 worklist.pop();
4723
4724 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4725 auto &func = targetedModule->functionTable[funcId.second];
4726
4727 // skip unreachable functions during analysis phase, unless they are preserved.
4728 if (callGraph.unreachableFunctions.count(funcId) && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve)) {
4729 continue;
4730 }
4731
4732 bool oldYieldValueNullable = functionAnalysisResults.at(funcId).isYieldValueNullable;
4733 bool oldYieldValueRaw = functionAnalysisResults.at(funcId).isYieldValueRaw;
4734
4735 IRFunctionOptimizer analyzer{compilerCtx, targetedModule, functionAnalysisResults};
4736 analyzer.setTargetFunction(func, funcId);
4737
4738 // Re-analyze the function to get its new properties
4739 bool newIsNullable = analyzer.performNullableCheck();
4740 bool newIsRaw = analyzer.performRawCheck();
4741
4742 FunctionAnalysisInfo &currentInfo = functionAnalysisResults.at(funcId);
4743
4745 for (yoi::indexT index = 0; index < func->argumentTypes.size(); index++) {
4746 auto &i = func->argumentTypes[index];
4747 if (i->hasAttribute(IRValueType::ValueAttr::Nullable)) {
4749 } else if (i->hasAttribute(IRValueType::ValueAttr::Raw)) {
4750 paramStates.push_back(FunctionAnalysisInfo::ParameterState::Raw);
4751 } else if (currentInfo.paramStates.empty() || currentInfo.paramStates[index] != FunctionAnalysisInfo::ParameterState::Nullable) {
4752 // we only add plain tag when both branch is plain to prevent loop
4753 paramStates.push_back(FunctionAnalysisInfo::ParameterState::Plain);
4754 } else {
4756 }
4757 }
4758
4759 if (currentInfo.isReturnValueNullable != newIsNullable || currentInfo.isReturnValueRaw != newIsRaw ||
4760 !paramStateComparator(paramStates, currentInfo.paramStates) || oldYieldValueNullable != currentInfo.isYieldValueNullable || oldYieldValueRaw != currentInfo.isYieldValueRaw) {
4761 // update the global results
4762 currentInfo.isReturnValueNullable = newIsNullable;
4763 currentInfo.isReturnValueRaw = newIsRaw;
4764
4765 // if they changed, add all CALLERS of this function back to the worklist
4766 // because their analysis might now be incorrect.
4767 if (callGraph.callerGraph.count(funcId)) {
4768 for (const auto &callerId : callGraph.callerGraph.at(funcId)) {
4769 set_current_file_path(func->debugInfo.sourceFile);
4770 // warning(func->debugInfo.line, func->debugInfo.column, "Function " + wstring2string(func->name) + " has been updated, adding its callers back to the worklist. \nBefore:" + wstring2string(paramStateToString(currentInfo.paramStates)) + "\nAfter :" + wstring2string(paramStateToString(paramStates)) + "\n");
4771 worklist.push(callerId);
4772 }
4773 }
4774
4775 currentInfo.paramStates = paramStates;
4776 }
4777 }
4778
4779 for (const auto &[funcId, analysisInfo] : functionAnalysisResults) {
4780 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4781 auto &func = targetedModule->functionTable[funcId.second];
4782
4783 // get a mutable reference to the function's return type
4784 auto returnType = managedPtr(*func->returnType);
4785
4786 // synchronize the Nullable attribute
4787 if (analysisInfo.isReturnValueNullable && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoRawAndNullOptimization)) {
4788 returnType->addAttribute(IRValueType::ValueAttr::Nullable);
4789 } else {
4790 // crucially, remove the attribute if the analysis proved non-nullability.
4791 returnType->removeAttribute(IRValueType::ValueAttr::Nullable);
4792 }
4793
4794 auto statesBefore = analysisInfo.paramStates;
4795
4796 for (yoi::indexT i = 0; i < analysisInfo.paramStates.size(); i++) {
4797 auto &paramType = func->argumentTypes[i];
4798 // the only shift that may take place is the plain to nullable.
4799 // we DO NOT modify the original Raw tag.
4800 switch (analysisInfo.paramStates[i]) {
4802 break;
4803 }
4805 paramType->addAttribute(IRValueType::ValueAttr::Nullable);
4806 break;
4807 }
4809 break;
4810 }
4811 }
4812 }
4813
4814 // synchronize the Raw attribute
4815 if (analysisInfo.isReturnValueRaw && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoRawAndNullOptimization)) {
4816 returnType->addAttribute(IRValueType::ValueAttr::Raw);
4817 } else {
4818 returnType->removeAttribute(IRValueType::ValueAttr::Raw);
4819 }
4820
4821 if (analysisInfo.isYieldValueRaw && func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Generator)) {
4822 auto ctxIndex = func->getVariableTable().lookup(L"__context__");
4823 auto ctxType = func->getVariableTable().get(ctxIndex);
4824 auto &yieldField = compilerCtx->getImportedModule(HOSHI_COMPILER_CTX_GLOB_ID_CONST)->structTable[ctxType->typeIndex]->fieldTypes[1];
4825
4826 if (yieldField->isBasicType() && yieldField->dimensions.empty()) {
4827 yieldField = managedPtr(*yieldField);
4828 yieldField->metadata.setMetadata(L"STRUCT_DATAFIELD", true);
4829 }
4830 }
4831
4832 func->returnType = returnType;
4833 }
4834
4835 for (const auto &funcId : callGraph.functions) {
4836 auto targetedModule = compilerCtx->getImportedModule(funcId.first);
4837 auto &func = targetedModule->functionTable[funcId.second];
4838
4839 if (callGraph.unreachableFunctions.count(funcId) && !func->hasAttribute(IRFunctionDefinition::FunctionAttrs::Preserve)) {
4841 func->codeBlock.clear();
4842 continue;
4843 }
4844
4845 set_current_file_path(func->debugInfo.sourceFile);
4846 IRFunctionOptimizer optimizer{compilerCtx, targetedModule, functionAnalysisResults};
4847 optimizer.setTargetFunction(func, funcId).doOptimizationForCurrentFunction();
4848 }
4849
4850 return true;
4851 }
4852} // 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:1374
Definition IR.h:264
IRDebugInfo debugInfo
Definition IR.h:369
yoi::vec< IROperand > operands
Definition IR.h:367
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:2898
constexpr E value(std::size_t i) noexcept
Definition magic_enum.h:668
std::string wstring2string(const std::wstring &v)
Definition def.cpp:184
std::shared_ptr< T > managedPtr(const T &v)
Definition def.hpp:324
void warning(yoi::indexT line, yoi::indexT col, const std::string &msg, const std::string &label)
Definition def.cpp:143
std::vector< t > vec
Definition def.hpp:53
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:171
void set_current_file_path(const std::wstring &path)
Definition def.cpp:113
std::wstring wstr
Definition def.hpp:48
uint64_t indexT
Definition def.hpp:51
AnalysisState mergeStates(const AnalysisState &s1, const AnalysisState &s2)
void panic(yoi::indexT line, yoi::indexT col, const std::string &msg)
Definition def.cpp:131
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:87
bool hasMetadata(const yoi::wstr &key) const
Definition IR.cpp:1584