Skip to content

feat: complete safe RV32 constant-load merge optimization - #35

Merged
jizhenjun merged 16 commits into
ScratchV-Compiler:mainfrom
yuki-328:feature/const_merge
Aug 26, 2026
Merged

feat: complete safe RV32 constant-load merge optimization#35
jizhenjun merged 16 commits into
ScratchV-Compiler:mainfrom
yuki-328:feature/const_merge

Conversation

@yuki-328

@yuki-328 yuki-328 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • reuse the shared assembly parser and canonicalize RV32 integer-register aliases
  • safely merge numeric lui + addi pairs and remove redundant lui only within basic blocks
  • reject relocations, out-of-range immediates, invalid registers, unknown-opcode state, and unsafe control-flow cases
  • preserve whitespace/comments and guarantee textual fixed-point idempotence
  • expose categorized statistics and integrate the pass with the compiler CLI
  • add real same-assembly A/B reporting plus clearly separated synthetic microbenchmarks
  • add a deterministic one-case report that executes before/after machine code with real TinyFive and forbids fallback
  • harden TinyFive/NumPy compatibility and RV32 li encoding across the signed 32-bit range
  • synchronize the development and technical-design documents with implemented behavior

Statistics semantics

  • candidate_pairs: structural same-block lui/addi candidates before safety checks
  • merged_pairs: candidates that pass register and immediate validation and are transformed
  • redundant_lui_removed: safely removed duplicate high-immediate loads

Real benchmark zero-hit cases are retained. li is treated as a pseudo-instruction, so source assembly reductions are not presented as machine-code or runtime wins.

Deterministic case report

The benchmark job now runs benchmarks/cases/const_merge_feature.asm through the configured CompilerDriver assembly post-pass and then executes the encoded before/after programs with the real TinyFive backend.

Metric Before After Reduction
Source assembly instructions 6 4 2
Encoded machine instructions 6 5 1
Code size 24 bytes 20 bytes 4 bytes
TinyFive executed instructions 6 5 1

Additional report results:

  • compiler_config_const_merge=true
  • feature_used=true
  • merged_pairs=1
  • redundant_lui_removed=1
  • backend=tinyfive
  • fallback=false
  • all 32 integer registers are equal before and after
  • observed values: x5=4098, x6=8195, x7=7

The difference between the source reduction (2) and machine-instruction reduction (1) is expected: li x5, 4098 is a pseudo-instruction and expands to a real lui/addi pair. This case demonstrates feature integration and execution equivalence; it is not presented as a real-workload speedup.

CI writes benchmark_reports/const_merge_report.json and benchmark_reports/const_merge_report.md, uploads them in the benchmark-reports artifact, and includes the Markdown result in the job summary.

The older generic tinyfive_compare.py path still falls back for instructions unsupported by the local encoder (slli/ecall). Its output now explicitly says static-analysis-fallback and is not used as proof of TinyFive execution. The deterministic report rejects any fallback instead.

CNN real-model A/B benchmark

The CI CNN step now invokes the standalone compiler with --const-merge. It generates baseline and optimized code from identical memory plans, then reports measured encoded output sizes rather than substituting expected values.

Metric Before After Reduction
Source assembly instructions 887 817 70
Encoded machine instructions 887 876 11
Code size 3,548 bytes 3,504 bytes 44 bytes (1.2%)

Pass statistics are candidate_pairs=70, merged_pairs=70, and redundant_lui_removed=0. Source-level merges exceed the machine-instruction reduction because large li pseudo-instructions still encode as lui + addi; 11 merged constants fit a single encoded instruction.

The 2.20B dynamic-instruction figure remains the existing analytical CNN estimate, not an end-to-end A/B simulation, so this report claims a measured static/code-size reduction rather than a fabricated runtime speedup.

Validation

  • pytest -q: 459 passed, 4 skipped, 0 failed
  • 4 skips require optional onnxruntime
  • deterministic case report: PASS, real TinyFive, fallback=false, output_equal=true
  • signed RV32 li boundary cases validated from -2147483648 through 2147483647
  • 10,000 randomized RV32 programs passed semantic-equivalence, textual-idempotence, and change-count invariants
  • compileall: passed
  • CI YAML parse: passed
  • git diff --check: passed

Known environment limitation

The deterministic A/B report now provides real TinyFive execution evidence. GNU RISC-V binutils/Spike external-toolchain validation is still pending and is not represented as completed.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 AI Code Review

