LLVM 15 API生成ptr类型无效IR问题求助(OCaml实现)
问题:LLVM 15不透明指针下ptr*类型无效的编译错误
我正在使用LLVM 15与OCaml开发一款类Pascal语法的自定义语言编译器。当创建包含ptr元素的结构体,通过getelementptr获取其字段引用并执行存储操作时,生成了含ptr类型的无效IR。手动将ptr改为ptr后可正常编译,现咨询:
- 为何ptr*不被允许(C中指针的指针是合法的);
- 如何在OCaml代码中修复该问题(也可提供C++ API方案)。不透明指针已带来诸多困扰。
附相关代码及错误信息
OCaml代码
let frame = named_struct_type llvm.context "frame1" in struct_set_body frame ([| pointer_type2 llvm.ctx |]) false; let frame_ptr = build_alloca frame ("frame_ptr") llvm.builder in let base = build_struct_gep2 frame frame_ptr 0 "frame_elem_ptr" llvm.builder in ignore (build_store v base llvm.builder);
其中v为类型ptr的函数参数auxiliary。
生成的IR
%frame = type { ptr } %frame_ptr = alloca %frame, align 8 %frame_elem_ptr = getelementptr inbounds %frame, %frame* %frame_ptr, i32 0, i32 0 store ptr %auxiliary, ptr* %frame_elem_ptr, align 8
编译错误
llc-15: error: llc-15: llvm_ir.ll:54:28: error: ptr* is invalid - use ptr instead
解答
1. 为何ptr*不被允许?
LLVM的不透明指针(Opaque Pointers)模型和C语言的指针逻辑存在本质差异:
- C语言中,
int**这类多级指针是语法层面的层级表示,用来明确“指向指针的指针”; - 但LLVM的
ptr是无类型的不透明指针,它统一指代所有指针类型,不管是一级、二级还是更高级,都不需要通过加星号来区分。在不透明指针模型里,根本不存在ptr*这种类型,所有指针都只用ptr表示,因此编译器会报错。
你手动修改后能正常编译,就是因为符合了不透明指针模型的规则。
2. 代码修复方案
OCaml API修复
问题出在build_struct_gep2是适配旧类型指针模型的API,生成了ptr*这种不符合不透明指针规范的类型。修改方式如下:
let frame = named_struct_type llvm.context "frame1" in -- 直接使用不透明ptr类型定义结构体元素 struct_set_body frame ([| llvm.ptr_type llvm.context |]) false; let frame_ptr = build_alloca frame ("frame_ptr") llvm.builder in -- 使用适配不透明指针的build_struct_gep替代build_struct_gep2 let base = build_struct_gep frame frame_ptr 0 "frame_elem_ptr" llvm.builder in -- 如果仍存在类型不匹配,通过bitcast强制转为不透明ptr let base_ptr = build_bitcast base (llvm.ptr_type llvm.context) "base_ptr" llvm.builder in ignore (build_store v base_ptr llvm.builder);
关键改动:
- 用
llvm.ptr_type创建标准不透明指针类型,替换可能基于旧模型的pointer_type2; - 优先使用不带数字后缀的
build_struct_gep(LLVM 15 OCaml绑定中,这类API默认适配不透明指针); - 若仍有类型问题,通过
build_bitcast将返回值转为纯ptr类型。
C++ API修复
在C++中,直接使用不透明指针的原生API即可避免错误:
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/StructType.h" #include "llvm/IR/Type.h" using namespace llvm; // 示例代码片段 LLVMContext &Ctx = ...; // 获取上下文 IRBuilder<> Builder(Ctx); // 创建包含不透明ptr元素的结构体 StructType *FrameTy = StructType::create(Ctx, {PointerType::getUnqual(Ctx)}, "frame1"); // 分配结构体空间,返回ptr类型 Value *FramePtr = Builder.CreateAlloca(FrameTy, nullptr, "frame_ptr"); // 获取结构体第0个字段的指针,返回ptr类型 Value *FrameElemPtr = Builder.CreateStructGEP(FrameTy, FramePtr, 0, "frame_elem_ptr"); // 执行存储操作,两个参数均为ptr类型 Builder.CreateStore(Auxiliary, FrameElemPtr); // Auxiliary是ptr类型的参数
核心逻辑:
- 用
PointerType::getUnqual(Ctx)创建不透明指针类型; CreateStructGEP在不透明指针模型下直接返回ptr类型,无需额外转换;- 存储操作的源和目标均为
ptr类型,完全符合LLVM 15的IR规范。
内容的提问来源于stack exchange,提问作者lil_papa
相关产品推荐
相关产品推荐

