GraalVM Polyglot: pass arguments from Java to LLVM (C++) - graalvm

Following the docs I could run Polyglot application where the start language is Java and the target language is C++, which is in a separated source file.
I wonder how could I pass some arguments from Java to C++.
Code examples
Start Language (Java)
import org.graalvm.polyglot.*;
import java.io.File;
import java.io.IOException;
public class HelloPolyglot {
public static void main(String[] args) throws IOException {
File file = new File("polyglot"); // the path to the file
Source source = Source.newBuilder("llvm", file).build();
Context polyglot = Context.newBuilder().allowAllAccess(true).build();
Value cpart = polyglot.eval(source);
cpart.executeVoid();
}
}
Target Language (C++)
#include <iostream>
using namespace std;
int main(int argc, char** argv){
cout << "You have entered " << argc
<< " arguments:" << "\n";
for (int i = 0; i < argc; ++i)
cout << argv[i] << "\n";
return 0;
}
Thank you in advance.

As answered by Schatz on GraalVM Slack Server you can pass arguments to LLVM by:
String[] arguments = {"Hello", "World"};
Context polyglot = Context.newBuilder().arguments("llvm", arguments).allowAllAccess(true).build();
Value cpart = polyglot.eval(source);
cpart.executeVoid();
Alternatively, you can call functions directly, with
cpart.readMember("functionName").execute(arguments);