共审查 10 个变更文件
⚠️ 另有 11 个文件超过上限(最多 10 个)未审查

📁 .github/workflows/ci.yml

🔴 Deploy 缺少 Pages 写入权限 — 顶级 permissions 移除了 pages: writeid-token: write,但 deploy 作业未显式声明权限,会继承顶级权限,导致 actions/deploy-pages 因缺少写入权限而失败。建议在 deploy 作业中添加 permissions: pages: write id-token: write 或恢复顶级权限。

🟡 测试重复运行Run all topic tests 未忽略 tests/test_const_merge.py,而 Run constant-merge regressions 又显式运行它,导致该文件中的测试被运行两次。建议在 Run all topic tests 中添加 --ignore=tests/test_const_merge.py 或将其合并到同一步骤中。

🟡 PR 运行器缺少 Python 缓存 — 为 PR 使用 ubuntu-latest 时,setup-python 未配置 cache,且未安装依赖缓存,每次 PR 都会从头安装依赖,增加 1-2 分钟流水线时间。建议为 setup-python 添加 cache: 'pip' 并指定 cache-dependency-path 指向 pyproject.tomlsetup.py

🟡 始终上传 benchmark 报告Upload benchmark reports 新增 if: always(),但原版仅对 main 分支(由于 upload-artifactif: github.ref == 'refs/heads/main' 被移除)才上传。现在所有分支(包括 PR)都会上传报告,可能浪费存储空间并暴露敏感信息。建议恢复条件上传,或至少添加 if: github.ref == 'refs/heads/main' || github.event_name == 'pull_request' 的明确条件。

💭 if: always() 用于 Write job summary — 该步骤新增 if: always(),但原版无条件,行为不变。但若步骤失败,$GITHUB_STEP_SUMMARY 可能写入不完整内容,无实际影响,仅为风格 nit。


📁 benchmarks/bench_const_merge.py

🔴 潜在运行时错误:依赖未定义的属性bench_merge 中使用了 stats.total_changesstats.candidate_pairsstats.merged_pairsstats.redundant_lui_removed,但 merge_constants_detailed 的返回类型未在 diff 中定义。如果返回的对象缺少这些属性,会导致 AttributeError。建议检查该函数的返回类型或添加显式断言。

🔴 潜在运行时错误:依赖未定义的属性bench_merge 中通过 parse_asm 解析结果,并访问 line.opcodeline.is_directive。如果 parse_asm 返回的对象不具备这些属性,会崩溃。建议确认 _asm_parser 模块的接口,或添加防御性检查。

🟡 建议:避免修改全局 sys.path — 通过 sys.path.insert(0, PROJ_DIR) 修改导入路径可能有副作用(如影响其他模块的导入顺序)。建议在运行基准测试时通过 PYTHONPATH 环境变量或使用 -m 来运行,以保持代码的纯净。

🟡 建议:验证 num_instructions 为正数_gen_synthetic_asm 目前允许 num_instructions=0,此时只会生成 .textsynthetic_func: 标签,无实际指令。这可能不是预期行为,考虑要求 num_instructions > 0 或至少添加注释说明。

💭 nit:冗余 LUI 生成中硬编码的寄存器redundant_lui_density 分支中使用了硬编码的 add a4, a5, a6,与选择的 reg 无关。虽然功能上正确(生成无关指令),但可能令人困惑。建议添加注释说明这是有意制造的无关指令,或使用 reg 变量使模式更一致。


📁 benchmarks/cases/const_merge_feature.asm

🟡 缺少期望结果注释 — 测试用例描述了规则,但没有说明优化后的预期代码。建议添加注释,例如“After Rule A: replace lui+addi with li; After Rule B: remove redundant lui and keep addi.” 这样便于验证优化器是否正确实现规则。


📁 benchmarks/run_benchmark.py

🔴 Bug: 优化后代码未使用asm_after 只用于统计,但 asm_str 未更新,asm_line_count 仍基于原始代码,验证步骤也跳过优化后代码。建议:若目的是测量优化效果,应同时更新 asm_str 并验证优化后结果。

🟡 误导性输出 — 在 run_all_benchmarks 中,非 RISC-V 后端也会打印 {r.merged_pairs + r.redundant_lui_removed} const changes,显示为 0。建议仅对 RISC-V 后端添加该信息,或统一输出。

🟡 条件导入位置from scratchv.backend.const_merge import merge_constants_detailed 放在循环内,虽然 Python 会缓存,但不符合惯例。建议移到文件顶部。

