将函数从 LLVM C++ API 导出到 WebAssembly
Exporting functions from LLVM C++ API to WebAssembly
情况:我目前在LLVM IR中解析一个前端语言并生成函数定义
我可以使用 LLVM12 C++ 将函数定义编译为 WebAssembly 文件API。
但是,生成的 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 对外界可见,这将允许 javascript 加载 wasm 模块以调用函数 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 指定应该导出该函数?
您可以通过设置 wasm-export-name
和 wasm-export-name
属性来触发给定符号的导出。
在 C/C++ 中,这些对应于 export_name
和 export_module
clang 属性。
有关此示例,请参阅 llvm 树中的 llvm/test/CodeGen/WebAssembly/export-name.ll
。
您还可以要求链接器使用 --export
命令行标志导出给定符号。参见 https://lld.llvm.org/WebAssembly.html#exports。
情况:我目前在LLVM IR中解析一个前端语言并生成函数定义
我可以使用 LLVM12 C++ 将函数定义编译为 WebAssembly 文件API。
但是,生成的 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 对外界可见,这将允许 javascript 加载 wasm 模块以调用函数 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 指定应该导出该函数?
您可以通过设置 wasm-export-name
和 wasm-export-name
属性来触发给定符号的导出。
在 C/C++ 中,这些对应于 export_name
和 export_module
clang 属性。
有关此示例,请参阅 llvm 树中的 llvm/test/CodeGen/WebAssembly/export-name.ll
。
您还可以要求链接器使用 --export
命令行标志导出给定符号。参见 https://lld.llvm.org/WebAssembly.html#exports。