3

My objective is to have a Delphi( or freepascal) code, that will call the C function func like this one:

The C/Cuda file:

/* this is the "progcuda.cu" file */
#include <stdio.h>

__global__ void foo(int *a, int *b, int *c, int n){
    /*
    add all the vector's element
    */
}


void func(int *a, int *b, int *c,int n){
    int *da,*db,*dc;
    cudaMalloc(&da, n*sizeof(int));
    cudaMalloc(&db, n*sizeof(int));
    cudaMalloc(&dc, n*sizeof(int));

    cudaMemcpy(da,a,sizeof(int)*n,cudaMemcpyHostToDevice);
    cudaMemcpy(db,b,sizeof(int)*n,cudaMemcpyHostToDevice);
    cudaMemcpy(dc,c,sizeof(int)*n,cudaMemcpyHostToDevice);

    foo<<<1,256>>>(da,db,dc);
    cudaMemcpy(c,dc,sizeof(int),cudaMemcpyDeviceToHost);

    /* do other stuff and call another Host and Device functions*/

    return;
}

The pascal main file:

// this is the "progpas.pas" file
program progpas;
{$mode objfpc}{$H+}
uses unitpas;

var
    ...


begin
    ...
    func(a, b, c, len);
    ...
end.

The pascal unit file:

// this is the "unitpas.pas" file
unit unitpas;
{$link progcuda.o}
interface

uses ctypes;
procedure func(a, b, c : cpint32 , n:cint32); cdecl; external;
procedure foo(a, b, c : cpint32 , n:cint32);cdecl; external;

implementation

end.

I've found this post Programming CUDA using Delphi or FreePascal , but it shows more a way to program CUDA in delphi.

I don't want to program CUDA in Delphi, I want to program in CUDA in pure C/C++ code and only call that C function in delphi.

What is the problem? How can I link the .cu code to the delphi one?

I'm using linux ubuntu 16.04 LTS, but I also have CUDA and VS in windows if necessary.

Note: if you guys could explain in detail how to do it, would help ( new to pascal and linking files )


I've already tried to generate the .o object file and link it in free pascal with
$ nvcc progcuda.cu -c -o progcuda.o then $fpc progpas.pas
but it fails at linking.

Note: I've tried once to link a normal .o generated by C code to pascal code, using gcc and freepascal compiler, and it worked, but if I use nvcc instead of gcc and rename the extension to .cu ( still same code), the linking fails.


note: new account in stack overflow, i cannot repply answers yet.

2 Answers2

5

I don't know anything about Delphi and FreePascal, but I do know about CUDA, C and C++, so maybe my solution will also work for you.

I'll be demonstrating it with a simple problem:

Content of f.cu:

int f() { return 42; }

Content of main.c:

extern int f();

int main() {
    return f();
}

The following works:

$ gcc -c -xc f.cu # need -xc to tell gcc it's a C file
$ gcc main.c f.o
(no errors emitted)

Now when we try replacing gcc with nvcc:

$ nvcc -c f.cu
$ gcc main.c f.o
/tmp/ccI3tBM1.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
f.o: In function `__cudaUnregisterBinaryUtil()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x52): undefined reference to `__cudaUnregisterFatBinary'
f.o: In function `__nv_init_managed_rt_with_module(void**)':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x6d): undefined reference to `__cudaInitModule'
f.o: In function `__sti____cudaRegisterAll()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0xa9): undefined reference to `__cudaRegisterFatBinary'
collect2: error: ld returned 1 exit status

The problem here is that nvcc adds references to some symbols from the CUDA runtime API when compiling f.cu, and these symbols have to be linked to the final executable. My CUDA installation is in /opt/cuda, so I will use that, but you have to replace it with wherever CUDA is installed on your system. So if we link libcudart.so when compiling the library we get:

$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
/tmp/ccUeDZcb.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
collect2: error: ld returned 1 exit status

This looks better, no strange errors, but it's still not finding the function f. That's because nvcc is treating f.cu as a C++ file, so it does name mangling when creating the object file, and we have to specify that we want f to have C, and not C++ linkage (see more here: http://en.cppreference.com/w/cpp/language/language_linkage). To do that we have to modify f.cu like this:

extern "C" int f() { return 42; }

Now when we do:

$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
(no errors emitted)

I hope you manage to modify this to work with your language.

EDIT: I tried a bit more complicated example:

// f.cu
#include <stdio.h>

__global__ void kernel() {
    printf("Running kernel\n");
}

extern "C" void f() {
    kernel<<<1, 1>>>();
    // make sure the kernel completes before exiting
    cudaDeviceSynchronize();
}

// main.c
extern void f();

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

When compiling it I got:

    f.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status

To fix it you also need to add the standard C++ libraries to the linker flags:

$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart -lstdc++
$ ./a.out
Running kernel
gflegar
  • 1,583
  • 6
  • 22
  • Maybe my answer and https://www.freepascal.org/docs-html/prog/progsu147.html#x186-1890007.1.1 will be enough to figure it out. – gflegar Apr 10 '18 at 20:22
4

I fixed the files as @Goran Flegar explained: Add extern "C" int func(...); to the .cu file. And then tried to compile/link the .cu code, but with no device calls (yet with device code), and all worked well.

but when i add a device call ( foo<<<Nb,Nt>>>(...) ) and compile with:

$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64

i get:

Free Pascal Compiler version 3.0.4 [2017/12/13] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling prog1.pas
Linking sum.exe
/usr/bin/ld: aviso: link.res contém seções de saída; você se esqueceu -T?
/usr/bin/ld: sum.o: undefined reference to symbol '_Unwind_Resume@@GCC_3.0'
//lib/x86_64-linux-gnu/libgcc_s.so.1: error adding symbols: DSO missing from command line
prog1.pas(16,1) Error: Error while linking
prog1.pas(16,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode

So there's still some missing libs.


Solution:

Found that linking the stdc++ and gcc_s lib to pascal solved the compilation problem.

unit unitpas;
// file "unitpas.pas"
{$LINK progcuda.o}
{$LINKLIB c}
{$LINKLIB cudart}
{$linklib stdc++}
{$linklib gcc_s}

interface

uses ctypes;
function func(x,y: cint32): cint32; cdecl; external;

implementation

end.

Run

$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64

and everything works.