【问题标题】:Exporting functions from LLVM C++ API to WebAssembly将函数从 LLVM C++ API 导出到 WebAssembly
【发布时间】:2021-04-15 04:29:21
【问题描述】:

情况:我目前正在解析一种前端语言并在 LLVM IR 中生成函数定义。

我可以使用 LLVM12 C++ API 将函数定义编译为 WebAssembly 文件。

但是,生成的 wasm 代码不会“导出”任何已编译的函数,因此无法从加载 wasm 文件的 javascript 访问。

问题:有人可以告诉我我可能遗漏了什么吗?如何告诉 llvm 编译器为定义的函数创建导出。我尝试将函数可见性设置为 llvm::GlobalValue::DefaultVisibility。但这似乎没有帮助。

为函数生成的 IR(具有默认可见性)看起来像

define double @f(double %x) #0 {
entry:
  %multmp = fmul double %x, 2.000000e+00
  ret double %multmp
}

attributes #0 = { "target-features" }

将包含函数定义的模块编译到 Wasm 目标的函数如下所示:

llvm::Module *TheModule; // module containing the function definition

// function to compile to Wasm target
bool compile_file(){
  const char *TargetTriple = "wasm-wasi";
  
  // create a llvm::Target for the specified triple
  std::string Error;
  const llvm::Target *Target =   llvm::TargetRegistry::lookupTarget(TargetTriple, Error);
  if(!Target) {
    llvm::errs() << Error;
    return false;
  }
  
  // set the options and features for the target and create a TargetMachine instance
  auto CPU = "generic";
  auto Features = "";

  llvm::TargetOptions opt;
  auto RM = llvm::Optional<llvm::Reloc::Model>();
  auto TheTargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM);
  TheModule->setDataLayout(TheTargetMachine->createDataLayout());

  // create a output stream to write the compiled code to a .wasm file in the current directory
  std::error_code EC;
  llvm::raw_fd_ostream dest("output.wasm", EC, llvm::sys::fs::OF_None);

  if(EC) {
    llvm::errs() << "Could not open file: " << EC.message();
    return false;
  }


  // set the visibility of all functions in the module to DefaultVisibility
  auto &functionList = TheModule->getFunctionList();
  for (auto &function : functionList) {
    function.setVisibility(llvm::GlobalValue::DefaultVisibility);
  }
  
  // add a emit pass to write the generated code to the wasm file 
  llvm::legacy::PassManager pass;
  if(TheTargetMachine->addPassesToEmitFile(pass,dest,nullptr,llvm::CGFT_ObjectFile)){
    llvm::errs() << "TheTargetMachine can't emit a file of this type";
    return false;
  }
  // run the pass on the module and flush the output stream to the file
  pass.run(*(TheModule));
  dest.flush();
  // return true on success
  return true;

这会输出一个类似的 wasm 文件

(module
  (type $t0 (func (param f64) (result f64)))
  (import "env" "__linear_memory" (memory $env.__linear_memory 0))
  (import "env" "__indirect_function_table" (table $env.__indirect_function_table 0 funcref))
  (func $f0 (type $t0) (param $p0 f64) (result f64)
    local.get $p0
    local.get $p0
    f64.add))

但是,这个生成的文件有问题。 它没有添加“导出”语句以使函数 f0 对外界可见,这将允许加载 wasm 模块的 javascript 调用函数 f0。 理想情况下,生成的文件应该有类似的函数定义行

func $f0 (export "f") (type $t0) (param $p0 f64) (result f64)
    local.get $p0
    local.get $p0
    f64.add))

这样,加载的 javascript 将可以访问名为“f”的函数,它可以从 wasm 调用。

有没有办法向 LLVM C++ API 指定函数应该被导出?

【问题讨论】:

  • 要按照以下答案中的建议设置函数属性,可以添加如下属性: function.addFnAttr( llvm::Attribute::get(TheContext,"wasm-export-name", function.getName()) ) // TheContext 是定义模块的 llvm::Context

标签: c++ clang llvm webassembly llvm-c++-api


【解决方案1】:

您可以通过设置wasm-export-namewasm-export-name 属性来触发给定符号的导出。

在 C/C++ 中,这些对应于 export_nameexport_module clang 属性。

有关此示例,请参阅 llvm 树中的 llvm/test/CodeGen/WebAssembly/export-name.ll

您还可以要求链接器使用--export 命令行标志导出给定符号。见https://lld.llvm.org/WebAssembly.html#exports

【讨论】:

  • 谢谢@sbc100。那工作得很好。我使用 llvm api 为函数设置了“wasm-export-name”属性,并更新了上面评论中的代码,以防其他人想知道这是如何完成的。
  • 再次感谢@sbc100。作为这项工作的继续,我实际上遇到了另一个问题,在运行依赖“全局 __stack_pointer”导入来处理类似对象的结构的 wasm 文件时,我似乎遇到了类似分段错误的情况。我怀疑,我在提供要由 __stack_pointer 从加载模块的 js 文件导入的正确类型的对象时犯了一些错误。我遇到了您的提交 reviews.llvm.org/D34172 ,并要求在那里进行一些澄清。如果这不适合讨论,我会在这里提出一个新问题
  • 没关系。我在这里找到了 __stack_pointer 问题的答案:github.com/WebAssembly/tool-conventions/blob/master/…。只需要了解指针所指的内容。为堆栈提供足够高的值作为线性内存对象中的偏移量,即可解决问题。
  • 除非您使用的是实验 PIC ABI,否则堆栈指针应该在 wasm 模块本身中定义而不是导入。 (假设您实际运行 wasm-ld 来产生最终输出?)
  • 在这种情况下,您拥有的是一个 wasm 对象文件。就像 ELF 目标文件一样,它不是为直接运行而设计的,而是传递给链接器(wasm-ld)。链接器将执行该内存布局并设置堆栈指针。
猜你喜欢
  • 1970-01-01
  • 2019-10-25
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多