#include "nvfp4_linear.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr int kAbiVersion = 1; constexpr int kFp4BlockElements = 16; constexpr int kScaleTileOuter = 128; constexpr int kScaleTileInner = 4; constexpr int kWarpsPerQuantBlock = 8; constexpr int kQuantThreads = 32 * kWarpsPerQuantBlock; constexpr int kReduceThreads = 256; constexpr int kReduceItemsPerThread = 4; constexpr int kBiasThreads = 256; constexpr float kFp4E2M1Max = 6.0f; constexpr float kFp4TensorScaleMax = 448.0f; constexpr float kTensorScaleDenominator = kFp4E2M1Max * kFp4TensorScaleMax; constexpr size_t kWorkspaceBytes = 64ull * 1024ull * 1024ull; thread_local std::string g_last_error; [[noreturn]] void fail(const std::string& message) { throw std::runtime_error(message); } #define CUDA_CHECK(expr) \ do { \ const cudaError_t status_ = (expr); \ if (status_ != cudaSuccess) { \ fail(std::string(#expr) + ": " + cudaGetErrorString(status_)); \ } \ } while (0) #define CUBLASLT_CHECK(expr) \ do { \ const cublasStatus_t status_ = (expr); \ if (status_ != CUBLAS_STATUS_SUCCESS) { \ fail(std::string(#expr) + " failed with cuBLASLt status " + \ std::to_string(static_cast(status_))); \ } \ } while (0) int round_up(int value, int multiple) { if (value <= 0 || multiple <= 0 || value > std::numeric_limits::max() - (multiple - 1)) { fail("invalid or overflowing round_up arguments"); } return ((value + multiple - 1) / multiple) * multiple; } size_t checked_multiply(size_t left, size_t right, const char* label) { if (right != 0 && left > std::numeric_limits::max() / right) { fail(std::string(label) + " size overflows size_t"); } return left * right; } size_t round_up_divide(size_t dividend, size_t divisor) { return (dividend + divisor - 1) / divisor; } struct ScaleLayout { int inner_dim = 0; int outer_tiles = 0; size_t bytes = 0; }; ScaleLayout make_scale_layout(int rows_k, int outer_columns) { if (rows_k <= 0 || rows_k % 16 != 0 || outer_columns <= 0) { fail("scale layout requires positive K divisible by 16 and positive M/N"); } ScaleLayout layout; layout.inner_dim = round_up(rows_k / kFp4BlockElements, kScaleTileInner); layout.outer_tiles = (outer_columns + kScaleTileOuter - 1) / kScaleTileOuter; layout.bytes = checked_multiply( checked_multiply(static_cast(layout.outer_tiles), static_cast(layout.inner_dim), "scale tensor"), static_cast(kScaleTileOuter), "scale tensor"); return layout; } size_t packed_weight_bytes_checked(int n, int k) { if (n <= 0 || k <= 0 || n % 8 != 0 || k % 32 != 0) { fail("NVFP4 weight requires N divisible by 8 and K divisible by 32"); } return checked_multiply(static_cast(n), static_cast(k), "packed weight") / 2; } size_t host_scale_offset(int outer, int inner_scale, int scale_inner_dim) { const int outer_tile = outer / kScaleTileOuter; const int local_outer = outer % kScaleTileOuter; const int local_inner = inner_scale % kScaleTileInner; const int inner_tile_start = inner_scale - local_inner; const size_t tile_base = static_cast(inner_tile_start + outer_tile * scale_inner_dim) * kScaleTileOuter; return tile_base + static_cast(local_outer % 32) * 16 + static_cast(local_outer / 32) * 4 + local_inner; } __device__ __forceinline__ size_t device_scale_offset( int outer, int inner_scale, int scale_inner_dim) { const int outer_tile = outer / kScaleTileOuter; const int local_outer = outer % kScaleTileOuter; const int local_inner = inner_scale % kScaleTileInner; const int inner_tile_start = inner_scale - local_inner; const size_t tile_base = static_cast(inner_tile_start + outer_tile * scale_inner_dim) * kScaleTileOuter; return tile_base + static_cast(local_outer % 32) * 16 + static_cast(local_outer / 32) * 4 + local_inner; } float host_ue4m3_to_float(uint8_t raw) { const __half_raw half_raw = __nv_cvt_fp8_to_halfraw(raw, __NV_E4M3); return __half2float(static_cast<__half>(half_raw)); } __device__ __forceinline__ float device_ue4m3_to_float(uint8_t raw) { const __half_raw half_raw = __nv_cvt_fp8_to_halfraw(raw, __NV_E4M3); return __half2float(static_cast<__half>(half_raw)); } __device__ __forceinline__ float tensor_scale_from_amax(float amax) { return amax == 0.0f ? 1.0f : amax / kTensorScaleDenominator; } __device__ __forceinline__ float block_reduce_max(float value) { __shared__ float shared[kReduceThreads]; shared[threadIdx.x] = value; __syncthreads(); for (int offset = kReduceThreads / 2; offset > 0; offset >>= 1) { if (threadIdx.x < offset) { shared[threadIdx.x] = fmaxf(shared[threadIdx.x], shared[threadIdx.x + offset]); } __syncthreads(); } return shared[0]; } __global__ void reduce_abs_max_bf16( const __nv_bfloat16* source, float* block_maxima, size_t element_count) { const size_t block_start = static_cast(blockIdx.x) * kReduceThreads * kReduceItemsPerThread; float local_max = 0.0f; for (int item = 0; item < kReduceItemsPerThread; ++item) { const size_t index = block_start + static_cast(threadIdx.x) + static_cast(item) * kReduceThreads; if (index < element_count) { local_max = fmaxf(local_max, fabsf(__bfloat162float(source[index]))); } } const float block_max = block_reduce_max(local_max); if (threadIdx.x == 0) { block_maxima[blockIdx.x] = block_max; } } __global__ void reduce_max_float( const float* source, float* block_maxima, size_t element_count) { const size_t block_start = static_cast(blockIdx.x) * kReduceThreads * kReduceItemsPerThread; float local_max = 0.0f; for (int item = 0; item < kReduceItemsPerThread; ++item) { const size_t index = block_start + static_cast(threadIdx.x) + static_cast(item) * kReduceThreads; if (index < element_count) { local_max = fmaxf(local_max, source[index]); } } const float block_max = block_reduce_max(local_max); if (threadIdx.x == 0) { block_maxima[blockIdx.x] = block_max; } } __global__ void finalize_activation_scale( const float* activation_amax, const float* weight_tensor_scale, float* activation_tensor_scale, float* fp4_alpha) { if (blockIdx.x == 0 && threadIdx.x == 0) { const float activation_scale = tensor_scale_from_amax(activation_amax[0]); activation_tensor_scale[0] = activation_scale; fp4_alpha[0] = weight_tensor_scale[0] * activation_scale; } } // logical_m may be smaller than padded_m. Padded rows are written as exact // FP4 zero with zero block scales, so reusable scratch never exposes a prior // call's tail. __global__ void dynamic_quantize_activation( const __nv_bfloat16* source, uint8_t* destination_fp4, uint8_t* destination_scales, const float* activation_tensor_scale, int rows_k, int logical_m, int scale_inner_dim, uint64_t padded_scale_blocks) { const int lane = threadIdx.x & 31; const int warp_in_block = threadIdx.x >> 5; const uint64_t logical_block = static_cast(blockIdx.x) * kWarpsPerQuantBlock + static_cast(warp_in_block); if (logical_block >= padded_scale_blocks) { return; } const int blocks_per_column = rows_k / kFp4BlockElements; const int outer = static_cast(logical_block / blocks_per_column); const int inner_scale = static_cast( logical_block - static_cast(outer) * blocks_per_column); const int first_row = inner_scale * kFp4BlockElements; const size_t column_base = static_cast(outer) * rows_k; const bool valid_outer = outer < logical_m; const float tensor_scale = activation_tensor_scale[0]; const float inverse_tensor_scale = tensor_scale == 0.0f ? 0.0f : 1.0f / tensor_scale; float value0 = 0.0f; float value1 = 0.0f; float magnitude = 0.0f; size_t destination_source_index = 0; if (lane < kFp4BlockElements / 2) { const int row0 = first_row + lane * 2; destination_source_index = column_base + row0; if (valid_outer) { value0 = __bfloat162float(source[destination_source_index]) * inverse_tensor_scale; value1 = __bfloat162float(source[destination_source_index + 1]) * inverse_tensor_scale; magnitude = fmaxf(fabsf(value0), fabsf(value1)); } } for (int offset = 16; offset > 0; offset >>= 1) { magnitude = fmaxf(magnitude, __shfl_down_sync(0xffffffffU, magnitude, offset)); } float rounded_scale = 0.0f; if (lane == 0) { const uint8_t scale_raw = __nv_cvt_float_to_fp8( magnitude / kFp4E2M1Max, __NV_SATFINITE, __NV_E4M3); rounded_scale = device_ue4m3_to_float(scale_raw); destination_scales[device_scale_offset( outer, inner_scale, scale_inner_dim)] = scale_raw; } rounded_scale = __shfl_sync(0xffffffffU, rounded_scale, 0); if (lane < kFp4BlockElements / 2) { const float inverse_scale = rounded_scale == 0.0f ? 0.0f : 1.0f / rounded_scale; destination_fp4[destination_source_index / 2] = __nv_cvt_float2_to_fp4x2( make_float2(value0 * inverse_scale, value1 * inverse_scale), __NV_E2M1, cudaRoundNearest); } } __global__ void add_bias_bf16( __nv_bfloat16* output, const __nv_bfloat16* bias, size_t element_count, int n) { const size_t index = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; if (index < element_count) { const float value = __bfloat162float(output[index]); const float bias_value = __bfloat162float(bias[index % n]); output[index] = __float2bfloat16(value + bias_value); } } size_t reduction_block_count(size_t element_count) { return round_up_divide( element_count, static_cast(kReduceThreads * kReduceItemsPerThread)); } size_t reduction_scratch_elements(size_t element_count) { size_t max_blocks = 1; while (element_count > 1) { const size_t blocks = reduction_block_count(element_count); max_blocks = std::max(max_blocks, blocks); element_count = blocks; } return max_blocks; } void enqueue_activation_amax_reduce( const __nv_bfloat16* source, size_t element_count, float* scratch_a, float* scratch_b, float* destination_amax, int max_grid_x, cudaStream_t stream) { const float* current_source = nullptr; float* current_destination = scratch_a; size_t current_count = element_count; bool first_stage = true; while (true) { const size_t blocks = reduction_block_count(current_count); if (blocks == 0 || blocks > static_cast(max_grid_x)) { fail("activation amax reduction exceeds the GPU grid limit"); } if (first_stage) { reduce_abs_max_bf16<<< static_cast(blocks), kReduceThreads, 0, stream>>>(source, current_destination, current_count); } else { reduce_max_float<<< static_cast(blocks), kReduceThreads, 0, stream>>>(current_source, current_destination, current_count); } CUDA_CHECK(cudaPeekAtLastError()); if (blocks == 1) { if (current_destination != destination_amax) { CUDA_CHECK(cudaMemcpyAsync( destination_amax, current_destination, sizeof(float), cudaMemcpyDeviceToDevice, stream)); } return; } current_count = blocks; current_source = current_destination; current_destination = current_destination == scratch_a ? scratch_b : scratch_a; first_stage = false; } } struct DeviceAllocation { void* pointer = nullptr; size_t bytes = 0; ~DeviceAllocation() { if (pointer != nullptr) { cudaFree(pointer); } } DeviceAllocation() = default; DeviceAllocation(const DeviceAllocation&) = delete; DeviceAllocation& operator=(const DeviceAllocation&) = delete; }; struct Context { int device = -1; int max_grid_x = 0; cublasLtHandle_t handle = nullptr; DeviceAllocation x_fp4; DeviceAllocation x_scales; DeviceAllocation reduce_a; DeviceAllocation reduce_b; DeviceAllocation activation_amax; DeviceAllocation activation_scale; DeviceAllocation fp4_alpha; DeviceAllocation fp4_beta; DeviceAllocation workspace; uintptr_t bound_stream = 0; bool stream_bound = false; std::mutex mutex; ~Context() { if (handle != nullptr) { cublasLtDestroy(handle); } } }; void allocate_exact(DeviceAllocation* allocation, size_t bytes) { if (allocation->pointer != nullptr) { CUDA_CHECK(cudaFree(allocation->pointer)); allocation->pointer = nullptr; allocation->bytes = 0; } if (bytes != 0) { CUDA_CHECK(cudaMalloc(&allocation->pointer, bytes)); allocation->bytes = bytes; } } void ensure_capacity( Context* context, DeviceAllocation* allocation, size_t bytes, cudaStream_t stream) { if (allocation->bytes >= bytes) { return; } // A growth invalidates scratch pointers. Synchronize the one bound stream // before freeing; steady-state forwards do not synchronize. if (context->stream_bound) { CUDA_CHECK(cudaStreamSynchronize(stream)); } allocate_exact(allocation, bytes); } void set_scale_mode( cublasLtMatmulDesc_t operation, cublasLtMatmulDescAttributes_t attribute) { const int32_t mode = CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3; CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute( operation, attribute, &mode, sizeof(mode))); } void set_pointer_attribute( cublasLtMatmulDesc_t operation, cublasLtMatmulDescAttributes_t attribute, const void* pointer) { CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute( operation, attribute, &pointer, sizeof(pointer))); } struct Descriptors { cublasLtMatmulDesc_t operation = nullptr; cublasLtMatrixLayout_t w = nullptr; cublasLtMatrixLayout_t x = nullptr; cublasLtMatrixLayout_t c = nullptr; cublasLtMatrixLayout_t d = nullptr; ~Descriptors() { if (d != nullptr) cublasLtMatrixLayoutDestroy(d); if (c != nullptr) cublasLtMatrixLayoutDestroy(c); if (x != nullptr) cublasLtMatrixLayoutDestroy(x); if (w != nullptr) cublasLtMatrixLayoutDestroy(w); if (operation != nullptr) cublasLtMatmulDescDestroy(operation); } }; cublasLtMatmulAlgo_t choose_algo( Context* context, const Descriptors& descriptors, size_t* workspace_bytes) { cublasLtMatmulPreference_t preference = nullptr; CUBLASLT_CHECK(cublasLtMatmulPreferenceCreate(&preference)); CUBLASLT_CHECK(cublasLtMatmulPreferenceSetAttribute( preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &kWorkspaceBytes, sizeof(kWorkspaceBytes))); cublasLtMatmulHeuristicResult_t candidates[16]{}; int returned = 0; const cublasStatus_t status = cublasLtMatmulAlgoGetHeuristic( context->handle, descriptors.operation, descriptors.w, descriptors.x, descriptors.c, descriptors.d, preference, 16, candidates, &returned); cublasLtMatmulPreferenceDestroy(preference); if (status != CUBLAS_STATUS_SUCCESS || returned == 0) { fail("cuBLASLt returned no NVFP4 heuristic"); } for (int index = 0; index < returned; ++index) { if (candidates[index].state == CUBLAS_STATUS_SUCCESS) { *workspace_bytes = candidates[index].workspaceSize; return candidates[index].algo; } } fail("all cuBLASLt NVFP4 heuristics were unsupported"); } void validate_device_pointer( const void* pointer, int expected_device, const char* label) { if (pointer == nullptr) { fail(std::string(label) + " is null"); } cudaPointerAttributes attributes{}; CUDA_CHECK(cudaPointerGetAttributes(&attributes, pointer)); if (attributes.type != cudaMemoryTypeDevice || attributes.device != expected_device) { fail(std::string(label) + " is not a CUDA allocation on the context device"); } if ((reinterpret_cast(pointer) & 0x0fU) != 0) { fail(std::string(label) + " is not at least 16-byte aligned"); } } void forward_impl( Context* context, const void* input_bf16, const void* packed_weight, size_t packed_weight_bytes, const void* packed_weight_scales, size_t packed_weight_scale_bytes, const void* weight_tensor_scale_f32, const void* bias_bf16, void* output_bf16, int logical_m, int k, int n, uintptr_t stream_value) { if (logical_m <= 0 || k <= 0 || n <= 0) { fail("M, K, and N must be positive"); } const size_t expected_weight_bytes = packed_weight_bytes_checked(n, k); const size_t expected_scale_bytes = make_scale_layout(k, n).bytes; if (packed_weight_bytes != expected_weight_bytes || packed_weight_scale_bytes != expected_scale_bytes) { fail("packed weight or scale buffer has the wrong byte size"); } CUDA_CHECK(cudaSetDevice(context->device)); cudaStream_t stream = reinterpret_cast(stream_value); if (!context->stream_bound) { context->bound_stream = stream_value; context->stream_bound = true; } else if (context->bound_stream != stream_value) { fail("resident NVFP4 context is bound to a different CUDA stream"); } cudaStreamCaptureStatus capture_status = cudaStreamCaptureStatusNone; CUDA_CHECK(cudaStreamIsCapturing(stream, &capture_status)); if (capture_status != cudaStreamCaptureStatusNone) { fail("resident ctypes prototype does not support CUDA graph capture"); } validate_device_pointer(input_bf16, context->device, "input"); validate_device_pointer(packed_weight, context->device, "packed weight"); validate_device_pointer( packed_weight_scales, context->device, "weight scales"); validate_device_pointer( weight_tensor_scale_f32, context->device, "weight tensor scale"); validate_device_pointer(output_bf16, context->device, "output"); if (bias_bf16 != nullptr) { validate_device_pointer(bias_bf16, context->device, "bias"); } const int padded_m = round_up(logical_m, 8); const size_t input_elements = checked_multiply( static_cast(logical_m), static_cast(k), "input"); const size_t padded_input_elements = checked_multiply( static_cast(padded_m), static_cast(k), "padded input"); const size_t output_elements = checked_multiply( static_cast(padded_m), static_cast(n), "output"); const ScaleLayout x_scale_layout = make_scale_layout(k, padded_m); const size_t reduce_elements = reduction_scratch_elements(input_elements); ensure_capacity( context, &context->x_fp4, padded_input_elements / 2, stream); ensure_capacity( context, &context->x_scales, x_scale_layout.bytes, stream); ensure_capacity( context, &context->reduce_a, checked_multiply(reduce_elements, sizeof(float), "reduction"), stream); ensure_capacity( context, &context->reduce_b, checked_multiply(reduce_elements, sizeof(float), "reduction"), stream); // Clear the complete tiled scale allocation, including padding that the // logical quantizer does not address. CUDA_CHECK(cudaMemsetAsync( context->x_scales.pointer, 0, x_scale_layout.bytes, stream)); enqueue_activation_amax_reduce( static_cast(input_bf16), input_elements, static_cast(context->reduce_a.pointer), static_cast(context->reduce_b.pointer), static_cast(context->activation_amax.pointer), context->max_grid_x, stream); finalize_activation_scale<<<1, 1, 0, stream>>>( static_cast(context->activation_amax.pointer), static_cast(weight_tensor_scale_f32), static_cast(context->activation_scale.pointer), static_cast(context->fp4_alpha.pointer)); CUDA_CHECK(cudaPeekAtLastError()); const uint64_t padded_blocks = static_cast(padded_m) * static_cast(k / kFp4BlockElements); const uint64_t cuda_blocks = (padded_blocks + kWarpsPerQuantBlock - 1) / kWarpsPerQuantBlock; if (cuda_blocks == 0 || cuda_blocks > static_cast(context->max_grid_x)) { fail("activation quantizer exceeds the GPU grid limit"); } dynamic_quantize_activation<<< static_cast(cuda_blocks), kQuantThreads, 0, stream>>>( static_cast(input_bf16), static_cast(context->x_fp4.pointer), static_cast(context->x_scales.pointer), static_cast(context->activation_scale.pointer), k, logical_m, x_scale_layout.inner_dim, padded_blocks); CUDA_CHECK(cudaPeekAtLastError()); Descriptors descriptors; CUBLASLT_CHECK(cublasLtMatmulDescCreate( &descriptors.operation, CUBLAS_COMPUTE_32F, CUDA_R_32F)); const cublasOperation_t transpose_a = CUBLAS_OP_T; const cublasOperation_t transpose_b = CUBLAS_OP_N; CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute( descriptors.operation, CUBLASLT_MATMUL_DESC_TRANSA, &transpose_a, sizeof(transpose_a))); CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute( descriptors.operation, CUBLASLT_MATMUL_DESC_TRANSB, &transpose_b, sizeof(transpose_b))); const cublasLtPointerMode_t pointer_mode = CUBLASLT_POINTER_MODE_DEVICE; CUBLASLT_CHECK(cublasLtMatmulDescSetAttribute( descriptors.operation, CUBLASLT_MATMUL_DESC_POINTER_MODE, &pointer_mode, sizeof(pointer_mode))); set_scale_mode( descriptors.operation, CUBLASLT_MATMUL_DESC_A_SCALE_MODE); set_scale_mode( descriptors.operation, CUBLASLT_MATMUL_DESC_B_SCALE_MODE); // Refresh pointer attributes for every call. Module buffers may differ // between adjacent linears even though the shape is identical. set_pointer_attribute( descriptors.operation, CUBLASLT_MATMUL_DESC_A_SCALE_POINTER, packed_weight_scales); set_pointer_attribute( descriptors.operation, CUBLASLT_MATMUL_DESC_B_SCALE_POINTER, context->x_scales.pointer); CUBLASLT_CHECK(cublasLtMatrixLayoutCreate( &descriptors.w, CUDA_R_4F_E2M1, k, n, k)); CUBLASLT_CHECK(cublasLtMatrixLayoutCreate( &descriptors.x, CUDA_R_4F_E2M1, k, padded_m, k)); CUBLASLT_CHECK(cublasLtMatrixLayoutCreate( &descriptors.c, CUDA_R_16BF, n, padded_m, n)); CUBLASLT_CHECK(cublasLtMatrixLayoutCreate( &descriptors.d, CUDA_R_16BF, n, padded_m, n)); size_t selected_workspace_bytes = 0; const cublasLtMatmulAlgo_t algorithm = choose_algo(context, descriptors, &selected_workspace_bytes); if (selected_workspace_bytes > context->workspace.bytes) { fail("selected cuBLASLt algorithm exceeds the context workspace"); } CUBLASLT_CHECK(cublasLtMatmul( context->handle, descriptors.operation, context->fp4_alpha.pointer, packed_weight, descriptors.w, context->x_fp4.pointer, descriptors.x, context->fp4_beta.pointer, output_bf16, descriptors.c, output_bf16, descriptors.d, &algorithm, context->workspace.pointer, selected_workspace_bytes, stream)); if (bias_bf16 != nullptr) { const size_t blocks = round_up_divide(output_elements, static_cast(kBiasThreads)); if (blocks > static_cast(context->max_grid_x)) { fail("bias kernel exceeds the GPU grid limit"); } add_bias_bf16<<< static_cast(blocks), kBiasThreads, 0, stream>>>( static_cast<__nv_bfloat16*>(output_bf16), static_cast(bias_bf16), output_elements, n); CUDA_CHECK(cudaPeekAtLastError()); } } template int guarded(Function&& function) { try { g_last_error.clear(); function(); return 0; } catch (const std::exception& error) { g_last_error = error.what(); return 1; } catch (...) { g_last_error = "unknown native NVFP4 error"; return 2; } } } // namespace extern "C" int mage_nvfp4_abi_version(void) { return kAbiVersion; } extern "C" const char* mage_nvfp4_last_error(void) { return g_last_error.c_str(); } extern "C" size_t mage_nvfp4_packed_weight_bytes(int n, int k) { try { g_last_error.clear(); return packed_weight_bytes_checked(n, k); } catch (const std::exception& error) { g_last_error = error.what(); return 0; } } extern "C" size_t mage_nvfp4_weight_scale_bytes(int n, int k) { try { g_last_error.clear(); packed_weight_bytes_checked(n, k); return make_scale_layout(k, n).bytes; } catch (const std::exception& error) { g_last_error = error.what(); return 0; } } extern "C" int mage_nvfp4_pack_weight_bf16( const void* weight_bf16, int n, int k, void* packed_weight, size_t packed_weight_capacity, void* packed_scales, size_t packed_scale_capacity, float* tensor_scale) { return guarded([&]() { if (weight_bf16 == nullptr || packed_weight == nullptr || packed_scales == nullptr || tensor_scale == nullptr) { fail("weight packer received a null pointer"); } const size_t required_weight = packed_weight_bytes_checked(n, k); const ScaleLayout scale_layout = make_scale_layout(k, n); if (packed_weight_capacity != required_weight || packed_scale_capacity != scale_layout.bytes) { fail("weight packer received an incorrectly sized destination"); } const auto* source = static_cast(weight_bf16); auto* destination = static_cast(packed_weight); auto* scales = static_cast(packed_scales); std::memset(destination, 0, required_weight); std::memset(scales, 0, scale_layout.bytes); const size_t elements = checked_multiply(static_cast(n), static_cast(k), "weight"); float global_amax = 0.0f; for (size_t index = 0; index < elements; ++index) { global_amax = std::max( global_amax, std::abs(__bfloat162float(source[index]))); } *tensor_scale = global_amax == 0.0f ? 1.0f : global_amax / kTensorScaleDenominator; const float inverse_tensor_scale = 1.0f / *tensor_scale; // nn.Linear weight [N,K] is physical column-major KxN. for (int column = 0; column < n; ++column) { for (int block = 0; block < k / kFp4BlockElements; ++block) { const int first_row = block * kFp4BlockElements; float block_amax = 0.0f; for (int lane = 0; lane < kFp4BlockElements; ++lane) { const size_t index = static_cast(first_row + lane) + static_cast(column) * k; block_amax = std::max( block_amax, std::abs(__bfloat162float(source[index]) * inverse_tensor_scale)); } const uint8_t scale_raw = __nv_cvt_float_to_fp8( block_amax / kFp4E2M1Max, __NV_SATFINITE, __NV_E4M3); const float rounded_scale = host_ue4m3_to_float(scale_raw); scales[host_scale_offset( column, block, scale_layout.inner_dim)] = scale_raw; const float inverse_scale = rounded_scale == 0.0f ? 0.0f : 1.0f / rounded_scale; for (int pair = 0; pair < kFp4BlockElements / 2; ++pair) { const int row0 = first_row + pair * 2; const size_t source_index = static_cast(row0) + static_cast(column) * k; const float value0 = __bfloat162float(source[source_index]) * inverse_tensor_scale; const float value1 = __bfloat162float(source[source_index + 1]) * inverse_tensor_scale; destination[source_index / 2] = __nv_cvt_float2_to_fp4x2( make_float2(value0 * inverse_scale, value1 * inverse_scale), __NV_E2M1, cudaRoundNearest); } } } }); } extern "C" int mage_nvfp4_create_context( int cuda_device, void** context) { return guarded([&]() { if (context == nullptr) { fail("context output pointer is null"); } *context = nullptr; int device_count = 0; CUDA_CHECK(cudaGetDeviceCount(&device_count)); if (cuda_device < 0 || cuda_device >= device_count) { fail("invalid CUDA device index"); } CUDA_CHECK(cudaSetDevice(cuda_device)); cudaDeviceProp properties{}; CUDA_CHECK(cudaGetDeviceProperties(&properties, cuda_device)); if (properties.major != 12 || properties.minor != 0) { fail("resident NVFP4 prototype requires sm_120"); } Context* created = new Context(); try { created->device = cuda_device; created->max_grid_x = properties.maxGridSize[0]; CUBLASLT_CHECK(cublasLtCreate(&created->handle)); allocate_exact(&created->activation_amax, sizeof(float)); allocate_exact(&created->activation_scale, sizeof(float)); allocate_exact(&created->fp4_alpha, sizeof(float)); allocate_exact(&created->fp4_beta, sizeof(float)); allocate_exact(&created->workspace, kWorkspaceBytes); const float zero = 0.0f; CUDA_CHECK(cudaMemcpy( created->fp4_beta.pointer, &zero, sizeof(zero), cudaMemcpyHostToDevice)); *context = created; } catch (...) { delete created; throw; } }); } extern "C" int mage_nvfp4_destroy_context(void* context) { return guarded([&]() { if (context == nullptr) { return; } auto* typed = static_cast(context); { std::lock_guard lock(typed->mutex); CUDA_CHECK(cudaSetDevice(typed->device)); if (typed->stream_bound) { CUDA_CHECK(cudaStreamSynchronize( reinterpret_cast(typed->bound_stream))); } } delete typed; }); } extern "C" size_t mage_nvfp4_context_reserved_bytes( const void* context) { if (context == nullptr) { return 0; } const auto* typed = static_cast(context); return typed->x_fp4.bytes + typed->x_scales.bytes + typed->reduce_a.bytes + typed->reduce_b.bytes + typed->activation_amax.bytes + typed->activation_scale.bytes + typed->fp4_alpha.bytes + typed->fp4_beta.bytes + typed->workspace.bytes; } extern "C" int mage_nvfp4_linear_forward( void* context, const void* input_bf16, const void* packed_weight, size_t packed_weight_bytes, const void* packed_weight_scales, size_t packed_weight_scale_bytes, const void* weight_tensor_scale_f32, const void* bias_bf16, void* output_bf16, int logical_m, int k, int n, uintptr_t stream) { return guarded([&]() { if (context == nullptr) { fail("context is null"); } auto* typed = static_cast(context); std::lock_guard lock(typed->mutex); forward_impl( typed, input_bf16, packed_weight, packed_weight_bytes, packed_weight_scales, packed_weight_scale_bytes, weight_tensor_scale_f32, bias_bf16, output_bf16, logical_m, k, n, stream); }); }