• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <compiler/compiler_options.h>
17 #include <gtest/gtest.h>
18 
19 #include <bitset>
20 #include <vector>
21 
22 #include "assembly-emitter.h"
23 #include "assembly-parser.h"
24 #include "code_info/code_info_builder.h"
25 #include "libpandabase/utils/cframe_layout.h"
26 #include "runtime/bridge/bridge.h"
27 #include "runtime/include/runtime.h"
28 #include "runtime/include/stack_walker-inl.h"
29 #include "runtime/include/thread_scopes.h"
30 #include "runtime/mem/refstorage/global_object_storage.h"
31 #include "runtime/tests/test_utils.h"
32 
33 namespace panda::test {
34 
35 using namespace compiler;
36 
37 #define HOOK_ASSERT(cond, action)                                                     \
38     do {                                                                              \
39         if (!(cond)) {                                                                \
40             std::cerr << "ASSERT FAILED(" << __LINE__ << "): " << #cond << std::endl; \
41             action;                                                                   \
42         }                                                                             \
43     } while (0)
44 
45 class StackWalkerTest : public testing::Test {
46 public:
47     using Callback = int (*)(uintptr_t, uintptr_t);
48 
StackWalkerTest()49     StackWalkerTest()
50         : default_compiler_non_optimizing_(options.IsCompilerNonOptimizing()),
51           default_compiler_regex_(options.GetCompilerRegex())
52     {
53     }
54 
~StackWalkerTest()55     ~StackWalkerTest()
56     {
57         options.SetCompilerNonOptimizing(default_compiler_non_optimizing_);
58         options.SetCompilerRegex(default_compiler_regex_);
59     }
60 
SetUp()61     void SetUp() override
62     {
63 #ifndef PANDA_COMPILER_ENABLE
64         GTEST_SKIP();
65 #endif
66     }
67 
68     void TestModifyManyVregs(bool is_compiled);
69 
GetMethod(std::string_view method_name)70     static Method *GetMethod(std::string_view method_name)
71     {
72         PandaString descriptor;
73         auto *thread = MTManagedThread::GetCurrent();
74         thread->ManagedCodeBegin();
75         auto *extension = Runtime::GetCurrent()->GetClassLinker()->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
76         auto cls = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
77         thread->ManagedCodeEnd();
78         ASSERT(cls);
79         return cls->GetDirectMethod(utf::CStringAsMutf8(method_name.data()));
80     }
81 
82     static mem::Reference *global_obj;
83 
84 private:
85     bool default_compiler_non_optimizing_;
86     std::string default_compiler_regex_;
87 };
88 
89 mem::Reference *StackWalkerTest::global_obj;
90 
91 extern "C" int PandaRunnerHook();
92 extern "C" int StackWalkerHookAArch64Bridge();
93 
94 class PandaRunner {
95 public:
96     using Callback = int (*)(uintptr_t, uintptr_t);
97 
PandaRunner()98     PandaRunner()
99     {
100         auto exec_path = panda::os::file::File::GetExecutablePath();
101 
102         options_.SetBootPandaFiles({exec_path.Value() + "/../pandastdlib/arkstdlib.abc"});
103 
104         options_.SetShouldLoadBootPandaFiles(true);
105         options_.SetShouldInitializeIntrinsics(false);
106         options_.SetCompilerEnableJit(true);
107         options_.SetNoAsyncJit(true);
108     }
109 
110     ~PandaRunner() = default;
111 
Run(std::string_view source,Callback hook)112     void Run(std::string_view source, Callback hook)
113     {
114         auto finalizer = [](void *) {
115             callback_ = nullptr;
116             Runtime::Destroy();
117         };
118         std::unique_ptr<void, decltype(finalizer)> runtime_destroyer(&finalizer, finalizer);
119 
120         pandasm::Parser parser;
121 
122         Runtime::Create(options_);
123         auto thread = MTManagedThread::GetCurrent();
124         {
125             ScopedManagedCodeThread s(thread);
126             auto storage = Runtime::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage();
127             StackWalkerTest::global_obj =
128                 storage->Add(panda::mem::AllocateNullifiedPayloadString(1), mem::Reference::ObjectType::GLOBAL);
129         }
130         auto res = parser.Parse(source.data());
131         ASSERT_TRUE(res) << "Parse failed: " << res.Error().message << "\nLine " << res.Error().line_number << ": "
132                          << res.Error().whole_line;
133         auto pf = pandasm::AsmEmitter::Emit(res.Value());
134         Runtime::GetCurrent()->GetClassLinker()->AddPandaFile(std::move(pf));
135 
136         if (auto method = StackWalkerTest::GetMethod("hook"); method != nullptr) {
137             if constexpr (RUNTIME_ARCH == Arch::AARCH64) {
138                 // AARCH64: only used callee-saved registers are saved/restored in prologue/epilogue.
139                 // So the code must enter PandaRunnerHook through StackWalkerHookAArch64Bridge,
140                 // which saves all callee-saved registers before calling PandaRunnerHook.
141                 method->SetCompiledEntryPoint(reinterpret_cast<void *>(StackWalkerHookAArch64Bridge));
142             } else {
143                 method->SetCompiledEntryPoint(reinterpret_cast<void *>(PandaRunnerHook));
144             }
145             callback_ = hook;
146         }
147 
148         auto eres = Runtime::GetCurrent()->Execute("_GLOBAL::main", {});
149         ASSERT_TRUE(eres) << (unsigned)eres.Error();
150         ASSERT_EQ(eres.Value(), 0);
151     }
152 
GetRuntimeOptions()153     RuntimeOptions &GetRuntimeOptions()
154     {
155         return options_;
156     }
157 
GetCompilerOptions()158     auto &GetCompilerOptions()
159     {
160         return compiler::options;
161     }
162 
163 private:
164     friend int PandaRunnerHook();
165     RuntimeOptions options_;
166     static inline Callback callback_ {nullptr};
167 };
168 
PandaRunnerHook()169 NO_OPTIMIZE int PandaRunnerHook()
170 {
171     ASSERT(PandaRunner::callback_);
172     uintptr_t fp = 0;
173     uintptr_t lr = 0;
174     if constexpr (RUNTIME_ARCH == Arch::AARCH64) {
175         ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
176         fp = reinterpret_cast<uintptr_t>(ManagedThread::GetCurrent()->GetCurrentFrame());
177         lr = ManagedThread::GetCurrent()->GetNativePc();
178         return PandaRunner::callback_(lr, fp);
179     }
180     if constexpr (RUNTIME_ARCH == Arch::AARCH32) {
181         ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
182 #if (defined(__clang__) || defined(PANDA_TARGET_ARM64))
183         asm("ldr %0, [fp, #0]" : "=r"(fp));
184         asm("ldr %0, [fp, #4]" : "=r"(lr));
185 #else
186         // gcc compile header "push {r4, r11, lr}"
187         asm("ldr %0, [fp, #-4]" : "=r"(fp));
188         asm("ldr %0, [fp, #0]" : "=r"(lr));
189 #endif
190         ManagedThread::GetCurrent()->SetCurrentFrame(reinterpret_cast<Frame *>(fp));
191         return PandaRunner::callback_(lr, fp);
192     }
193     if constexpr (RUNTIME_ARCH == Arch::X86_64) {
194         ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
195         asm("movq (%%rbp), %0" : "=r"(fp));
196         asm("movq 8(%%rbp), %0" : "=r"(lr));
197         ManagedThread::GetCurrent()->SetCurrentFrame(reinterpret_cast<Frame *>(fp));
198         return PandaRunner::callback_(lr, fp);
199     }
200     return -1;
201 }
202 
203 template <typename T>
ConvertToU64(T val)204 uint64_t ConvertToU64(T val)
205 {
206     if constexpr (sizeof(T) == sizeof(uint32_t)) {
207         return bit_cast<uint32_t>(val);
208     } else if constexpr (sizeof(T) == sizeof(uint64_t)) {
209         return bit_cast<uint64_t>(val);
210     } else {
211         return static_cast<uint64_t>(val);
212     }
213 }
214 
215 template <Arch arch>
ToCalleeRegister(size_t reg)216 int32_t ToCalleeRegister(size_t reg)
217 {
218     return reg + GetFirstCalleeReg(arch, false);
219 }
220 
221 template <Arch arch>
ToCalleeFpRegister(size_t reg)222 int32_t ToCalleeFpRegister(size_t reg)
223 {
224     return reg + GetFirstCalleeReg(arch, true);
225 }
226 
TEST_F(StackWalkerTest,ModifyVreg)227 TEST_F(StackWalkerTest, ModifyVreg)
228 {
229 // In Release and FastVerify modes compiler can omit frame pointer,
230 // thus PandaRunner can't work properly in these modes.
231 #if defined(NDEBUG) || defined(PANDA_FAST_VERIFY)
232     if constexpr (RUNTIME_ARCH == Arch::X86_64) {
233         GTEST_SKIP();
234     }
235 #endif
236     if constexpr (RUNTIME_ARCH == Arch::AARCH64) {
237         GTEST_SKIP();
238     }
239     auto source = R"(
240         .function i32 main() {
241             movi.64 v0, 27
242         try_begin:
243             call.short testa, v0
244             jmp try_end
245         try_end:
246             return
247         .catchall try_begin, try_end, try_end
248         }
249         .function i32 testa(i64 a0) {
250             call.short testb, a0
251             return
252         }
253         .function i32 testb(i64 a0) {
254             call.short testc, a0
255             return
256         }
257         .function i32 testc(i64 a0) {
258             lda a0
259         try_begin:
260             call.short hook  # change vregs in all frames
261             jnez exit
262             call.short hook  # verify vregs in all frames
263             jmp exit
264         exit:
265             return
266         .catchall try_begin, exit, exit
267         }
268         .function i32 hook() {
269             ldai 1
270             return
271         }
272     )";
273 
274     PandaRunner runner;
275     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
276     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
277     runner.GetCompilerOptions().SetCompilerRematConst(false);
278     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::testb|_GLOBAL::hook).*");
279     [[maybe_unused]] static constexpr std::array<uint64_t, 3> frame_values = {0x123456789abcdef, 0xaaaabbbbccccdddd,
280                                                                               0xabcdef20};
281     static int run_count = 0;
282     runner.Run(source, [](uintptr_t lr, [[maybe_unused]] uintptr_t fp) -> int {
283         StackWalker walker(reinterpret_cast<void *>(fp), true, lr);
284         bool success = false;
285         walker.NextFrame();
286         if (run_count == 0) {
287             bool was_set = false;
288             HOOK_ASSERT(!walker.IsCFrame(), return 1);
289             success = walker.IterateVRegsWithInfo([&was_set, &walker](const auto &reg_info, const auto &reg) {
290                 if (!reg_info.IsAccumulator()) {
291                     HOOK_ASSERT(reg.GetLong() == 27, return false);
292                     walker.SetVRegValue(reg_info, frame_values[0]);
293                     was_set = true;
294                 }
295                 return true;
296             });
297             HOOK_ASSERT(success, return 1);
298             HOOK_ASSERT(was_set, return 1);
299 
300             walker.NextFrame();
301             HOOK_ASSERT(walker.IsCFrame(), return 1);
302             success = walker.IterateVRegsWithInfo([&walker](const auto &reg_info, const auto &reg) {
303                 if (!reg_info.IsAccumulator()) {
304                     HOOK_ASSERT(reg.GetLong() == 27, return false);
305                     walker.SetVRegValue(reg_info, frame_values[1]);
306                 }
307                 return true;
308             });
309             HOOK_ASSERT(success, return 1);
310 
311             walker.NextFrame();
312             HOOK_ASSERT(walker.IsCFrame(), return 1);
313             success = walker.IterateVRegsWithInfo([&walker](const auto &reg_info, const auto &reg) {
314                 if (!reg_info.IsAccumulator()) {
315                     HOOK_ASSERT(reg.GetLong() == 27, return true;);
316                     walker.SetVRegValue(reg_info, frame_values[2]);
317                 }
318                 return true;
319             });
320             HOOK_ASSERT(success, return 1);
321         } else if (run_count == 1) {
322             HOOK_ASSERT(!walker.IsCFrame(), return 1);
323             success = walker.IterateVRegsWithInfo([](const auto &reg_info, const auto &reg) {
324                 if (!reg_info.IsAccumulator()) {
325                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(frame_values[0]), return true;);
326                 }
327                 return true;
328             });
329             HOOK_ASSERT(success, return 1);
330 
331             walker.NextFrame();
332             HOOK_ASSERT(walker.IsCFrame(), return 1);
333             success = walker.IterateVRegsWithInfo([](const auto &reg_info, const auto &reg) {
334                 if (!reg_info.IsAccumulator()) {
335                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(frame_values[1]), return true;);
336                 }
337                 return true;
338             });
339             HOOK_ASSERT(success, return 1);
340 
341             walker.NextFrame();
342             HOOK_ASSERT(walker.IsCFrame(), return 1);
343             success = walker.IterateVRegsWithInfo([](const auto &reg_info, const auto &reg) {
344                 if (!reg_info.IsAccumulator()) {
345                     HOOK_ASSERT(reg.GetLong() == bit_cast<int64_t>(frame_values[2]), return true;);
346                 }
347                 return true;
348             });
349             HOOK_ASSERT(success, return 1);
350         } else {
351             return 1;
352         }
353         run_count++;
354         return 0;
355     });
356     ASSERT_EQ(run_count, 2);
357 }
358 
TestModifyManyVregs(bool is_compiled)359 void StackWalkerTest::TestModifyManyVregs(bool is_compiled)
360 {
361     auto source = R"(
362         .function i32 main() {
363             movi.64 v0, 5
364         try_begin:
365             call.short test
366             jmp try_end
367         try_end:
368             return
369 
370         .catchall try_begin, try_end, try_end
371         }
372 
373         .function i32 test() {
374             movi.64 v1, 1
375             movi.64 v2, 2
376             movi.64 v3, 3
377             movi.64 v4, 4
378             movi.64 v5, 5
379             movi.64 v6, 6
380             movi.64 v7, 7
381             movi.64 v8, 8
382             movi.64 v9, 9
383             movi.64 v10, 10
384             movi.64 v11, 11
385             movi.64 v12, 12
386             movi.64 v13, 13
387             movi.64 v14, 14
388             movi.64 v15, 15
389             movi.64 v16, 16
390             movi.64 v17, 17
391             movi.64 v18, 18
392             movi.64 v19, 19
393             movi.64 v20, 20
394             movi.64 v21, 21
395             movi.64 v22, 22
396             movi.64 v23, 23
397             movi.64 v24, 24
398             movi.64 v25, 25
399             movi.64 v26, 26
400             movi.64 v27, 27
401             movi.64 v28, 28
402             movi.64 v29, 29
403             movi.64 v30, 30
404             movi.64 v31, 31
405         try_begin:
406             mov v0, v31
407             newarr v0, v0, i32[]
408             call.short stub
409             jmp try_end
410         try_end:
411             return
412 
413         .catchall try_begin, try_end, try_end
414         }
415 
416         .function i32 stub() {
417         try_begin:
418             call.short hook  # change vregs in all frames
419             jnez exit
420             call.short hook  # verify vregs in all frames
421             jmp exit
422         exit:
423             return
424 
425         .catchall try_begin, exit, exit
426         }
427 
428         .function i32 hook() {
429             ldai 1
430             return
431         }
432     )";
433 
434     static bool first_run;
435     static bool compiled;
436 
437     PandaRunner runner;
438     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
439     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
440 
441     if (!is_compiled) {
442         runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main)(?!_GLOBAL::test)(?!_GLOBAL::hook).*");
443     }
444 
445     first_run = true;
446     compiled = is_compiled;
447     runner.Run(source, [](uintptr_t lr, [[maybe_unused]] uintptr_t fp) -> int {
448         StackWalker walker(reinterpret_cast<void *>(fp), true, lr);
449 
450         HOOK_ASSERT(walker.GetMethod()->GetFullName() == "_GLOBAL::stub", return 1);
451         walker.NextFrame();
452         HOOK_ASSERT(walker.GetMethod()->GetFullName() == "_GLOBAL::test", return 1);
453         HOOK_ASSERT(walker.IsCFrame() == compiled, return 1);
454 
455         int reg_index = 1;
456         bool success = false;
457         auto obj = Runtime::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage()->Get(StackWalkerTest::global_obj);
458         if (first_run) {
459             success = walker.IterateVRegsWithInfo([&reg_index, &walker, &obj](const auto &reg_info, const auto &reg) {
460                 if (!reg_info.IsAccumulator()) {
461                     if (reg.HasObject()) {
462                         HOOK_ASSERT(reg.GetReference() != nullptr, return false);
463                         walker.SetVRegValue(reg_info, obj);
464                     } else if (reg_info.GetLocation() != VRegInfo::Location::CONSTANT) {
465                         // frame_size is more than nregs_ now for inst `V4_V4_ID16` and `V4_V4_V4_V4_ID16`
466                         HOOK_ASSERT(reg.GetLong() == 0 || reg_info.GetIndex() == reg.GetLong(), return false);
467                         walker.SetVRegValue(reg_info, reg_info.GetIndex() + 100000000000);
468                     }
469                     reg_index++;
470                 }
471                 return true;
472             });
473             HOOK_ASSERT(success, return 1);
474             HOOK_ASSERT(reg_index >= 32, return 1);
475             first_run = false;
476         } else {
477             success = walker.IterateVRegsWithInfo([&reg_index, &obj](const auto &reg_info, const auto &reg) {
478                 if (!reg_info.IsAccumulator()) {
479                     if (reg.HasObject()) {
480                         HOOK_ASSERT((reg.GetReference() == reinterpret_cast<ObjectHeader *>(Low32Bits(obj))),
481                                     return false);
482                     } else {
483                         if (reg_info.GetLocation() != VRegInfo::Location::CONSTANT) {
484                             HOOK_ASSERT(reg.GetLong() == (reg_info.GetIndex() + 100000000000), return false);
485                         }
486                         reg_index++;
487                     }
488                 }
489                 return true;
490             });
491             HOOK_ASSERT(success, return 1);
492             HOOK_ASSERT(reg_index >= 32, return 1);
493         }
494 
495         HOOK_ASSERT(success, return 1);
496         return 0;
497     });
498 }
499 
TEST_F(StackWalkerTest,ModifyMultipleVregs)500 TEST_F(StackWalkerTest, ModifyMultipleVregs)
501 {
502 // In Release and FastVerify modes compiler can omit frame pointer, thus, PandaRunner can't work properly in these
503 // modes.
504 #if defined(NDEBUG) || defined(PANDA_FAST_VERIFY)
505     if constexpr (RUNTIME_ARCH == Arch::X86_64) {
506         GTEST_SKIP();
507     }
508 #endif
509     if constexpr (ArchTraits<RUNTIME_ARCH>::SUPPORT_DEOPTIMIZATION) {
510         TestModifyManyVregs(true);
511         TestModifyManyVregs(false);
512     }
513 }
514 
TEST_F(StackWalkerTest,ThrowExceptionThroughMultipleFrames)515 TEST_F(StackWalkerTest, ThrowExceptionThroughMultipleFrames)
516 {
517     auto source = R"(
518         .record E {}
519 
520         .function u1 f4() {
521             newobj v0, E
522             throw v0
523             return
524         }
525 
526         .function u1 f3() {
527             call f4
528             return
529         }
530 
531         .function u1 f2() {
532             call f3
533             return
534         }
535 
536         .function u1 f1() {
537             call f2
538             return
539         }
540 
541         .function u1 main() {
542         try_begin:
543             movi v0, 123
544             call f1
545             ldai 1
546             return
547         try_end:
548 
549         catch_block1_begin:
550             movi v1, 123
551             lda v0
552             jne v1, exit_1
553             ldai 0
554             return
555             exit_1:
556             ldai 1
557             return
558 
559         .catch E, try_begin, try_end, catch_block1_begin
560         }
561     )";
562 
563     PandaRunner runner;
564     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
565     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
566 
567     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
568     runner.Run(source, nullptr);
569 
570     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2).*");
571     runner.Run(source, nullptr);
572 
573     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4|_GLOBAL::f2).*");
574     runner.Run(source, nullptr);
575 
576     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f4).*");
577     runner.Run(source, nullptr);
578 
579     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
580     runner.Run(source, nullptr);
581 
582     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main).*");
583     runner.Run(source, nullptr);
584 
585     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
586     runner.Run(source, nullptr);
587 
588     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f1|_GLOBAL::f2).*");
589     runner.Run(source, nullptr);
590 
591     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4|_GLOBAL::f2).*");
592     runner.Run(source, nullptr);
593 
594     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f4).*");
595     runner.Run(source, nullptr);
596 
597     runner.GetCompilerOptions().SetCompilerRegex(".*");
598     runner.Run(source, nullptr);
599 
600     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f1|_GLOBAL::f2|_GLOBAL::f3).*");
601     runner.Run(source, nullptr);
602 
603     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f1|_GLOBAL::f2).*");
604     runner.Run(source, nullptr);
605 
606     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::f2).*");
607     runner.Run(source, nullptr);
608 
609     runner.GetCompilerOptions().SetCompilerRegex(".*");
610     runner.Run(source, nullptr);
611 }
612 
TEST_F(StackWalkerTest,CatchInCompiledCode)613 TEST_F(StackWalkerTest, CatchInCompiledCode)
614 {
615     auto source = R"(
616         .record panda.String <external>
617         .record panda.ArrayIndexOutOfBoundsException <external>
618 
619         .function i32 f2() {
620             movi v0, 4
621             newarr v1, v0, i64[]
622             ldai 42
623             ldarr v1 # BoundsException
624             return
625         }
626 
627         .function i32 f1() {
628         try_begin:
629             call f2
630             ldai 1
631             return
632         try_end:
633 
634         catch_block1_begin:
635             ldai 123
636             return
637 
638         .catch panda.ArrayIndexOutOfBoundsException, try_begin, try_end, catch_block1_begin
639         }
640 
641         .function u1 main() {
642             call f1
643             movi v0, 123
644             jne v0, exit_1
645             ldai 0
646             return
647             exit_1:
648             ldai 1
649             return
650         }
651     )";
652 
653     PandaRunner runner;
654     runner.GetRuntimeOptions().SetCompilerHotnessThreshold(0);
655     runner.GetCompilerOptions().SetCompilerNonOptimizing(true);
656 
657     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f2).*");
658     runner.Run(source, nullptr);
659 
660     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main|_GLOBAL::f1).*");
661     runner.Run(source, nullptr);
662 
663     runner.GetCompilerOptions().SetCompilerRegex("(?!_GLOBAL::main).*");
664     runner.Run(source, nullptr);
665 
666     runner.GetCompilerOptions().SetCompilerRegex(".*");
667     runner.Run(source, nullptr);
668 }
669 
670 }  // namespace panda::test
671