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?
You can trigger the export of a given symbol by setting the wasm-export-name and wasm-export-name attributes.
In C/C++ these correspond the export_name and export_module clang attribtes.
See llvm/test/CodeGen/WebAssembly/export-name.ll in the llvm tree for an example of this.
You can also ask the linker to export a given symbol with the --export command line flag. See https://lld.llvm.org/WebAssembly.html#exports.
Related
We can use LLVMVerifyModule to verify the consistency of modules inside of code. But imagine we handcrafted a LLVM IR file. This file can be syntactically correct but I do want to run something to sanitize it.
Consider the following module:
; ModuleID = 'main'
source_filename = "main"
define void #gl.main() {
entry:
ret void
ret void
}
I suppose it is not right because it contains two return in the same block. I ran llvm-as but it went through. Am I understanding it correctly? What command do you run to verify a hand-written LLVM source file?
update:
opt doesn't crash but instead output this file:
; ModuleID = 'try.bc.2'
source_filename = "main"
define void #gl.main() {
entry:
ret void
0: ; No predecessors!
ret void
}
I have a simple program that loads a DLL from the current path
#include <iostream>
#include <windows.h>
using namespace std;
auto loaddll(const char * library) {
auto dllModule = LoadLibrary(library);
if(dllModule == NULL)
throw "Can't load dll";
return dllModule;
}
int main() {
try {
auto Handle = loaddll("ISab.dll");
} catch(const char * error) {
cerr << "An Unexpected error :" << error << endl;
cerr << "Get Last Error : " << GetLastError();
}
}
the load library fails for every DLL in the current path but succeeds for DLL like User.dll
if I ran it output will be like
An Unexpected error :Can't load dll
Get Last Error : 0
this also fails if i specify full path to dll
When a Win32 API call fails, and sets the error code, you must call GetLastError before calling any other Win32 API function. You don't do that.
Raising an exception, streaming to cerr etc. are all liable to call other Win32 API functions and so reset the error code.
Your code must look like this:
auto dllModule = LoadLibrary(library);
if (dllModule == NULL)
auto err = GetLastError();
Once you have the error code you should be better placed to understand why the module could not be loaded. Common error codes for LoadLibrary include:
ERROR_MOD_NOT_FOUND which means that the module, or one of its dependencies, cannot be located by the DLL search.
ERROR_BAD_EXE_FORMAT which invariably means a 32/64 bit mismatch, either with the module you load, or one of its dependencies.
I am running into an issue trying to use AliasAnalysis from within a module pass (LLVM 3.4). This seems to be a fairly common issue with two main solutions, but I haven't been able to get it to work.
With the following simple test program:
#include <stdio.h>
static int gX;
void f() {
gX = 1;
}
int main() {
printf("Hello.\n");
return 0;
}
Compiled like this: clang++ -O0 -c -emit-llvm -o test.bc test.cpp
And my simple pass:
class DebugPass : public ModulePass {
public:
static char ID;
DebugPass() : ModulePass(ID) {}
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<AliasAnalysis>();
}
virtual bool runOnModule(Module &M) {
for (Module::iterator fit = M.begin(), fite = M.end(); fit != fite; ++fit) {
Function *F = fit;
if (F->isDeclaration()) continue;
errs() << "Getting AA for " << F->getName() << "\n";
AliasAnalysis &AA = getAnalysis<AliasAnalysis>(*F);
}
return false;
}
}
char DebugPass::ID = 0;
static RegisterPass<DebugPass> X("testdebug", "Debugging pass", false, false);
And by invoking my pass like this: opt -load ../Debug+Asserts/lib/TestLib.dylib -basicaa -testdebug < test.bc > /dev/null
I am seeing the following assertion failure:
Getting AA for _Z1fv
Assertion failed: (FPP && "Unable to find on the fly pass"), function getOnTheFlyPass, file LegacyPassManager.cpp, line 1682.
...
6 opt 0x0000000102e5ba62 abort + 18
7 opt 0x0000000102e5ba41 __assert_rtn + 129
8 opt 0x0000000102d94820 (anonymous namespace)::MPPassManager::getOnTheFlyPass(llvm::Pass*, void const*, llvm::Function&) + 144
9 opt 0x0000000102d949cf non-virtual thunk to (anonymous namespace)::MPPassManager::getOnTheFlyPass(llvm::Pass*, void const*, llvm::Function&) + 63
10 opt 0x0000000102d8ed4e llvm::AnalysisResolver::findImplPass(llvm::Pass*, void const*, llvm::Function&) + 78
11 TestLib.dylib 0x0000000104ba9bac llvm::AliasAnalysis& llvm::Pass::getAnalysisID<llvm::AliasAnalysis>(void const*, llvm::Function&) + 236
12 TestLib.dylib 0x0000000104b9eef6 llvm::AliasAnalysis& llvm::Pass::getAnalysis<llvm::AliasAnalysis>(llvm::Function&) + 134
13 TestLib.dylib 0x0000000104bdc287 DebugPass::runOnModule(llvm::Module&) + 215
Can anyone offer some insight into what is going wrong?
Edit: Here is the pass structure via -debug-pass=Structure:
Pass Arguments: -targetlibinfo -datalayout -notti -basictti -x86tti -no-aa -basicaa -testdebug -preverify -domtree -verify
Target Library Information
Data Layout
No target information
Target independent code generator's TTI
X86 Target Transform Info
No Alias Analysis (always returns 'may' alias)
Basic Alias Analysis (stateless AA impl)
ModulePass Manager
Debugging pass
FunctionPass Manager
Preliminary module verification
Dominator Tree Construction
Module Verifier
Bitcode Writer
Is it significant that my debugging pass occurs before the FunctionPass manager? I am not practiced in interpreting the pass structure output.
I'm creating a GCC plugin.
I'm trying to create a plugin for a specific loop transformation - unroll loop exactly N (parameter given) times.
I have installed plugins correctly and I can successfully register my pragma in compilation process.
When I register pragma with function c_register_pragma, I can handle it in lexical analysis (with function handle_my_pragma), but how can I find it then?
I can also define my own pass and traverse GIMPLE, but there is no trace of any pragma.
So my question is: Where is my pragma and how can I influence my code with it?
Or what would you suggest to reach my goal? It doesn't have to be with pragma, but it seemed to be a good idea.
Also, I know about MELT, but within the study of GCC, I would prefer pure plugin in C.
My code
static bool looplugin_gate(void)
{
return true;
}
static unsigned looplugin_exec(void)
{
printf( "===looplugin_exec===\n" );
basic_block bb;
gimple stmt;
gimple_stmt_iterator gsi;
FOR_EACH_BB(bb)
{
for (gsi=gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi), j++)
{
stmt = gsi_stmt(gsi);
print_gimple_stmt (stdout, stmt, 0, TDF_SLIM);
}
}
return 0;
}
void handle_my_pragma(cpp_reader *ARG_UNUSED(dummy))
{
printf ("=======Handling loopragma=======\n" );
enum cpp_ttype token;
tree x;
int num = -1;
token = pragma_lex (&x);
if (TREE_CODE (x) != INTEGER_CST)
warning (0, "invalid constant in %<#pragma looppragma%> - ignored");
num = TREE_INT_CST_LOW (x);
printf( "Detected #pragma loopragma %d\n", num );
}
static void register_my_pragma (void *event_data, void *data)
{
warning (0, G_("Callback to register pragmas"));
c_register_pragma (NULL, "loopragma", handle_my_pragma);
}
static struct opt_pass myopt_pass =
{
.type = GIMPLE_PASS,
.name = "LoopPlugin",
.gate = looplugin_gate,
.execute = looplugin_exec
};
int plugin_init(struct plugin_name_args *info, /* Argument infor */
struct plugin_gcc_version *ver) /* Version of GCC */
{
const char * plugin_name = info->base_name;
struct register_pass_info pass;
pass.pass = &myopt_pass;
pass.reference_pass_name = "ssa";
pass.ref_pass_instance_number = 1;
pass.pos_op = PASS_POS_INSERT_BEFORE;
register_callback( plugin_name, PLUGIN_PRAGMAS, register_my_pragma, NULL );
register_callback( plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass );
return 0;
}
PS: If there was someone familiar with GCC plugins development and had a good heart :), please contact me (mbukovy gmail com). I'm doing this because of my final thesis (own choice) and I welcome any soulmate.
When I register pragma with function c_register_pragma, I can handle it in lexical analysis (with function handle_my_pragma), but how can I find it then?
There is an option (actually, a hack) to create fictive helper function call at the place of pragma, when parsing. Then you can detect this function by name in intermediate representation.
Aslo, several days ago there was a question in GCC ML from felix.yang (huawei) "How to deliver loop-related pragma information from TREE to RTL?" - http://comments.gmane.org/gmane.comp.gcc.devel/135243 - check the thread
Some recommendations from the list:
Look at how we implement #pragma ivdep (see replace_loop_annotate ()
and fortran/trans-stmt.c where it builds ANNOTATE_EXPR).
Patch with replace_loop_annotate() function addition and ivdep pragma implementation: "Re: Patch: Add #pragma ivdep support to the ME and C FE" by Tobias Burnus (2013-08-24).
I do not think registering a DEFERRED pragma in plugin is possible, since the handler for deferred pragma is not exposed in GCC plugin level.
So your pragma just works during preprocessing stage instead of parsing stage, then, it is quite tricky to achieve an optimization goal.
I'm wondering how I you can create and register a function from the C++-side that returns a table when called from the Lua-side.
I've tried a lot of things but nothing did really work. :/
(sorry for the long code)
This for example won't work, because Register() expects a "luaCFunction"-styled function:
LuaPlus::LuaObject Test( LuaPlus::LuaState* state ) {
int top = state->GetTop();
std::string var( state->ToString(1) );
LuaPlus::LuaObject tableObj(state);
tableObj.AssignNewTable(state);
if (var == "aaa")
tableObj.SetString("x", "ABC");
else if (var == "bbb")
tableObj.SetString("x", "DEF");
tableObj.SetString("y", "XYZ");
return tableObj;
}
int main()
{
LuaPlus::LuaState* L = LuaPlus::LuaState::Create(true);
//without true I can't access the standard libraries like "math.","string."...
//with true, GetLastError returns 2 though (ERROR_FILE_NOT_FOUND)
//no side effects noticed though
LuaPlus::LuaObject globals = L->GetGlobals();
globals.Register("Test",Test);
char pPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH,pPath);
strcat_s(pPath,MAX_PATH,"\\test.lua");
if(L->DoFile(pPath)) {
if( L->GetTop() == 1 ) // An error occured
std::cout << "An error occured: " << L->CheckString(1) << std::endl;
}
}
When I try to set it up as a luaCFunction-function it just crashes (0x3) and says:
Assertion failed: 0, file C:\......\luafunction.h, line 41
int Test( LuaPlus::LuaState* state ) {
int top = state->GetTop();
std::string var( state->ToString(1) );
LuaPlus::LuaObject tableObj(state);
tableObj.AssignNewTable(state);
if (var == "aaa")
tableObj.SetString("x", "ABC");
else if (var == "bbb")
tableObj.SetString("x", "DEF");
tableObj.SetString("y", "XYZ");
tableObj.Push();
return state->GetTop() - top;
}
For clarification: from the Lua side I wanted it to be callable like:
myVar = Test("aaa")
Print(myVar) -- output: ABC
EDIT: The Print function comes from here. And was basically the cause for this to not work. Print can only print strings not tables... The C++ code from above works fine if you just return 1.
This is the documentation that came with my LuaPlus version btw: http://luaplus.funpic.de/
I really hope you can help me.. I'm already starting to think that it is not possible. :'(
edit:
I totally forgot to say that using PushStack() lead into an error because "the member does not exist"...
After some painstaking probing from the long comment discussion, I'm posting this answer to help summary the situation and hopefully to offer some useful advice.
The main issue the OP was running into was that the wrong print function was being called in the lua test script. Contrary to the original code shown the real code the OP was testing against was calling Print(myVar) which is a custom provided lua_CFunction and not the builtin print function.
Somehow along the way, this ended up creating some instantiation of template <typename RT> class LuaFunction and calling the overloaded operator()(). From inspecting the luafunction.h from luaPlus any lua errors that occurs inside this call will get swallowed up without any kind of logging (not a good design decision on luaPlus's part):
if (lua_pcall(L, 0, 1, 0)) {
const char* errorString = lua_tostring(L, -1); (void)errorString;
luaplus_assert(0);
}
To help catch future errors like this, I suggest adding a new luaplus_assertlog macro. Specifically, this macro will include the errorString so that the context isn't completely lost and hopefully help with debugging. This change hopefully won't break existing uses of luaplua_assert from other parts of the API. In the long run though, it's probably better to modify luaplus_assert so it actually includes something meaningful.
Anyway here's a diff of the changes made:
LuaPlusInternal.h
## -81,5 +81,6 ##
} // namespace LuaPlus
#if !LUAPLUS_EXCEPTIONS
+#include <stdio.h>
#include <assert.h>
#define luaplus_assert(e) if (!(e)) assert(0)
## -84,5 +85,6 ##
#include <assert.h>
#define luaplus_assert(e) if (!(e)) assert(0)
+#define luaplus_assertlog(e, msg) if (!(e)) { fprintf(stderr, msg); assert(0); }
//(void)0
#define luaplus_throw(e) assert(0)
//(void)0
LuaFunction.h
## -21,7 +21,7 ##
class LuaFunction
{
public:
- LuaFunction(LuaObject& _functionObj)
+ LuaFunction(const LuaObject& _functionObj)
: functionObj(_functionObj) {
}
## -36,7 +36,7 ##
if (lua_pcall(L, 0, 1, 0)) {
const char* errorString = lua_tostring(L, -1); (void)errorString;
- luaplus_assert(0);
+ luaplus_assertlog(0, errorString);
}
return LPCD::Type<RT>::Get(L, -1);
}
In the change above, I opted not to use std::cerr simply because C++ streams tend to be heavier than plain-old C-style io functions. This is especially true if you're using mingw as your toolchain -- the ld linker is unable to eliminate unused C++ stream symbols even if your program never uses it.
With that in place, here's an example where an unprotected call is made to a lua function so you can see the errorString printed out prior to the crash:
// snip...
int main(int argc, const char *argv[])
{
LuaStateAuto L ( LuaState::Create(true) );
LuaObject globals = L->GetGlobals();
globals.Register("Test", Test);
globals.Register("Print", Print);
if(argc > 1)
{
/*
if (L->DoFile(argv[argc - 1]))
std::cout << L->CheckString(1) << '\n';
/*/
L->LoadFile( argv[argc - 1] );
LuaFunction<int> f ( LuaObject (L, -1) );
f();
//*/
}
}
Running the above will trigger the crash but will include a semi-helpful error message:
g++ -Wall -pedantic -O0 -g -I ./Src -I ./Src/LuaPlus/lua51-luaplus/src plustest.cpp -o plustest.exe lua51-luaplus.dll
plustest.exe plustest.lua
plustest.lua:2: bad argument #1 to 'Print' (string expected, got table)Assertion failed!
Program: G:\OSS\luaplus51-all\plustest.exe
File: ./Src/LuaPlus/LuaFunction.h, Line 39
Expression: 0
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
first you may try to register the function using RegisterDirect(), this may avoid lua_CFunction's problem, check the luaplus manual.like this
LuaPlus::LuaObject globals = L->GetGlobals();
globals.RegisterDirect("Test",Test);
second if I remeber to create a table have two solutions,like this
//first
LuaObject globalsObj = state->GetGlobals();
LuaObject myArrayOfStuffTableObj = globalsObj.CreateTable("MyArrayOfStuff");
//second
LuaObject aStandaloneTableObj;
aStandaloneTableObj.AssignNewTable(state);
check whether you have use the right function.
third I remember the lua stack object is not the luaobject, they have a conversion, may be you can try this
LuaStackObject stack1Obj(state, 1);
LuaObject nonStack1Obj = stack1Obj;
forth, like the function Test() you have give above, the table tableObj you have pushing onto the lua stack, you must remember to clear the object.