💭 未使用的字段machine_instructions_before/aftercode_size_before/afteroutput_equal 被添加但从未赋值,可能造成后续维护困惑。建议移除或添加 TODO 注释。


📁 benchmarks/run_const_merge_case.py

🔴 Bug: 使用私有方法 _run_asm_passes — 第67行直接调用 driver._run_asm_passes。私有方法不应在外部调用,建议改为公开接口或通过 CompilerDriver 的公共 API 执行变换。

🔴 Bug: assert 在优化模式下失效 — 第72行 assert pipeline_matches_public_passpython -O 下被跳过,导致潜在状态不一致不被检测。应改为 if not pipeline_matches_public_pass: raise AssertionError(...)

🟡 风险: warnings 列表可能未被填充 — 第68行传递空列表 warnings_run_asm_passes,但若该方法不修改传入列表,warnings 始终为空,feature_used 判断(第92行)依赖 any("Const merge:" ...) 将永远为 False。建议确认函数行为或改为从返回值获取警告。

🟡 风险: 模拟失败时无诊断信息 — 第43行 machine.last_error 仅抛出 RuntimeError,但未包含 last_error 的详细内容。建议改为 raise RuntimeError(f"TinyFive error: {machine.last_error}") 以便调试。

🟡 可维护性: 硬编码起始地址 origin=0start=0 — 第40-41行 load_binary(words, origin=0)run(..., start=0) 假设代码从0地址开始,但真实 RISC-V 平台通常起始地址不同。建议添加注释说明此测试特殊性,或使用目标平台的默认地址。

🟡 健壮性: _simulateget_perf() 未在报告中输出 — 第48行调用 machine.get_perf() 但结果未使用,可能浪费资源。建议移除或明确说明意图。

💭 风格: 表格列对齐不一致 — 第140行 | Constant-merge metric | Value | 缺少对齐标记,而其他表格使用了 |---:|。建议统一格式。

💭 错误处理: main 捕获所有异常但未打印 traceback — 第219行 except Exception as exc 仅记录异常类型和消息,无法确定堆栈位置。若为 CI 脚本,建议在 _failure_report 中包含 traceback.format_exc()


📁 benchmarks/test_benchmark.py

🔴 测试依赖私有 API_gen_synthetic_asm_count_asm_instructions 都是私有函数(以下划线开头),测试直接导入它们。如果这些函数被重构或删除,测试会无声失败。建议改为测试公共接口,或将这些函数提升为稳定 API 并添加文档。

🟡 密度验证测试可能不覆盖实际逻辑test_synthetic_density_validation(0.6, 0.5) 单个值均在 [0,1] 内,但测试预期 ValueError。如果 _gen_synthetic_asm 仅检查单个值范围,此测试将永远不通过(或永远通过)。请确认验证逻辑是否包含和约束或其他条件,否则应调整参数或移除该用例。

🟡 减法断言过于严格test_synthetic_benchmark_covers_both_rules 断言 instruction_reduction == merged_pairs + redundant_lui_removed。如果未来添加其他优化(如死代码消除),该等式会破裂。建议改为 >= 或基于已知合成数据断言具体值。

🟡 _zero_hit_result 未设置 candidate_pairstest_json_keeps_zero_and_na_fields 中断言 data["candidate_pairs"] == 0,但 _zero_hit_result 没有显式赋值该字段,依赖 BenchResult 默认值。如果 BenchResult 无默认值,会引发 KeyError。建议显式设置 candidate_pairs=0

💭 输出断言易碎test_summary_keeps_zero_hit_case 硬编码 "3→3""N/A without toolchain"。如果 print_summary 的格式稍有变化(如空格、符号),测试就会失败。考虑使用 in 或正则匹配。

💭 导入顺序不符合 PEP8json 应位于 os 之后、sys 之前(按字母顺序:json < os < sys),但当前为 os, json, sys


📁 docs/课题14-常量加载合并优化-开发文档初稿.md

🔴 文档类型不一致 — 标题为“开发文档初稿”,但内容大量使用完成时态(“已修复”、“已完成”)且验收标准多数已打勾,造成读者困惑。建议明确为“实现报告”或统一时态为完成状态。

🔴 关联 Issue 占位符未填写 — 第 1.1 节“关联 Issue”为“#待补充”,应补充实际编号。

🔴 步骤 0 产出缺失 — 第 5 节步骤 0 要求“评审结论写入设计文档‘工作假设’章节”,但文档中未见该章节,需补充或删除该引用。

