ffi 库使用demo

FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI。libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。

动态调用C函数,使用libffi提供接口动态调用流程如下:
1. 准备好参数数据及其对应ffi_type数组、返回值内存指针、函数指针
2. 创建与函数特征相匹配的函数原型:ffi_cif对象
3. 使用“ffi_call”来完成函数调用

补全https://blog.csdn.net/JaimeCool/article/details/76332930中的源码,编译gcc rtl.cpp -fpermissive -w -lffi

结论:

1、指针functionPtr仅仅定义了一个普通数据类型的指针,并没有保证和函数testFunc拥有相同的函数指针类型,这样就可以通用的执行多种类似的函数

     可见使用ffi,只要有函数原型cif对象,函数实现指针,返回值内存指针和函数参数数组,我们就可以实现在运行时动态调用任意C函数

#include <stdio.h>
#include <ffi.h>
#include <stdlib.h>

int testFunc(int m, int n) {
    printf("params: %d %d \n", m, n);
    return m+n;
}

void testCall (void) {
    testFunc(1, 2);

    //拿函数指针
	// int (*funcPointer)(int, int) = &testFunc;
    void* functionPtr = &testFunc;
    int argCount = 2;

    //参数类型数组
    ffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount);
    ffiArgTypes[0] = &ffi_type_sint;
    ffiArgTypes[1] = &ffi_type_sint;

    //参数数据数组
    void **ffiArgs = alloca(sizeof(void *) *argCount);
    void *ffiArgPtr = alloca(ffiArgTypes[0]->size);
    int *argPtr = ffiArgPtr;
    *argPtr = 5;
    ffiArgs[0] = ffiArgPtr;

    void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size);
    int *argPtr2 = ffiArgPtr2;
    *argPtr2 = 3;
    ffiArgs[1] = ffiArgPtr2;

    //生成函数原型 ffi_cfi 对象
    ffi_cif cif;
    ffi_type *returnFfiType = &ffi_type_sint;
    ffi_status ffiPrepStatus = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, (unsigned int)argCount, returnFfiType, ffiArgTypes);

    if (ffiPrepStatus == FFI_OK) {
        //生成用于保存返回值的内存
        void *returnPtr = NULL;
        if (returnFfiType->size) {
            returnPtr = alloca(returnFfiType->size);
        }
        //根据cif函数原型,函数指针,返回值内存指针,函数参数数据调用这个函数
        ffi_call(&cif, functionPtr, returnPtr, ffiArgs);

        //拿到返回值
        int returnValue = *(int *)returnPtr;
        printf("ret: %d \n", returnValue);
    }
}

int main ()
{
	testCall();
	return 0;
}

进阶版本,参考llvm中omp的__tgt_rtl_run_target_region处理方法,限制条件设备函数无返回返回值,处理结果通过参数返回

#include <stdio.h>
#include <ffi.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <cassert>
#include <vector>

#define OFFLOAD_SUCCESS (0)
#define OFFLOAD_FAIL (~0)
#define OFFLOAD_DEVICE_DEFAULT     -1

#define DPxMOD "0x%0*" PRIxPTR
#define DPxPTR(ptr) ((int)(2 * sizeof(uintptr_t))), ((uintptr_t)(ptr))
// #define DPxPTR(ptr) ptr

#define PRIxPTR "l" "x"
#define DEBUG_PREFIX "TARGET aarch64 RTL"

#define DEBUGP(prefix, ...)                                                    \
  {                                                                            \
    fprintf(stderr, "%s --> ", prefix);                                        \
    fprintf(stderr, __VA_ARGS__);                                              \
  }

/// Emit a message for debugging
#define DP(...)                                                                \
  do {                                                                         \
      DEBUGP(DEBUG_PREFIX, __VA_ARGS__);                                       \
  } while (false)

#ifdef __cplusplus
extern "C" {
#endif

int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,
                                    void **tgt_args, ptrdiff_t *tgt_offsets,
                                    int32_t arg_num);

int32_t __tgt_rtl_run_target_team_region(int32_t device_id, void *tgt_entry_ptr,
                                         void **tgt_args,
                                         ptrdiff_t *tgt_offsets,
                                         int32_t arg_num, int32_t team_num,
                                         int32_t thread_limit,
                                         uint64_t loop_tripcount /*not used*/) {
  // ignore team num and thread limit.

  // Use libffi to launch execution.
  ffi_cif cif;

  // All args are references.
  std::vector<ffi_type *> args_types(arg_num, &ffi_type_pointer);
  std::vector<void *> args(arg_num);
  std::vector<void *> ptrs(arg_num);

  // for (int i=0; i<arg_num; i++) printf("params[%d]: %d + %d\n", i, *(int**)tgt_args[i], tgt_offsets[i]);
  
  for (int32_t i = 0; i < arg_num; ++i) {
    ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]);
	// printf("params[%d]: %d\n", i, *(int*)ptrs[i]);
    args[i] = &ptrs[i];
	// printf("params[%d]: %d\n", i, *(int*)args[i]);
  }

  // 函数输入参数个数及数据类型由arg_num及args_types指定,函数返回值类型为void
  ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_num,
                                   &ffi_type_void, &args_types[0]);

  assert(status == FFI_OK && "Unable to prepare target launch!");

  if (status != FFI_OK)
    return OFFLOAD_FAIL;

  DP("Running entry point at " DPxMOD "...\n", DPxPTR(tgt_entry_ptr));

  void (*entry)(void);
  *((void**) &entry) = tgt_entry_ptr;
  // Set NULL for return value Pointer, so return value must from arguement ?
  ffi_call(&cif, entry, NULL, &args[0]);
  return OFFLOAD_SUCCESS;
}

// return RTL->run_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets
int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,
                                    void **tgt_args, ptrdiff_t *tgt_offsets,
                                    int32_t arg_num) {
  // for (int i=0; i<arg_num; i++) printf("params[%d]: %d \n", i, *(int**)tgt_args[i]);
  // use one team and one thread.
  return __tgt_rtl_run_target_team_region(device_id, tgt_entry_ptr, tgt_args,
                                          tgt_offsets, arg_num, 1, 1, 0);
}

// omp 函数的传参类似引用,非值传递
void testFunc(int &m, int &n) {
    printf("params: %d %d \n", m, n);
	m += n;
    return;
}

int main (void) {

    //  获取函数指针
    void* functionPtr = &testFunc;
    int argCount = 2;
	int i;

    // 参数类型数组, 两个参数的类型为sint
    ffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount);
    ffiArgTypes[0] = &ffi_type_sint;
    ffiArgTypes[1] = &ffi_type_sint;

    //参数数据数组
    void **ffiArgs = alloca(sizeof(void *) *argCount);
	ptrdiff_t *tgt_offsets = alloca(sizeof(ptrdiff_t) *argCount);
    void *ffiArgPtr = alloca(ffiArgTypes[0]->size);
    int *argPtr = ffiArgPtr;
    *argPtr = 5;
    ffiArgs[0] = ffiArgPtr;

    void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size);
    int *argPtr2 = ffiArgPtr2;
    *argPtr2 = 3;
    ffiArgs[1] = ffiArgPtr2;
	
	for (i=0; i<argCount; i++)
		tgt_offsets[i] = 0; // 参考processDataBefore,只有OMP_TGT_MAPTYPE_PRIVATE需要设置

	__tgt_rtl_run_target_region(-1, functionPtr, ffiArgs, tgt_offsets, argCount);
	
	printf("return value from param: %d\n", *(int*)ffiArgs[0]);
	return 0;
}

#ifdef __cplusplus
}
#endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值