Situation: I currently parse a front-end language and generate function definitions in LLVM IR.
I can compile the function definition to a WebAssembly file using the LLVM12 C++ API.
However, the generated wasm code does not "export" any of the compiled functions and thus cannot be accessed from a javascript that loads the wasm file.
Question: Could someone let me know what I might be missing? How does one tell the llvm compiler to create exports for the defined functions. I tried setting the function visibility to llvm::GlobalValue::DefaultVisibility. But that doesn't seem to help.
The generated IR for the function (with default visibility) looks like
define double @f(double %x) #0 {
entry:
%multmp = fmul double %x, 2.000000e+00
ret double %multmp
}
attributes #0 = { "target-features" }
The function to compile the module containing the function definition to the Wasm target looks like this:
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;
This outputs a wasm file that looks like
(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))
However, this generated file has a problem.
It does not add an "export" statement to make the function f0 visible to the outside world, which would allow a javascript loading the wasm module to call the function f0.
Ideally, the generated file should have the function definition line looking like
func $f0 (export "f") (type $t0) (param $p0 f64) (result f64)
local.get $p0
local.get $p0
f64.add))
This way the loading javascript will have access to a function named "f" that it can call from the wasm.
Is there a way to specify to the LLVM C++ API that the function should be exported?
question from:
https://stackoverflow.com/questions/65646383/exporting-functions-from-llvm-c-api-to-webassembly