RPC on LLVM IR: any example programs for this?

Hi everyone,

I want to use an LLVM new feature (llvm::orc::remote::RPCBase) to implement a simple RPC framework between two LLVM modules. It seems by this new class we can do serialization/deserialization on LLVM IR type system directly. But I haven’t found any helpful stuff on google showing me how to use these RPC APIs. Can anyone give me some hints or example programs showing me how to use these API correctly? Just a simple serialization/deserialization toy demo is OK, Thanks very much!

Best regards,

Shen

Hi Shen,

The best place to look for examples is currently the RPCUtilsTest unit test: llvm/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp

What API do you want to be able to call via RPC? If it’s small I can probably write up an example for you.

Cheers,
Lang.

Hi Lang,

Thanks for your reply! It seems that the code of ORC has been changed a lot these months. Actually what I wanted to see then was quite simple, Let’s say we have two modules M1.bc and M2.bc, and in M1 we want to call function foo(defined in M2.bc)with the function type

int foo(float, char*){…}

what basic steps do I need to do to make M1.bc call foo remotely?

BTW, does the RPCUtility supports handling pointers now?

Thanks very much!

Best regards,

Shen

Hi Shen,

Let’s say we have two modules M1.bc and M2.bc, and in M1 we want to call function foo(defined in M2.bc)with the function type

int foo(float, char*){…}

what basic steps do I need to do to make M1.bc call foo remotely?

There’s currently no built in support for calling functions with signatures other than void(), int(), or int(int, char*).

To call a function with a different signature you have a couple of options:

(1) Generate a wrapper, encode the args, then decode the return:

void wrapper(void *inputs, void *output) {
// args = decode(inputs)
int result = foo(args…);
// output = encode(result)
}

Then on the client end you have to write data to the inputs slab, send it to the server, call wrapper, read the outputs slab back, then decode it.

(2) If you know the signature of the function that you want to be able to call ahead of time, you can extend the JIT server’s RPC endpoint to handle your function type. (We would just need to make the RPC endpoint in OrcRemoteTargetServer publicly visible for this to work, but that’s an easy change):

// Declare your function pointer type.
typedef int (MyFuncType)(float, char);

// Write a handler to call into a function of your type.
int callMyFunc(JITTargetAddress MyFuncAddr, float Arg1, std::string Arg2) {
MyFuncType MyFunc = (MyFuncType)MyFuncAddr;
return MyFunc(Arg1, Arg2.c_str());
}

// Write an RPC function definition.
class CallMyFunc : public llvm::orc::rpc::Function<CallMyFunc, int(JITTargetAddress, float, std::string)> {
public:
static const char *getName() { return “CallMyFunc”; }
};

// Register the callMyFunc function as a handler for CallMyFunc (the RPC definition) with the RPC endpoint.
OrcRemoteTargetServer Server(…);
Server.getRPCEndpoint().addHandler(callMyFunc);

Now on your client you could write:

int Result = Client.getRPCEndpoint.callB(3.141, “pi”);

(3) If you don’t know the signature of the function at server build type, you can write the code from part (2) and JIT it into the server after you connect. This would require us extending the OrcRemoteTargetRPC API with a “RegisterRPCExtension” function, but that should also be pretty easy.

BTW, does the RPCUtility supports handling pointers now?

It does not, and I doubt it ever will - I expect it to remain limited to value types only.

The aim of Orc RPC is to be good enough for basic cases, and especially for testing purposes, but we don’t want it growing into a fully featured RPC library. That kind of use case would be better supported by writing a new client/server (out of tree) on top of a fully featured RPC library like GRPC or Cap’n Proto.

Cheers,
Lang.

Thank you for your helpful answer, Lang! I will try it.