Well, I found your question very interesting and recently faced a lot of issues, and unfortunately, I don't found any detailed guide about this. As i result I will provide a full explanation of how I make it work to call c++ function from java with parameters and return a result.
So let's begin its important to have installed the llvm-toolchain so if you don't simply run:
gu install llvm-toolchain
So assuming the c++ code is the following:
#include<iostream>
using namespace std;
// Function to find the sum of integer array
// extern "C" is required to suppress mangling
extern "C" int getSumOfArray(int array[], int size) {
printf("C++ => Find sum of numbers in an array\n");
int i, sum = 0;
for(i = 0; i < size; i++) {
sum += array[i];
}
return sum;
}
The magic trick that makes all things work is to feed you java polyglot code with the binary produced file and not the .cpp source because probably it will not work and you will face exceptions like this:
invalid elf header
So to move one simply run the following two commands
export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
$LLVM_TOOLCHAIN/clang++ -shared cpppart.cpp -lpolyglot-mock -o binary
After that, run the following java code: (Note that we don't use any extension it is no needed):
File file=new File("binary");
int[] input = new int[] { 4, 2, 8, 5, 20, 1, 40, 13, 23 };
Context context = Context.newBuilder().allowAllAccess(true).build();
Source source = Source.newBuilder("llvm", file).build();
context.eval(source);
Value cpart = context.getBindings("llvm").getMember("getSumOfArray");;
int sum = cpart.execute(input, input.length).asInt();
System.out.println(sum);
Et voila!!!!!! you will receive this message in java output:
116
C++ => Find sum of numbers in an array

Related

array<double*> to vector<double> range v3 style on VS2019

I want to convert an array<double*> to a vector<double> so that I can do a ranges::views::concat on two vectors of the same type but I'm having difficulty doing this.
I have the following code:
#include <range/v3/all.hpp>
#include <array>
static constexpr auto max_elements = 2000;
struct PriceInfo
{
std::array<double*, max_elements> prices;
};
auto main() -> int
{
const PriceInfo* const buf_prices = nullptr;
const auto vec = buf_prices->prices
| ranges::views::indirect
| ranges::to_vector;
}
When I compile I'm getting the following errors and I don't understand why as I believe my code is correct, at least it compiles errorfree under gcc.
Error (active) E0349 no operator "|" matches these operands
How do I fix this error please?
FIrst of, your code contains UB, because you had never created PriceInfo.
Second, the error may mean incorrect project configuration, is standard set? is compiler fully compatible with requirements of library? Is it proper fork of library, if that's the case (e.g. there were separate forks for MSVC compilers).
Third, assuming those problems will be solved, this code would segfault unless ALL elements of prices are non-nullptr.
This way it works:
#include <range/v3/all.hpp>
#include <iostream>
#include <array>
static constexpr auto max_elements = 3; // will segfault if there are null pointers
struct PriceInfo
{
std::array<double*, max_elements> prices;
};
auto main() -> int
{
auto a = std::array<double,3>{1.0, 2.0, 3.0};
const PriceInfo* const buf = new PriceInfo{&a[0], &a[1], &a[2]};
const auto vec = buf->prices
| ranges::views::indirect
| ranges::to_vector;
for( auto a : vec)
std::cout << a << "\n";
}

How to pass a file stream as an argument to a Rcpp-function?

I am new to Rcpp. My problem: I want to have a function (writeToFile) that writes to a file on my hard drive.
This function shall be called several times within a second function (foo). Thus I want to open the stream only once, write to the file and close the stream.
What I've already learned: In C++ streams need to be passed by reference.
My code, however, where the argument myfile is passed by reference does not compile:
#include <Rcpp.h>
#include <fstream>
using namespace Rcpp;
using namespace std;
// [[Rcpp::export]]
void writeToFile (ostream& myfile, string text) {
myfile << text << "\n";
}
// [[Rcpp::export]]
void foo (string text, int n, string file = "example.txt") {
ofstream myfile;
myfile.open(file.c_str());
for (int i = 0; i < n; i++) {
writeToFile(myfile, text);
}
}
I get the error message:
std::basic_ostream<_CharT, _Traits>::basic_ostream(std::basic_ostream<<_CharT, _Traits>&&) [with _CharT = char; _Traits = std::char_traits<char>]' is protected within this context.
Any tips what I'm doing wrong?

Usage of FunctionPass over ModulePass when creating LLVM passes

I've seen quite a numerous amount of examples that go over creating functions passes (e.g. Brandon Holt and Adrian Sampson), but I am curious as to the difficulty in creating a module pass to do these very similar problems. I've tried to implement a module pass to display the global variable names using this example and llvm source code to understand how you have to iterate through members.
I am using a source compiled version of LLVM, and using the example from the above links to add the pass, and then running:
$ clang -Xclang -load -Xclang build/Skeleton/libSkeletonPass.so something.c
Which then returns this gibberish. However, if I implement a functionPass and just use Auto to determine the type to be initialized it's very straight forward and works. Am I just going about printing the global variables the wrong way?
This is a pastebin of the error output from the terminal. link
Skeleton.cpp
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/IR/LLVMContext.h"
using namespace llvm;
namespace {
// Helper method for converting the name of a LLVM type to a string
static std::string LLVMTypeAsString(const Type *T) {
std::string TypeName;
raw_string_ostream N(TypeName);
T->print(N);
return N.str();
}
struct SkeletonPass : public ModulePass {
static char ID;
SkeletonPass() : ModulePass(ID) {}
virtual bool runOnModule(Module &M) {
for (Module::const_global_iterator GI = M.global_begin(),
GE = M.global_end(); GI != GE; ++GI) {
errs() << "Found global named: " << GI->getName()
<< "\tType: " << LLVMTypeAsString(GI->getType()) << "!\n";
}
return false;
}
};
}
char SkeletonPass::ID = 0;
// Automatically enable the pass.
// http://adriansampson.net/blog/clangpass.html
static void registerSkeletonPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new SkeletonPass());
}
static RegisterStandardPasses
RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible,
registerSkeletonPass);
something.c
int value0 = 5;
int main(int argc, char const *argv[])
{
int value = 4;
value += 1;
return 0;
}
I was able to figure this out after some extensive github searching. Here is the answer from which I was following a tutorial to help others who may be curious how to implement a Module Pass.

How to discover lock declaration instruction in llvm?