🟡 第 0 节与第 1 节内容重叠 — 第 0.1 节与第 1.1 节均提到 instruction_select.py 产生 LI 导致零命中,建议合并或精简。

🟡 第 3.1 节环境初始化冗余git clone、venv 等基础操作对于已完成的开发文档不必要,建议删除或移入附录。

🟡 第 11 节历史记录不当 — “第一天实际工作清单”包含现场命令和阅读顺序,属于一次性笔记,不应留在最终文档中,建议归档或删除。

🟡 第 12 节 PR 描述建议多余 — 该节是对 PR 提交者的建议,而非开发文档主题,可考虑移入贡献指南或删除。

🟡 验收标准未全部完成 — 第 8 节中第一项“设计文档已评审”和最后一项“PR 描述包含”仍为 [ ],但文档其他部分已标记完成,需统一状态或明确待办。

🟡 第 5 节步骤 9 的 benchmark 细节过于琐碎 — 步骤 9 详细描述了真实 case A/B 的输出字段和注意事项,这些更适合放在测试计划或设计文档附录,而非开发计划正文。

🟡 merge_constants_detailed 返回类型未说明 — 第 2.1 节仅写“返回 stats”,未明确 ConstantMergeStats 包含哪些字段,建议补充。


📁 docs/课题14-常量加载合并优化-技术设计文档初稿.md

🔴 不一致:文档标题为“设计初稿”,但0.1节标明“已实现结论”
设计文档应反映设计阶段,而非实现后回填。若已实现,应改为“实现总结”或“设计回顾”,否则误导读者对新设计方案的预期。

🟡 候选定义歧义(10.2)
candidate_pairs 定义中包含“寄存器不一致、名称非法”时仍计入,这违反了“候选”语义。建议更改为:仅计入满足操作码、相邻、操作数数量和基本块条件的;其余在统计中单独列为“格式不匹配”或“安全拒绝”。

🟡 lui 立即数语义笼统(4.2/6.3)
文档说 lui 接受“有符号20位写法或0..0xFFFFF”,但RISC-V规范中 lui 的立即数在编码上是20位无符号,汇编器允许写负数(如-1)作为补码。建议明确:解析时以 int(text,0) 处理,并将结果截断到20位无符号(imm_hi & 0xFFFFF),避免歧义。

🟡 规则B中“未知指令清空全部状态”过于保守(6.4)
虽然安全,但可能抑制合法优化。建议补充:若已知指令不影响寄存器(如 nopfence),应保持状态。可复用 _asm_parser 中的 classify_def_use 进行判断,而非仅靠未知清空。

🟡 逐字节保留测试要求可能不现实(9.1)
要求“未修改的伪操作、标签、空行、注释缩进逐字节保留”在基于解析器重建时很难保证。建议改为“语义等价,格式尽量保留,并记录差异”,除非明确指定保序算法。

🟡 修复点迭代最大迭代次数设定(6.5)
max(1, len(lines)) 在最坏情况下(如每次只删除一条)会导致 O(n²),但文档已接受。然而对于超长汇编(如10万行),可能触发性能问题。建议增加可配置上限或采用工作队列线性化。

💭 sign_extend_12 注释(4.1)
注释说“把12位编码(0..0xFFF)解释为有符号值”,但输入可能已是负数(如-1)。建议注明“对输入数值取低12位后进行符号扩展”。

💭 寄存器别名表格(6.2)
仅列出 t0 -> x5 等,未涵盖所有ABI名(如 t1 -> x6a0-a7 等)。建议补充完整或引用规范表。

💭 待评审问题(十二)
部分问题(如1、2、5)已在文档中明确假设,却仍列为待评审,可能造成混淆。建议将与假设不符的问题单独列出,或直接标注“已假定”。


📁 scratchv/backend/_asm_parser.py

🟡 冗余 strip/lower_looks_like_regname = s.strip().lower() 后传入 is_integer_reg(name),而后者内部又对参数做了一次 strip().lower()。建议移除 _looks_like_reg 中的这两个操作,或将 is_integer_reg 改为不重复处理,保持调用方干净。

🟡 缺少单元测试 — 新增的 canonical_regis_integer_reg 函数没有对应的测试覆盖,建议在关联测试文件中补充,涵盖别名、数字格式、浮点寄存器等边界情况。

💭 浮点寄存器大小写 — 浮点寄存器检查仍区分大小写(fullmatchre.IGNORECASE),但原有行为未变,且 RISC‑V 汇编通常大小写不敏感,可考虑后续统一改为不区分大小写。

