Files
rocm-systems/samples/2_Cookbook/15_static_library
Aaron En Ye Shi cfcf04d502 SWDEV-222423 - Add HIP Sample 2_Cookbook/15_static_library
HIP supports emitting two types of static libraries. One type
will export host functions and is compatible with host linkers.
The second type exports device functions, but is generated with
ar manually. Also, add a README with steps on how to run these
samples with Makefile or CMake.

Change-Id: I1be15c2884583b370092bc8e4bf04f726f8f5a27
2021-04-27 16:05:15 -04:00
..

Emitting Static Library

This sample shows how to generate a static library for a simple HIP application. We will evaluate two types of static libraries: the first type exports host functions in a static library generated with --emit-static-lib and is compatible with host linkers, and second type exports device functions in a static library made with system ar.

Please refer to the hip_programming_guide for limitations.

Static libraries with host functions

Source files

The static library source files may contain host functions and kernel __global__ and __device__ functions. Here is an example (please refer to the directory host_functions).

hipOptLibrary.cpp:

#define HIP_ASSERT(status) assert(status == hipSuccess)
#define LEN 512

__global__ void copy(uint32_t* A, uint32_t* B) {
    size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
    B[tid] = A[tid];
}

void run_test1() {
    uint32_t *A_h, *B_h, *A_d, *B_d;
    size_t valbytes = LEN * sizeof(uint32_t);

    A_h = (uint32_t*)malloc(valbytes);
    B_h = (uint32_t*)malloc(valbytes);
    for (uint32_t i = 0; i < LEN; i++) {
        A_h[i] = i;
        B_h[i] = 0;
    }

    HIP_ASSERT(hipMalloc((void**)&A_d, valbytes));
    HIP_ASSERT(hipMalloc((void**)&B_d, valbytes));

    HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice));
    hipLaunchKernelGGL(copy, dim3(LEN/64), dim3(64), 0, 0, A_d, B_d);
    HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost));

    for (uint32_t i = 0; i < LEN; i++) {
        assert(A_h[i] == B_h[i]);
    }

    HIP_ASSERT(hipFree(A_d));
    HIP_ASSERT(hipFree(B_d));
    free(A_h);
    free(B_h);
    std::cout << "Test Passed!\n";
}

The above source file can be compiled into a static library, libHipOptLibrary.a, using the --emit-static-lib flag, like so:

hipcc hipOptLibrary.cpp --emit-static-lib -fPIC -o libHipOptLibrary.a

Main source files

The main() program source file may link with the above static library using either hipcc or a host compiler (such as g++). A simple source file that calls the host function inside libHipOptLibrary.a:

hipMain1.cpp:

extern void run_test1();

int main(){
  run_test1();
}

To link to the static library:

Using hipcc:

hipcc hipMain1.cpp -L. -lHipOptLibrary -o test_emit_static_hipcc_linker.out

Using g++:

g++ hipMain1.cpp -L. -lHipOptLibrary -L/opt/rocm/hip/lib -lamdhip64 -o test_emit_static_host_linker.out

Static libraries with device functions

Source files

The static library source files which contain only __device__ functions need to be created using ar. Here is an example (please refer to the directory device_functions).

hipDevice.cpp:

#include <hip/hip_runtime.h>

__device__ int square_me(int A) {
  return A*A;
}

The above source file may be compiled into a static library, libHipDevice.a, by first compiling into a relocatable object, and then placed in an archive using ar:

hipcc hipDevice.cpp -c -fgpu-rdc -fPIC -o hipDevice.o
ar rcsD libHipDevice.a hipDevice.o

Main source files

The main() program source file can link with the static library using hipcc. A simple source file that calls the device function inside libHipDevice.a:

hipMain2.cpp:

#include <hip/hip_runtime.h>
#include <hip/hip_runtime_api.h>
#include <iostream>

#define HIP_ASSERT(status) assert(status == hipSuccess)
#define LEN 512

extern __device__ int square_me(int);

__global__ void square_and_save(int* A, int* B) {
    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    B[tid] = square_me(A[tid]);
}

void run_test2() {
    int *A_h, *B_h, *A_d, *B_d;
    A_h = new int[LEN];
    B_h = new int[LEN];
    for (unsigned i = 0; i < LEN; i++) {
        A_h[i] = i;
        B_h[i] = 0;
    }
    size_t valbytes = LEN*sizeof(int);

    HIP_ASSERT(hipMalloc((void**)&A_d, valbytes));
    HIP_ASSERT(hipMalloc((void**)&B_d, valbytes));

    HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice));
    hipLaunchKernelGGL(square_and_save, dim3(LEN/64), dim3(64),
                       0, 0, A_d, B_d);
    HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost));

    for (unsigned i = 0; i < LEN; i++) {
        assert(A_h[i]*A_h[i] == B_h[i]);
    }

    HIP_ASSERT(hipFree(A_d));
    HIP_ASSERT(hipFree(B_d));
    free(A_h);
    free(B_h);
    std::cout << "Test Passed!\n";
}

int main(){
  // Run test that generates static lib with ar
  run_test2();
}

To link to the static library:

hipcc libHipDevice.a hipMain2.cpp -fgpu-rdc -o test_device_static_hipcc.out

How to build and run this sample:

Use the make command to build the static libraries, link with it, and execute it.

  • Change directory to either host or device functions folder.
  • To build the static library and link the main executable, use make all.
  • To execute, run the generated executable ./test_*.out.

Alternatively, use these CMake commands.

cd device_functions
mkdir -p build
cd build
cmake ..
make
./test_*.out

For More Infomation, please refer to the HIP FAQ.