I'm new to llvm , and was trying to find lock declaration statement and then do some instrumention work,the code like this:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int share = 42;
mutex m;
void f()
{
m.lock();
--share;
cout << "function f -> share: " << share << '\n';
m.unlock();
}
int main()
{
thread thf{f};
thf.join();
return 0;
}
I want to find the lock declaration instruction eg:
mutex m;
the llvm instrumention pass like this:
struct SkeletonPass : public FunctionPass {
static char ID;
SkeletonPass() : FunctionPass(ID) {}
virtual bool runOnFunction(Function &F) {
// Get the function to call from our runtime library.
LLVMContext &Ctx = F.getContext();
Constant *logFunc = F.getParent()->getOrInsertFunction(
"logop", Type::getVoidTy(Ctx), Type::getInt32Ty(Ctx), NULL
);
for (auto &B : F) {
for (auto &I : B) {
***if ((&I) is lock declaration instruction)*** {
// Insert something *after* `op`.
IRBuilder<> builder(op);
builder.SetInsertPoint(&B, ++builder.GetInsertPoint());
// Insert a call to function.
builder.CreateCall(logFunc, ConstantInt::get(Type::getInt32Ty(Ctx), 2));
return true;
}
}
}
In short, could you please tell me how to discover lock declaration instruction, thanks!
The declaration would appear as a global, so you should write a module pass to find it, not a function pass. It should appear as something like:
#m = global %mutex zeroinitializer
In fact, using the demo at http://ellcc.org/demo/index.cgi to try this, you can indeed see that:
...
%"class.std::__1::mutex" = type { %struct.pthread_mutex_t }
%struct.pthread_mutex_t = type { %union.anon }
%union.anon = type { [5 x i8*] }
...
#m = global %"class.std::__1::mutex" zeroinitializer, align 8
You can use LLVM's CppBackend to compile your code. This would produce a C++ code that makes up the source. You can then easily find out how mutex m; definition is constructed via LLVM API.
Run clang -march=cpp foo.cpp to use CppBackend. Alternatively, you can use this demo page to compile your code online.

How to create and using vapi files?

I want to make a custom vapi file, I have the basic stuff but I obviously miss something and I can't find anywhere how to do this properly. My main goal is to create a torent app, using libtorrent, and create the GUI (the frontend?) with vala and gtk.
I have a c_func_head.h:
#ifndef WHATEVER_H_INCLUDED
#define WHATEVER_H_INCLUDED
int add(int a, int b);
#endif
c_functions.c:
#include <stdio.h>
#include <stdlib.h>
#include "c_func_head.h"
int add(int a, int b){
printf("Adding numbers in c...\n");
return a+b;
}
vala_p.vapi:
[CCode (cheader_filename = "c_func_head.h")]
namespace MyFunc {
[CCode (cname = "add")]
public int add (int a, int b);
}
and finally vala_program.vala:
//extern int add(int a, int b);
using MyFunc;
void main(){
stdout.printf("Calling a c function...\n");
//stdout.printf("The sum is: %d\n", add2number(2, 2));
int sum = add(2, 2);
stdout.printf("The sum is: %d\n", sum);
}
As you can see I used an extern too, it worked with it but I want to use vapi files.
I compiled with (everything is in the same folder):
valac vala_program.vala --vapidir=vala_p.vapi -o mustrun
and the error is:
The namespace name `MyFunc' could not be found using MyFunc;
One more thing. Is it possible to make bindings for libtorrent? It uses c++ and I gues I have to use c++ too.
You can't make Vala bindings of C++ code. Only C. There a a guide to writing legacy bindings and a binding for Transmission, which is C-based.
As for the specific error you have, you want to call valac vala_program.vala vala_p.vapi if the library (i.e., header files) are the the same directory or valac vala_program.vala --pkg vala_p --vapidir=/path/to/directory/containing/vapi.

Resources