💭 数字标签边缘情形_LINE_RE[0-9]+: 可能将 0x1: 误解析为标签 0:,但在常见 RISC‑V 局部标签(纯数字)中概率极低,可暂不处理。


📁 scratchv/backend/const_merge.py

🔴 Bug: _merge_lui_addi_once 中保留注释行时顺序错误 — 第 151 行 result.extend(insts[i + 1:j]) 将位于 luiaddi 之间的注释/空行保留在结果中,但此时 result 已经包含了合成的 li 指令,而注释被放在 li 之后,这改变了原始代码中注释与指令的相邻关系。原始代码中注释属于 luiaddi 的尾部注释,现在它们被插入在 li 之后,导致输出顺序与原始意图不符。建议:在合成 li 之前先将注释/空行保留在 result 中,或者将注释合并到 li 的 comment 中后再丢弃原始行。当前代码已经将注释合并到 li 的 comment 中,但同时又保留了原始注释行,可能会导致重复注释。建议只保留注释行或只使用合并后的 comment,避免重复。

🟡 可维护性: _merge_lui_addi_oncej 的索引处理逻辑不清晰 — 循环结束后 i = j + 1 跳过了 addi 后面的所有行,但 j 在跳过注释时指向的是 addi 指令,然后 result.extend(insts[i + 1:j]) 包含了 lui 之后、addi 之前的所有行。从 i = j + 1 开始,j 可能大于 i,但 j 本身没有被排除。如果 addi 后面还有注释,这些注释不会被保留(因为循环从 j+1 继续,而 jaddi 的索引,addi 未添加到 result 且注释行在 addi 之后未被处理)。需要检查:当 addi 后面紧跟着注释时,这些注释会丢失,因为它们被跳过了。建议:改用 i = j + 1 后,应该将 addi 及其后面的注释行也考虑保留,但当前逻辑只保留了 i+1j(不包括 j)之间的行,而 addi 本身被丢弃。注释行在 addi 之后将被忽略。这可能导致丢失 addi 之后的注释。建议要么在 result.extend(insts[i + 1:j]) 之后不跳过后面的注释,而是将 i 设为 j 并继续处理(但需要跳过 addi 本身)。修改为 i = j 然后 continue 会让 addi 被跳过,但后续注释会被处理。当前代码 i = j + 1 跳过了 addi 及其后面的所有行,如果 addi 后面还有注释,这些注释会被忽略。这是一个潜在的数据丢失 bug。

🟡 性能: _remove_redundant_lui_once 中对于未知指令清空 lui_state 过于保守 — 第 232 行 lui_state.clear() 清空了所有已知的 LUI 值,但可能许多未知指令并不影响寄存器。这可能导致漏掉合并机会,但安全。建议在性能敏感的代码中,可以仅在指令确实可能写寄存器时清空,但当前保守做法可接受。

💭 命名: _is_separator 函数名不够准确 — 该函数判断的是空白行或纯注释行,但“separator”可能让人误解为分隔符。建议改为 _is_whitespace_or_comment 或直接内联。

💭 可维护性: _merge_lui_addi_oncecomments 列表构建可以使用更简洁的语法 — 第 143 行 comments = [comment for comment in (lui.comment, addi.comment) if comment] 可以简化为 comments = [c for c in (lui.comment, addi.comment) if c]

🔴 安全/正确性: _parse_lui_imm 对超出范围的立即数返回 None,但 _merge_lui_addi_once 中未处理 imm_hi is None 的情况 — 第 131 行正确返回了 None,但在第 137 行检查了 if imm_hi is None or imm_lo is None,然后继续。这会导致 LUI 立即数不合法时跳过该候选,但不会报错。这是正确的,但应该记录日志或考虑是否要警告用户输入了非法立即数。当前设计是静默忽略,可以接受但建议在注释中说明。

🟡 测试: 缺乏对新的统计信息和迭代行为的测试 — 引入了 ConstantMergeStatsmerge_constants_detailed,但未在变更中看到对应的测试更新。需要确保测试覆盖了 candidate_pairsredundant_lui_removed 和多次迭代场景。

💭 代码风格: _merge_lui_addi_onceParsedAsmLine 构造时 raw="" — 合成指令的 raw 设为空字符串,然后在 _insts_to_asm 中会调用 to_asm() 重建。这是合理的,但可以添加注释说明意图。

