Loading...
/*
 * Copyright (c) 2024 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */
#ifndef _FUNCTION_VARIANT_MACROS_H_
#define _FUNCTION_VARIANT_MACROS_H_

#include <stddef.h>
#include <stdint.h>
#include <os/base.h>    // for OS_STRINGIFY

/*
 * These macros allow you to define function-variants without any compiler support.
 * A function-variant is when you have multiple implementation of the "same" function
 * each optimized for different runtime environments (such as processor kind or
 * security settings, etc).
 *
 * For instance, if you have three assembly implementation of strcpy() optimized for
 * different x86_64 processors, the variant table could be:
 *
 * FUNCTION_VARIANT_TABLE_EXPORTED(strcpy,
 *      { strcpy$Rosetta,  "rosetta" },
 *      { strcpy$Haswell,  "haswell" },
 *      { strcpy$Base,     "default" } );
 *
 * The first field in each line is the symbol name of a particular implementation function.
 * The convention is to name the variants starting with the generic name followed by something
 * identifying the variant. The second field is a condition string of when that matching
 * implementation function may be used.
 *
 * The order of rows is important. At runtime, each row will be evaluated in order (top to bottom).
 * The first row where the condition string is true will be used. Therefore, it is important to sort
 * the rows to match the way you want to prioritize implementations.  The last row must always
 * be "default" and its implementation must work in all environments.
 *
 * The condition strings may use "+" to specify multiple conditions that all must be true.
 * For example "foo+bar" means both "foo" and "bar" must evaluate to true for the implememtation
 * to be used.  There can be at most four conditions (three plus signs).
 *
 * There are four namespaces for conditions strings: arm64, x86_64, system-wide, per-process.
 * All condition strings in a table must be in the same namespace.
 *
 */


struct FunctionVariantTableEntry
{
    const void*                         func;
    __attribute__((aligned(8))) char    condition[56];  // alignment keeps layout same for 32-bit and 64-bit archs
};

#ifdef __cplusplus
static_assert(offsetof(FunctionVariantTableEntry, condition) == 8, "conditional field should be 8-bytes into struct");
static_assert(sizeof(FunctionVariantTableEntry) == 64, "struct should be 64-bytes for all arches");
#endif

/*
 * FUNCTION_VARIANT_TABLE_EXPORTED() for use in dylibs when the function-variant symbol will be exported.
 */
 #define FUNCTION_VARIANT_TABLE_EXPORTED(_name, ...) \
    extern const struct FunctionVariantTableEntry OS_CONCAT(fvtemp_, _name)[] __asm("_" OS_STRINGIFY(_name)); \
    __attribute__((section("__LD,__func_variants")))  \
    const struct FunctionVariantTableEntry OS_CONCAT(fvtemp_, _name)[] = { \
    __VA_ARGS__  \
};

/*
 * FUNCTION_VARIANT_TABLE() for use when the function-variant is for internal use (not exported).
 */
#define FUNCTION_VARIANT_TABLE(_name, ...) \
    extern const struct FunctionVariantTableEntry OS_CONCAT(fvtemp_, _name)[] __asm("_" OS_STRINGIFY(_name)); \
    __attribute__((visibility("hidden")))  \
    __attribute__((section("__LD,__func_variants")))  \
    const struct FunctionVariantTableEntry OS_CONCAT(fvtemp_, _name)[] = { \
    __VA_ARGS__  \
};



#endif // _FUNCTION_VARIANT_MACROS_H_