You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在LLVM Pass中正确修改IR?自定义函数转协程遇指令丢失

解决Clang 18中LLVM Pass插入@llvm.coro.id被省略的问题

核心原因分析

Clang 18的IR处理流水线中,后续的协程Lowering或IR规范化Pass会严格检查协程指令的合法性与完整性。如果插入的@llvm.coro.id不符合规范、缺少配套指令,或Pass运行时机错误,都会被判定为无效指令并移除。

具体解决步骤

1. 调整Pass的运行时机

Clang的Pass流水线有固定执行顺序,必须确保自定义Pass在协程Lowering Pass之前、IR构建完成之后运行:

  • 执行clang -Xclang -print-pipeline-passes查看当前Pass顺序,定位coroutine-lowering阶段的位置
  • 基于New Pass Manager(Clang 18默认使用)开发时,在Pass插件的registerPassBuilderCallbacks中,将你的Pass插入到EP_CoroutineLowering之前的阶段,示例代码:
    extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
      return {
        LLVM_PLUGIN_API_VERSION, "CoroTransformPass", "v0.1",
        [](PassBuilder &PB) {
          // 注册在流水线早期,确保在协程处理前执行
          PB.registerPipelineStartEPCallback([&](ModulePassManager &MPM, OptimizationLevel Level) {
            MPM.addPass(CoroTransformPass());
          });
        }
      };
    }
    
  • 编译时通过-Xclang -passes=-my-coro-pass,coroutine-lowering显式指定Pass执行顺序,确保自定义Pass优先运行

2. 严格按规范构造@llvm.coro.id指令

@llvm.coro.id的参数类型与顺序必须完全符合LLVM IR规范,错误参数会被后续Pass判定为无效:

  • 参数依次为:i32(栈对齐值)、i8*(函数地址的bitcast结果)、i8*(帧分配函数,可传null)、i8*(销毁函数,可传null)
  • 示例C++ API构造代码:
    auto &Ctx = Fn.getContext();
    auto Int8PtrTy = Type::getInt8PtrTy(Ctx);
    auto FnPtr = ConstantExpr::getBitCast(Fn, Int8PtrTy);
    auto CoroId = CallInst::Create(
        Intrinsic::getDeclaration(Fn.getParent(), Intrinsic::coro_id),
        {ConstantInt::get(Type::getInt32Ty(Ctx), 8), FnPtr, ConstantPointerNull::get(Int8PtrTy), ConstantPointerNull::get(Int8PtrTy)},
        "id", &Fn.getEntryBlock().front()
    );
    

3. 构建完整的协程IR结构

仅插入@llvm.coro.id不足以被Clang的协程逻辑认可,必须配套完整的协程指令链:

  • 生成@llvm.coro.alloca.token分配栈空间
  • 调用@llvm.coro.begin获取协程句柄
  • 保留原函数逻辑
  • 调用@llvm.coro.end完成协程
  • 完整示例IR:
    define void @f() {
      %id = call token @llvm.coro.id(i32 8, i8* bitcast (void ()* @f to i8*), i8* null, i8* null)
      %alloc = call i8* @llvm.coro.alloca.token(token %id)
      %frame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
      ; 原函数空逻辑
      call void @llvm.coro.end(i8* %frame, i1 false)
      ret void
    }
    declare token @llvm.coro.id(i32, i8*, i8*, i8*)
    declare i8* @llvm.coro.alloca.token(token)
    declare i8* @llvm.coro.begin(token, i8*)
    declare void @llvm.coro.end(i8*, i1)
    

4. 禁用Clang原生协程干扰

若Clang自带的协程处理逻辑与自定义Pass冲突,可添加编译选项-fno-coroutines,禁用原生协程支持,确保自定义Pass完全接管转换流程。

验证方法

  • 执行clang -Xclang -load -Xclang your-pass.so -S -emit-llvm test.c -o test.ll生成IR,检查是否保留了@llvm.coro.id及配套指令
  • 验证后续编译环节能否正常完成协程Lowering

内容的提问来源于stack exchange,提问作者Albert Skalt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:13:21