/*
 * SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION &
 * AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// .incbin macro for embedding raw binary blobs (cubin payloads) into a
// translation unit.
//
// Scope: Linux only, GCC/Clang only, x86_64 / aarch64 only.
//
// Why two macros?
//   * `TLLM_INCBIN(SYMBOL, FILENAME)` -- emits flat C-linkage globals named
//     `<SYMBOL>`, `<SYMBOL>_end`, `<SYMBOL>_len`. Useful for one-off cases
//     where collision is not a concern.
//   * `TLLM_INCBIN_NS(SYMBOL, ASM_DATA, ASM_END, ASM_LEN, FILENAME)` -- emits
//     globals with C++-mangled linker names so they're scoped to the
//     enclosing namespace (typically `tensorrt_llm::_v1::kernels::...`).
//     The mangling protects against multi-definition errors when two
//     packages link against the same kernel set, or when two TRT-LLM
//     ABI versions coexist in the same binary.
//
// The cubin aggregator generated by `cpp/cmake/modules/tllm_cubin_archive.cmake`
// uses `TLLM_INCBIN_NS` exclusively. The CMake helper computes the Itanium
// mangling for the chosen namespace path (default `tensorrt_llm::_v1::kernels`)
// at configure time and passes the three mangled names to each macro
// invocation.
//
// Layout of emitted data:
//   * `.section .rodata.tllm_incbin.<asm_data>`, 64-byte aligned
//   * `.incbin "<file>"` pulls the raw bytes verbatim
//   * `<asm_end>` is a label one byte past the end (also exported)
//   * `<asm_len>` is a 32-bit little-endian byte count, stored in the same
//     `.rodata` section right after the data (4-byte aligned)
//
// All three labels are global so consumer translation units can reference
// them. None are marked `.hidden` -- visibility control belongs in the
// consumer's CMake target, not in this header.

#pragma once

static_assert(sizeof(unsigned int) == 4, "Unsupported platform: sizeof(int) != 4!");

#if defined(_MSC_VER) || defined(_WIN32)
// Windows/MSVC: cubin embedding is not supported. Define macros as no-ops
// so empty aggregators can compile.
#define TLLM_INCBIN(SYMBOL, FILENAME)
#define TLLM_INCBIN_NS(SYMBOL, ASM_DATA, ASM_END, ASM_LEN, FILENAME)
#else // GCC/Clang on Linux

#if !defined(__GNUC__) && !defined(__clang__)
#error "cubinIncbin.h supports only GCC and Clang"
#endif

#if !defined(__linux__)
#error "cubinIncbin.h supports only Linux"
#endif

#if !(defined(__x86_64__) || defined(__aarch64__))
#error "cubinIncbin.h supports only x86_64 / aarch64"
#endif

#define TLLM_INCBIN_STR2(X) #X
#define TLLM_INCBIN_STR(X) TLLM_INCBIN_STR2(X)

// Internal helper: emit the asm block + section housekeeping. ASM_DATA,
// ASM_END, ASM_LEN are *string literals* containing the linker symbol names
// the asm block defines. FILENAME is the .cubin path.
#define TLLM_INCBIN_ASM_BLOCK(ASM_DATA, ASM_END, ASM_LEN, FILENAME)                                                    \
    __asm__(".section .rodata.tllm_incbin." ASM_DATA                                                                   \
            ",\"a\"\n"                                                                                                 \
            ".balign 64\n"                                                                                             \
            ".global " ASM_DATA                                                                                        \
            "\n"                                                                                                       \
            ".type " ASM_DATA ", @object\n" ASM_DATA                                                                   \
            ":\n"                                                                                                      \
            ".incbin \"" FILENAME                                                                                      \
            "\"\n"                                                                                                     \
            ".global " ASM_END                                                                                         \
            "\n"                                                                                                       \
            ".type " ASM_END ", @object\n" ASM_END                                                                     \
            ":\n"                                                                                                      \
            ".balign 4\n"                                                                                              \
            ".global " ASM_LEN                                                                                         \
            "\n"                                                                                                       \
            ".type " ASM_LEN                                                                                           \
            ", @object\n"                                                                                              \
            ".size " ASM_LEN ", 4\n" ASM_LEN                                                                           \
            ":\n"                                                                                                      \
            ".int " ASM_END " - " ASM_DATA                                                                             \
            "\n"                                                                                                       \
            ".previous\n")

// Flat-symbol form. SYMBOL must be a valid C identifier and unique within
// the final link. Emits `<SYMBOL>`, `<SYMBOL>_end`, `<SYMBOL>_len` with
// extern "C" linkage.
#define TLLM_INCBIN(SYMBOL, FILENAME)                                                                                  \
    TLLM_INCBIN_ASM_BLOCK(                                                                                             \
        TLLM_INCBIN_STR(SYMBOL), TLLM_INCBIN_STR(SYMBOL) "_end", TLLM_INCBIN_STR(SYMBOL) "_len", FILENAME);            \
    extern "C" __attribute__((aligned(64))) unsigned char const SYMBOL[];                                              \
    extern "C" __attribute__((aligned(64))) unsigned char const SYMBOL##_end[];                                        \
    extern "C" unsigned int const SYMBOL##_len

// Namespace-aware form. SYMBOL is the (unmangled) C++ identifier; ASM_DATA,
// ASM_END, ASM_LEN are pre-computed Itanium-mangled linker names provided by
// the build system (see cpp/cmake/modules/tllm_cubin_archive.cmake).
//
// The C++ declarations are NOT extern "C": they live at whatever scope the
// macro is invoked in (typically a namespace block). Each declaration carries
// an `asm()` label that pins the linker symbol to the supplied mangled name,
// matching what the asm block emits. Consumer translation units that declare
// the same variable inside the same namespace (without an asm label) will
// have the compiler mangle the reference to the same name -- so consumer
// references resolve without any code generator on the consumer side.
#define TLLM_INCBIN_NS(SYMBOL, ASM_DATA, ASM_END, ASM_LEN, FILENAME)                                                   \
    TLLM_INCBIN_ASM_BLOCK(ASM_DATA, ASM_END, ASM_LEN, FILENAME);                                                       \
    __attribute__((aligned(64))) extern unsigned char const SYMBOL[] asm(ASM_DATA);                                    \
    __attribute__((aligned(64))) extern unsigned char const SYMBOL##_end[] asm(ASM_END);                               \
    extern unsigned int const SYMBOL##_len asm(ASM_LEN)

#endif // !defined(_MSC_VER) && !defined(_WIN32)