🟡 可维护性: _remove_redundant_lui_onceopcode 变量可能为 None — 第 197 行 opcode = inst.opcode,但 inst.opcode 可能是 None(例如空行或注释)。后面第 209 行 if opcode == "lui" 没问题,因为 None == "lui" 为 False。但第 223 行 if opcode in _CONTROL_FLOW_OPCODESopcodeNone 时会触发 None in set 正确返回 False,但代码可读性稍差。建议在之前就处理 if inst.opcode is None 的情况,当前逻辑是在第 187 行处理了 if inst.opcode is None(包括空行和未知指令),但 is_separator 判断的是空行和注释,对于非空行但无法解析的指令(如宏),走的是 if not _is_separator(inst): lui_state.clear(),然后 result.append(inst)continue,所以不会到达后面的 opcode 分支。因此实际上 opcode 不会是 None 当到达后面时。但逻辑有点绕,建议简化。

总体而言,重构提升了可维护性和功能,但需要修复注释保留和索引跳过的 bug。



⚠️ 未审查的文件

  • scratchv/backend/riscv_encoder.py
  • scratchv/compiler.py
  • scratchv/simulator/tinyfive.py
  • scratchv/standalone/bench_report.py
  • scratchv/standalone/onnx_to_riscv_standalone.py
  • scratchv/standalone/tinyfive_compare.py
  • tests/test_backend.py
  • tests/test_const_merge.py
  • tests/test_const_merge_report.py
  • tests/test_simulator.py
  • tests/test_standalone_const_merge.py

@yuki-328
yuki-328 force-pushed the feature/const_merge branch from a04a5a8 to 179f885 Compare August 15, 2026 09:32
@yuki-328 yuki-328 changed the title topic14-docs feat: complete safe RV32 constant-load merge optimization Aug 15, 2026
Comment thread scratchv/simulator/tinyfive.py
watney1024 added a commit to watney1024/ScratchV that referenced this pull request Aug 25, 2026
The AI code review workflow failed (exit 28) on large PRs because
deepseek-v4-flash processing a big file diff (e.g. ci.yml ~14KB payload)
hangs past the curl timeout, and --retry-all-errors then retried the
subsequent HTTP 429 quota errors into a 180s+ hang + curl exit 28, which
the implicit set -e (GitHub Actions default bash) turned into a job
failure.

Local repro on real PR ScratchV-Compiler#35 data (same key and workflow curl args, model
swapped):
- deepseek-v4-flash: first file times out (exit 28), job aborts
- glm-5.2: 429 Workspace allocated quota exceeded (key has no quota)
- sensenova-6.8-flash-lite: all files return 200, stable

Changes:
- Switch model to sensenova-6.8-flash-lite (verified working).
- Drop --retry-all-errors so HTTP 429 (quota) is not retried into a hang.
- Wrap curl in || { [WARN] ...; continue; } so a per-file curl failure is
  treated as 'no result' and skipped while the job still succeeds, instead
  of aborting via set -e.
- Set --max-time 90 --retry 0: gives large diffs enough time while capping
  worst case at 90s/file (10 files fit inside timeout-minutes: 15).
jizhenjun pushed a commit that referenced this pull request Aug 25, 2026
…tal (#52)

The AI code review workflow failed (exit 28) on large PRs because
deepseek-v4-flash processing a big file diff (e.g. ci.yml ~14KB payload)
hangs past the curl timeout, and --retry-all-errors then retried the
subsequent HTTP 429 quota errors into a 180s+ hang + curl exit 28, which
the implicit set -e (GitHub Actions default bash) turned into a job
failure.

Local repro on real PR #35 data (same key and workflow curl args, model
swapped):
- deepseek-v4-flash: first file times out (exit 28), job aborts
- glm-5.2: 429 Workspace allocated quota exceeded (key has no quota)
- sensenova-6.8-flash-lite: all files return 200, stable

Changes:
- Switch model to sensenova-6.8-flash-lite (verified working).
- Drop --retry-all-errors so HTTP 429 (quota) is not retried into a hang.
- Wrap curl in || { [WARN] ...; continue; } so a per-file curl failure is
  treated as 'no result' and skipped while the job still succeeds, instead
  of aborting via set -e.
- Set --max-time 90 --retry 0: gives large diffs enough time while capping
  worst case at 90s/file (10 files fit inside timeout-minutes: 15).
@jizhenjun
jizhenjun merged commit 29e171a into ScratchV-Compiler:main Aug 26, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants