【AI 使用案例】如何使用 AI 进行代码调试
在编译器中端构图开发中,需要处理字节码之间的控制流关系。当遇到 fall through(顺序执行到下一个字节码)或跳转时,需要正确设置当前基本块(CurrentBlock)。如果 CurrentBlock 未被正确设置,后续 fall through 进行 merge 的时候,会出现崩溃。(错误进行 BB 块的相连)CurrentBlock 设置错了,会导致错误判断了 fall-through
【AI 使用案例】如何使用 AI 进行代码调试
背景说明
在编译器中端构图开发中,需要处理字节码之间的关系。字节码在进入构图阶段前已进行 RPO(Reverse Post Order)排序。当遇到 fall through(顺序执行到下一个字节码)或跳转时,需要正确设置当前基本块(CurrentBlock)。
如果 CurrentBlock 未被正确设置,会导致没有联系的两条字节码,使用到同一个块。
案例一:RPO 排序后 Fall Through 路径错误
案例背景
测试代码(switch 表达式):
function switch_expr_test(x) {
let result = 0;
switch (true) {
case x > 0 && x < 3:
result += 100;
break;
case x >= 3 && x < 5:
result += 200;
break;
default:
result += 0;
break;
}
return result;
}
崩溃信息:
ASSERTION FAILED: index < GetInputCount()
IN ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_vertex.h:800: SetInput
Backtrace [tid=2076632]:
Thread 7 "OS_JIT_Thread" received signal SIGABRT, Aborted.
[Switching to Thread 0x7fffd173a640 (LWP 2076632)]
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140736707405376) at ./nptl/pthread_kill.c:44
44 ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=140736707405376) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=140736707405376) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (signo=6, threadid=140736707405376) at ./nptl/pthread_kill.c:89
#3 0x00007ffff3982476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4 0x00007ffff39687f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007ffff6e20365 in panda::debug::AssertionFail (expr=0x7fffc7371851 "index < GetInputCount()", file=<optimized out>, line=800, function=0x7fffc73511c8 "SetInput") at ../../arkcompiler/runtime_core/libpandabase/utils/debug.cpp:31
#6 0x00007fffcb4753b5 in panda::ecmascript::arksteed::Vertex::SetInput (this=0x7fff7c011c80, index=1, vertex=0x7fff7c00ce50) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_vertex.h:800
#7 0x00007fffcb485771 in panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergeValue (this=0x7fff7c010bf0, reg=..., merged=0x7fff7c011b08, unmerged=0x7fff7c00ce50)
at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.cpp:188
#8 0x00007fffcb48763d in panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3::operator()(panda::ecmascript::arksteed::ValueVertex*&, panda::ecmascript::kungfu::VirtualRegister) const (this=0x7fffd1736ab8, value=@0x7fff7c011078: 0x7fff7c011b08, reg=...) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.cpp:199
#9 0x00007fffcb4876a8 in panda::ecmascript::arksteed::CompactInterpreterFrameState::ForEachParameter<panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3&>(unsigned long, panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3&) const (this=0x7fff7c010c40,
numParams=7, f=...) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.h:193
#10 0x00007fffcb48759f in panda::ecmascript::arksteed::CompactInterpreterFrameState::ForEachRegister<panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3&>(unsigned long, unsigned long, panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3&) (
this=0x7fff7c010c40, numVregs=8, f=...) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.h:217
#11 0x00007fffcb4859f3 in panda::ecmascript::arksteed::CompactInterpreterFrameState::ForEachValue<panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3>(unsigned long, unsigned long, panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis(panda::ecmascript::arksteed::InterpreterFrameState&, panda::ecmascript::arksteed::BB*)::$_3&&) (this=0x7fff7c010c40,
numParams=7, numVregs=8, f=...) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.h:231
#12 0x00007fffcb48562b in panda::ecmascript::arksteed::MergePointInterpreterFrameState::MergePhis (this=0x7fff7c010bf0, unmerged=..., predecessor=0x7fff7c011b08)
at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.cpp:197
#13 0x00007fffcb485466 in panda::ecmascript::arksteed::MergePointInterpreterFrameState::Merge (this=0x7fff7c010bf0, unmerged=..., predecessor=0x7fff7c011b08) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_framestate.cpp:155
#14 0x00007fffcb46ac26 in panda::ecmascript::arksteed::ArkSteedGraphBuilder::BuildBody (this=0x7fffd1736d40) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_graph_builder.cpp:172
#15 0x00007fffcb46a4b6 in panda::ecmascript::arksteed::ArkSteedGraphBuilder::Build (this=0x7fffd1736d40) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_graph_builder.cpp:40
#16 0x00007fffcb4655fb in panda::ecmascript::arksteed::ArkSteedCompilerTask::Compile (this=0x5555557d4820) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_compiler.cpp:62
#17 0x00007fffcb465b85 in ArkSteedCompile (compilerTask=0x5555557d4820, arkSteedTask=0x5555557d4670) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_compiler.cpp:120
#18 0x00007ffff75a3599 in panda::ecmascript::JitResources::Compile (this=0x55555566aa80, compiler=0x5555557d4820, jitTask=0x5555557d4670) at ../../arkcompiler/ets_runtime/ecmascript/jit/jit_resources.h:47
#19 0x00007ffff75a3554 in panda::ecmascript::Jit::JitCompile (this=0x7ffff7f92f78 <panda::ecmascript::Jit::GetInstance()::instance_>, compiler=0x5555557d4820, jitTask=0x5555557d4670) at ../../arkcompiler/ets_runtime/ecmascript/jit/jit.cpp:369
#20 0x00007ffff75b5781 in panda::ecmascript::JitTask::Optimize (this=0x5555557d4670) at ../../arkcompiler/ets_runtime/ecmascript/jit/jit_task.cpp:75
#21 0x00007ffff75b8584 in panda::ecmascript::JitTask::AsyncTask::Run (this=0x5555557d4c80, threadIndex=1) at ../../arkcompiler/ets_runtime/ecmascript/jit/jit_task.cpp:471
#22 0x00007ffff7c0a7ee in common::Runner::Run (this=0x5555556ceb10, threadId=1) at ../../arkcompiler/ets_runtime/common_components/taskpool/runner.cpp:135
#23 0x00007ffff7c0b47e in common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0::operator()() const (this=0x55555579dce8) at ../../arkcompiler/ets_runtime/common_components/taskpool/runner.cpp:32
#24 0x00007ffff7c0b455 in std::__invoke_impl<void, common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0>(std::__invoke_other, common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0&&) (__f=...) at /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:61
#25 0x00007ffff7c0b415 in std::__invoke<common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0>(std::__invoke_other, common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0&&) (__fn=...) at /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:96
#26 0x00007ffff7c0b3ed in std::thread::_Invoker<std::tuple<common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0> >::M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0x55555579dce8) at /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_thread.h:259
#27 0x00007ffff7c0b3c5 in std::thread::_Invoker<std::tuple<common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0> >::operator()() (this=0x55555579dce8) at /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_thread.h:266
#28 0x00007ffff7c0b319 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<common::Runner::Runner(unsigned int, std::function<void (unsigned long)>, std::function<void (unsigned long)>)::$_0> > >::_M_run() (this=0x55555579dce0) at /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_thread.h:211
#29 0x00007ffff3c65253 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#30 0x00007ffff39d4ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#31 0x00007ffff3a668d0 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) f 14
#14 0x00007fffcb46ac26 in panda::ecmascript::arksteed::ArkSteedGraphBuilder::BuildBody (this=0x7fffd1736d40) at ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_graph_builder.cpp:172
172 mergeStates_[index]->Merge(*currentFrameState_, prevBlock);
(gdb) p index
$1 = 32
问题描述:
在 ArkSteed 字节码解释器中,字节码在进入构图阶段前会进行 RPO(Reverse Post Order)排序。RPO 后,字节码的执行顺序会发生变化,导致原来的物理顺序(相邻字节码)不再是 RPO 顺序中的相邻位置。
现象:
- 字节码 35 (
lda v1) → 36 (add2) → 37 (return) 是物理顺序 - 但 RPO 后,字节码 36 可能会 fallthrough 到一个不相关的块(如 32)
根本原因:
当 nextRPOIndex != fallThroughIndex 时,说明 RPO 顺序中当前块的"下一个块"不是物理顺序上的下一个字节码,需要通过 Jump 来处理这个跳转。
编译后的字节码结构:
.function any test.#*#switch_expr_test(any a0, any a1, any a2, any a3) {
label_13:
label_0:
ldai 0x3
sta v0
ldai 0x0
sta v1
ldtrue
sta v2
lda v1
greater 0x0, a3
sta v3
callruntime.isfalse 0x1
jnez label_3 -- 10
label_2:
lda v0
less 0x2, a3
sta v3
label_3:
lda v3
strictnoteq 0x3, v2
jeqz label_4
label_5:
lda v0
greatereq 0x4, a3
sta v0
callruntime.isfalse 0x5 -- 20
jnez label_8
label_7:
ldai 0x5
less 0x6, a3
sta v0
label_8:
lda v0
strictnoteq 0x7, v2
jeqz label_9
jmp label_11
label_4:
ldai 0x64
add2 0x8, v1 --- 30
jmp label_12
label_9:
ldai 0xc8 --- 32
add2 0x9, v1
jmp label_12
label_11:
lda v1 --- 35
add2 0xa, v1 --- 36
label_12:
return --- 37
label_14:
}
字节码与源码对应表(行号从0开始):
| 行号 | 字节码 | 源码对应 |
|---|---|---|
| 0 | ldai 0x3 | 测试调用传入的参数 (switch_expr_test(3)) |
| 1 | sta v0 | v0 = x |
| 2 | ldai 0x0 | let result = 0 |
| 3 | sta v1 | v1 = result = 0 |
| 4 | ldtrue | switch (true) |
| 5 | sta v2 | v2 = true |
| 6 | lda v1 | 准备计算 x > 0 |
| 7 | greater 0x0, a3 | 计算 x > 0 |
| 8 | sta v3 | v3 = (x > 0) |
| 9 | callruntime.isfalse 0x1 | 检查条件是否为false |
| 10 | jnez label_3 | 若不为false则跳到label_3继续判断 x < 3 |
| 11 | lda v0 | 准备计算 x < 3 |
| 12 | less 0x2, a3 | 计算 x < 3 |
| 13 | sta v3 | v3 = (x < 3),此时v3 = (x > 0) && (x < 3) |
| 14 | lda v3 | 准备判断整个case条件 |
| 15 | strictnoteq 0x3, v2 | 比较 v3 !== true |
| 16 | jeqz label_4 | 若相等(条件为true),跳到label_4执行 result += 100 |
| 17 | lda v0 | 准备计算第二个case x >= 3 |
| 18 | greatereq 0x4, a3 | 计算 x >= 3 |
| 19 | sta v0 | 临时存结果 |
| 20 | callruntime.isfalse 0x5 | 检查是否为false |
| 21 | jnez label_8 | 若不为false则跳到label_8 |
| 22 | ldai 0x5 | 准备计算 x < 5 |
| 23 | less 0x6, a3 | 计算 x < 5 |
| 24 | sta v0 | 存结果 |
| 25 | lda v0 | 准备判断 |
| 26 | strictjoteq 0x7, v2 | 比较 v0 === true |
| 27 | jeqz label_9 | 若相等,跳到label_9执行 result += 200 |
| 28 | jmp label_11 | 跳到default分支 |
| 29 | ldai 0x64 | case 1: result += 100 (0x64=100) |
| 30 | add2 0x8, v1 | v1 = v1 + 100 |
| 31 | jmp label_12 | break,跳出switch |
| 32 | ldai 0xc8 | case 2: result += 200 (0xC8=200) |
| 33 | add2 0x9, v1 | v1 = v1 + 200 |
| 34 | jmp label_12 | break |
| 35 | lda v1 | default: 准备加0 |
| 36 | add2 0xa, v1 | v1 = v1 + 0 |
| 37 | return | return result |
崩溃信息
ASSERTION FAILED: index < GetInputCount()
IN ../../arkcompiler/ets_runtime/ecmascript/arksteed/arksteed_vertex.h:736: SetInput
崩溃栈:
#14 BuildBody at arksteed_graph_builder.cpp:191
-> mergeStates_[index]->Merge(*currentFrameState_, prevBlock);
含义:在设置 Vertex 输入时,index 超出了该 Vertex 的输入数量
问题代码
arksteed_graph_builder.cpp:178-187 - 原有代码没有处理 RPO 后的 fall through 情况:
ProcessBytecode();
// ❌ 缺失:RPO 后的 fall through 处理
问题根因深度分析
在 ArkSteed 字节码解释器中,字节码在进入构图阶段前会进行 RPO(Reverse Post Order)排序。RPO 后,字节码的执行顺序会发生变化。
核心问题:
- 字节码 35 → 36 → 37 是物理顺序
- 但 RPO 后,字节码 36 可能会 fallthrough 到一个不相关的块
- 当
nextRPOIndex != fallThroughIndex时,说明 RPO 顺序中当前块的"下一个块"不是物理顺序上的下一个字节码
修复方案
// arksteed_graph_builder.cpp:178-187
ProcessBytecode();
if (!iterator_.Done() && bytecodeInfo.needFallThrough()) {
uint32_t nextRPOIndex = iterator_.NextRPOIndex();
uint32_t fallThroughIndex = iterator_.NextIndex();
if (nextRPOIndex != fallThroughIndex) { // Jump
BB* block = FinishBlock<JumpVertex>({}, &jumpTargets_[fallThroughIndex]);
MergeIntoFrameState(block, fallThroughIndex);
}
}
问题验证
修复后:
- 当 RPO 顺序与物理顺序一致时:不创建额外跳转
- 当 RPO 顺序发生跳转时:显式创建 JumpVertex 连接到目标块
修改的完整代码
diff --git a/ecmascript/arksteed/arksteed_bytecode_iterator.h b/ecmascript/arksteed/arksteed_bytecode_iterator.h
index 333e2bc39..6c903b4d0 100644
--- a/ecmascript/arksteed/arksteed_bytecode_iterator.h
+++ b/ecmascript/arksteed/arksteed_bytecode_iterator.h
@@ -89,6 +89,12 @@ public:
return (*postOrderList_)[pos_] + 1;
}
+ uint32_t NextRPOIndex() const
+ {
+ ASSERT(!Done());
+ return (*postOrderList_)[pos_ + 1];
+ }
+
BytecodeInfo GetCurrentBytecodeInfo() const
{
uint32_t idx = Index();
diff --git a/ecmascript/arksteed/arksteed_graph_builder.cpp b/ecmascript/arksteed/arksteed_graph_builder.cpp
index 80f7ed3b9..fdf493f2c 100644
--- a/ecmascript/arksteed/arksteed_graph_builder.cpp
+++ b/ecmascript/arksteed/arksteed_graph_builder.cpp
@@ -161,6 +161,7 @@ bool ArkSteedGraphBuilder::BuildBody()
{
for (iterator_.GotoStart(); !iterator_.Done(); ++iterator_) {
uint32_t index = iterator_.Index();
+ auto bytecodeInfo = iterator_.GetCurrentBytecodeInfo();
MergePointInterpreterFrameState* mergeState = mergeStates_[index];
if (mergeState != nullptr) {
@@ -175,6 +176,15 @@ bool ArkSteedGraphBuilder::BuildBody()
}
ProcessBytecode();
+
+ if (!iterator_.Done() && bytecodeInfo.needFallThrough()) {
+ uint32_t nextRPOIndex = iterator_.NextRPOIndex();
+ uint32_t fallThroughIndex = iterator_.NextIndex();
+ if (nextRPOIndex != fallThroughIndex) { // Jump
+ BB* block = FinishBlock<JumpVertex>({}, &jumpTargets_[fallThroughIndex]);
+ MergeIntoFrameState(block, fallThroughIndex);
+ }
+ }
}
return true;
}
AI 辅助调试过程
第一步:AI 分析崩溃的原因
Q: 直接粘贴崩溃栈信息
A: 运行时Index=32和静态分析出来的前驱数量不同。
第二步:分析字节码和源码的对应关系
Q: 帮我分析一下字节码和源代码的对应关系(直接粘贴字节码和 test.ts 代码)
A: [AI 给出了字节码与源码对应表]
Q: 重新对应,行号从0开始,label 不算字节码,比如 add2 0x8, v1 是第 30 条字节码
A: [AI 给出了修正后的字节码与源码对应表]
第三步:修复代码
Q: 发现是 RPO 排序后,fall through 路径错误。请帮我看看当前的修改正确吗?(直接粘贴 git diff 出来的代码修改)
A: 分析了修改逻辑,确认是正确的。解释了 needFallThrough()、NextIndex()、NextRPOIndex() 的作用。
效率对比
| 调试方式 | 传统调试 | AI 辅助调试 |
|---|---|---|
| 分析崩溃原因 | 需要阅读大量代码猜测 | 直接定位到前驱数量不匹配 |
| 理解字节码 | 手动逐行对应(约30分钟) | AI 快速给出对应表(约1分钟) |
| 代码修改 | 自己验证逻辑 | AI 检查修改是否正确 |
时间节省估算:
| 阶段 | 传统调试 | AI 辅助调试 | 提升 |
|---|---|---|---|
| 原因分析 | 1 小时 | 20 分钟 | 3x |
| 字节码对应 | 30 分钟 | 1 分钟 | 30x |
| 方案验证 | 10 分钟 | 2 分钟 | 5x |
思考
- 原因分析阶段,直接给出崩溃栈,AI 能快速分析出根因范围
- 字节码对应阶段,粘贴字节码和源码。AI 特别擅长做映射类工作,节省大量手动对应时间
- 方案验证阶段,写出发现的问题,请帮我看看当前的修改正确吗?AI 能快速理解改动并验证
更多推荐

所有评论(0)