#include static int define_utf8( mcpu_macro_table *table, const char *name, const char *replacement ) { mcpu_text n; mcpu_text r; int rc = -1; mcpu_text_init( &n ); mcpu_text_init( &r ); if( mcpu_text_from_utf8(&n, name) != 0 || mcpu_text_from_utf8(&r, replacement ? replacement : "") != 0 ) goto done; rc = mcpu_macro_define_object( table, n.data, n.length, r.data, r.length, MCPU_MACRO_ORIGIN_BUILTIN ); done: mcpu_text_free( &n ); mcpu_text_free( &r ); return( rc ); } static int define_unsigned( mcpu_macro_table *table, const char *name, unsigned long value ) { char text[64]; snprintf( text, sizeof(text), "%lu", value ); return( define_utf8(table, name, text) ); } static int define_quoted( mcpu_macro_table *table, const char *name, const char *value ) { size_t n; char *text; size_t i; size_t o = 0; int rc; if( value == NULL ) value = ""; n = strlen( value ); if( n > (SIZE_MAX - 3) / 2 ) { errno = EOVERFLOW; return( -1 ); } text = (char *)malloc( n * 2 + 3 ); if( text == NULL ) return( -1 ); text[o++] = '"'; for( i = 0; i < n; ++i ) { if( value[i] == '\\' || value[i] == '"' ) text[o++] = '\\'; text[o++] = value[i]; } text[o++] = '"'; text[o] = 0; rc = define_utf8( table, name, text ); free( text ); return( rc ); } static int define_from_macro( mcpu_macro_table *table, const char *name, const char *source_name ) { mcpu_text source; mcpu_text target; mcpu_macro *macro; int rc = -1; mcpu_text_init( &source ); mcpu_text_init( &target ); if( mcpu_text_from_utf8(&source, source_name) != 0 || mcpu_text_from_utf8(&target, name) != 0 ) goto done; macro = mcpu_macro_find( table, source.data, source.length ); if( macro == NULL || macro->builtin != MCPU_MACRO_BUILTIN_NONE ) { errno = EINVAL; goto done; } rc = mcpu_macro_define_object( table, target.data, target.length, macro->replacement, macro->replacement_length, MCPU_MACRO_ORIGIN_BUILTIN ); done: mcpu_text_free( &source ); mcpu_text_free( &target ); return( rc ); } static int define_sized_builtin_utf8( mcpu_macro_table *table, const char *name, enum mcpu_macro_builtin builtin, unsigned bits ) { mcpu_text n; int rc; mcpu_text_init( &n ); if( mcpu_text_from_utf8(&n, name) != 0 ) return( -1 ); rc = mcpu_macro_define_sized_builtin( table, n.data, n.length, builtin, bits ); mcpu_text_free( &n ); return( rc ); } static unsigned long mcpp_uint_decimal_digs( unsigned bits ) { uint64_t n; if( bits < 1 ) return( 0 ); /* * Number of decimal digits in 2^bits - 1. This follows the integer-only * scheme used by LibMPU _int_digs(), but does not include a terminating * NUL. The tighter upper approximation keeps the result exact for every * integer width supported by the current LibMPU family through 65536 bits. * * log10(2) < 301029995664 / 1000000000000 */ n = ((uint64_t)bits * UINT64_C(301029995664) + UINT64_C(999999999999)) / UINT64_C(1000000000000); return( (unsigned long)n ); } static unsigned long mcpp_int_decimal_digs( unsigned bits ) { if( bits < 2 ) return( 0 ); /* Signed maximum is 2^(bits - 1) - 1; sign is not part of DECIMAL_DIG. */ return( mcpp_uint_decimal_digs(bits - 1) ); } static int define_fixed_integer_family( mcpu_macro_table *table, unsigned bits ) { size_t nb = (size_t)bits / 8; mpu_int *value = NULL; char *text = NULL; char name[96]; size_t text_size; unsigned long int_digs; unsigned long uint_digs; int rc = -1; if( bits == 0 || (bits % 8) != 0 ) { errno = EINVAL; return( -1 ); } snprintf( name, sizeof(name), "__INT%u_TYPE__", bits ); { char type[64]; snprintf( type, sizeof(type), "int%u", bits ); if( define_utf8(table, name, type) != 0 ) return( -1 ); } snprintf( name, sizeof(name), "__UINT%u_TYPE__", bits ); { char type[64]; snprintf( type, sizeof(type), "uint%u", bits ); if( define_utf8(table, name, type) != 0 ) return( -1 ); } snprintf( name, sizeof(name), "__INT%u_WIDTH__", bits ); if( define_unsigned(table, name, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__UINT%u_WIDTH__", bits ); if( define_unsigned(table, name, bits) != 0 ) return( -1 ); /* * TYPE/WIDTH/SIZEOF and decimal-digit metadata remain available for the * complete LibMPU integer family through NB_I_MAX * 8. Only textual MAX * values are capped at 256 bits so -dMP stays compact and useful. */ snprintf( name, sizeof(name), "__SIZEOF_INT%u__", bits ); if( define_unsigned(table, name, bits / 8) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__SIZEOF_UINT%u__", bits ); if( define_unsigned(table, name, bits / 8) != 0 ) return( -1 ); int_digs = mcpp_int_decimal_digs( bits ); uint_digs = mcpp_uint_decimal_digs( bits ); if( int_digs == 0 || uint_digs == 0 ) { errno = EINVAL; return( -1 ); } snprintf( name, sizeof(name), "__INT%u_DECIMAL_DIG__", bits ); if( define_unsigned(table, name, int_digs) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__UINT%u_DECIMAL_DIG__", bits ); if( define_unsigned(table, name, uint_digs) != 0 ) return( -1 ); if( bits > 256 ) return( 0 ); value = (mpu_int *)malloc( nb ); if( value == NULL ) return( -1 ); text_size = (size_t)bits + 3; text = (char *)calloc( text_size, 1 ); if( text == NULL ) goto done; memset( value, 0xff, nb ); iuitoa( (__mpu_char8_t *)text, value, RADIX_HEX, LOWERCASE, (int)nb ); snprintf( name, sizeof(name), "__UINT%u_MAX__", bits ); if( define_utf8(table, name, text) != 0 ) goto done; /* * iuitoa() is the canonical LibMPU conversion. For the signed maximum * the all-ones unsigned bit pattern has only its top bit cleared. */ if( strlen(text) < 3 || text[0] != '0' || text[1] != 'x' ) { errno = EINVAL; goto done; } text[2] = '7'; snprintf( name, sizeof(name), "__INT%u_MAX__", bits ); if( define_utf8(table, name, text) != 0 ) goto done; rc = 0; done: free( text ); free( value ); return( rc ); } typedef void (*real_value_fn)( mpu_real *, unsigned int, int ); static int append_real_value( mcpu_text *output, unsigned bits, real_value_fn fn ) { int nb = (int)(bits / 8); int max_string; mpu_real *value; char *text; int rc = -1; max_string = _real_max_string( nb ); if( max_string <= 0 ) { errno = EINVAL; return( -1 ); } value = (mpu_real *)calloc( (size_t)nb, 1 ); text = (char *)calloc( (size_t)max_string + 4096, 1 ); if( value == NULL || text == NULL ) goto done; fn( value, 0, nb ); real_to_ascii( (__mpu_char8_t *)text, value, _real_mant_digs(nb), 'e', 0, 0, nb ); if( text[0] == 0 ) { errno = EINVAL; goto done; } rc = mcpu_text_append_ascii( output, text ); done: free( text ); free( value ); return( rc ); } static void real_epsilon_wrapper( mpu_real *value, unsigned int sign, int nb ) { (void)sign; _real_epsilon( value, nb ); } static int append_real_exponent( mcpu_text *output, unsigned bits, void (*fn)(mpu_int *, int, int) ) { int nb_r = (int)(bits / 8); int nb_e = _sizeof_exp( nb_r ); mpu_int *value; char *text; size_t text_size; int rc = -1; if( nb_e <= 0 ) { errno = EINVAL; return( -1 ); } value = (mpu_int *)calloc( (size_t)nb_e, 1 ); text_size = (size_t)nb_e * 8 + 3; text = (char *)calloc( text_size, 1 ); if( value == NULL || text == NULL ) goto done; fn( value, nb_e, nb_r ); iitoa( (__mpu_char8_t *)text, value, RADIX_DEC, LOWERCASE, nb_e ); if( text[0] == 0 ) { errno = EINVAL; goto done; } rc = mcpu_text_append_ascii( output, text ); done: free( text ); free( value ); return( rc ); } static int define_real_family( mcpu_macro_table *table, unsigned bits ) { char name[96]; char type[64]; int nb = (int)(bits / 8); /* * Real and Complex language types exist only through the configured * MPU_REAL_IO_LIMIT. Complex WIDTH is the width parameter in the language * type name (complex128 -> 128), while SIZEOF is twice the Real storage. */ if( bits > MCPU_CPP_MPU_REAL_IO_LIMIT ) return( 0 ); snprintf( name, sizeof(name), "__REAL%u_TYPE__", bits ); snprintf( type, sizeof(type), "real%u", bits ); if( define_utf8(table, name, type) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__COMPLEX%u_TYPE__", bits ); snprintf( type, sizeof(type), "complex%u", bits ); if( define_utf8(table, name, type) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_WIDTH__", bits ); if( define_unsigned(table, name, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__COMPLEX%u_WIDTH__", bits ); if( define_unsigned(table, name, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__SIZEOF_REAL%u__", bits ); if( define_unsigned(table, name, bits / 8) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__SIZEOF_COMPLEX%u__", bits ); if( define_unsigned(table, name, bits / 4) != 0 ) return( -1 ); /* * These are compact structural/text-conversion properties. They are * available for every configured Real type through MPU_REAL_IO_LIMIT. * _real_max_string() returns a character count, not a byte count. */ { int exp_size = _sizeof_exp( nb ); int max_strlen = _real_max_string( nb ); if( exp_size <= 0 || max_strlen <= 0 ) { errno = EINVAL; return( -1 ); } snprintf( name, sizeof(name), "__SIZEOF_REAL%u_EXP__", bits ); if( define_unsigned(table, name, (unsigned long)exp_size) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MAX_STRLEN__", bits ); if( define_unsigned(table, name, (unsigned long)max_strlen) != 0 ) return( -1 ); } /* * Precision metadata is compact and is useful for every configured Real * width. Only large textual numeric values are capped at 256 bits. */ snprintf( name, sizeof(name), "__REAL%u_MANT_DIG__", bits ); if( define_unsigned(table, name, (unsigned long)_real_mant_digs(nb)) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_DECIMAL_DIG__", bits ); if( define_unsigned(table, name, (unsigned long)_real_digs(nb)) != 0 ) return( -1 ); if( bits > 256 ) return( 0 ); snprintf( name, sizeof(name), "__REAL%u_MAX__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MAX, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MIN__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MIN, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_EPSILON__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_EPSILON, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MAX_EXP__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MAX_EXP, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MIN_EXP__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MIN_EXP, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MAX_10_EXP__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MAX_10_EXP, bits) != 0 ) return( -1 ); snprintf( name, sizeof(name), "__REAL%u_MIN_10_EXP__", bits ); if( define_sized_builtin_utf8(table, name, MCPU_MACRO_BUILTIN_REAL_MIN_10_EXP, bits) != 0 ) return( -1 ); return( 0 ); } int mcpu_predefined_expand_builtin( enum mcpu_macro_builtin builtin, unsigned bits, mcpu_text *output ) { if( output == NULL || bits == 0 || bits > 256 || bits > MCPU_CPP_MPU_REAL_IO_LIMIT ) { errno = EINVAL; return( -1 ); } switch( builtin ) { case MCPU_MACRO_BUILTIN_REAL_MAX: return( append_real_value(output, bits, _real_max) ); case MCPU_MACRO_BUILTIN_REAL_MIN: return( append_real_value(output, bits, _real_min) ); case MCPU_MACRO_BUILTIN_REAL_EPSILON: return( append_real_value(output, bits, real_epsilon_wrapper) ); case MCPU_MACRO_BUILTIN_REAL_MAX_EXP: return( append_real_exponent(output, bits, _real_max_exp) ); case MCPU_MACRO_BUILTIN_REAL_MIN_EXP: return( append_real_exponent(output, bits, _real_min_exp) ); case MCPU_MACRO_BUILTIN_REAL_MAX_10_EXP: return( append_real_exponent(output, bits, _real_max_10_exp) ); case MCPU_MACRO_BUILTIN_REAL_MIN_10_EXP: return( append_real_exponent(output, bits, _real_min_10_exp) ); default: errno = EINVAL; return( -1 ); } } int mcpu_predefined_install( mcpu_macro_table *table ) { unsigned bits; char type[64]; char ref[96]; if( table == NULL ) { errno = EINVAL; return( -1 ); } if( define_utf8(table, "_ARCH_MCPU", "1") != 0 || define_quoted(table, "__MCPU_CPP_VERSION__", PACKAGE_VERSION) != 0 || define_utf8(table, "__REGISTER_PREFIX__", "") != 0 || define_utf8(table, "__LOCAL_LABEL_PREFIX__", "") != 0 || define_utf8(table, "__USER_LABEL_PREFIX__", "") != 0 || define_utf8(table, "__IMMEDIATE_PREFIX__", "") != 0 || define_utf8(table, "__ORDER_LITTLE_ENDIAN__", "1234") != 0 || define_utf8(table, "__ORDER_BIG_ENDIAN__", "4321") != 0 || define_utf8(table, "__ORDER_PDP_ENDIAN__", "3412") != 0 ) return( -1 ); #if MCPU_CPP_MPU_BYTE_ORDER == 1234 if( define_utf8(table, "__MCPU_BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__") != 0 ) return( -1 ); #else if( define_utf8(table, "__MCPU_BYTE_ORDER__", "__ORDER_BIG_ENDIAN__") != 0 ) return( -1 ); #endif if( define_utf8(table, "__BYTE_ORDER__", "__MCPU_BYTE_ORDER__") != 0 ) return( -1 ); #if MCPU_CPP_MPU_WORD_ORDER == 1234 if( define_utf8(table, "__MCPU_WORD_ORDER__", "__ORDER_LITTLE_ENDIAN__") != 0 ) return( -1 ); #else if( define_utf8(table, "__MCPU_WORD_ORDER__", "__ORDER_BIG_ENDIAN__") != 0 ) return( -1 ); #endif if( define_unsigned(table, "__MCPU_MACHINE_REGISTER_WIDTH__", MCPU_CPP_MPU_REGISTER_WIDTH) != 0 || define_unsigned(table, "__MCPU_REAL_IO_LIMIT__", MCPU_CPP_MPU_REAL_IO_LIMIT) != 0 || define_unsigned(table, "__MCPU_MATH_FN_LIMIT__", MCPU_CPP_MPU_MATH_FN_LIMIT) != 0 || define_unsigned(table, "__MCPU_INT_MAX_WIDTH__", MCPU_CPP_MPU_INT_MAX_WIDTH) != 0 || define_unsigned(table, "__MCPU_REAL_MAX_WIDTH__", MCPU_CPP_MPU_REAL_IO_LIMIT) != 0 || define_unsigned(table, "__MCPU_COMPLEX_MAX_WIDTH__", MCPU_CPP_MPU_REAL_IO_LIMIT) != 0 || define_unsigned(table, "__SIZEOF_POINTER__", 8) != 0 || define_unsigned(table, "__MCPU_POINTER_WIDTH__", 64) != 0 || define_unsigned(table, "__MCPU_SIZEOF_SIZE__", MCPU_CPP_SIZEOF_SIZE_T) != 0 || define_unsigned(table, "__MCPU_SIZE_WIDTH__", MCPU_CPP_SIZE_WIDTH) != 0 || define_unsigned(table, "__SIZEOF_PTRDIFF__", 8) != 0 || define_unsigned(table, "__PTRDIFF_WIDTH__", 64) != 0 || define_utf8(table, "__CHAR8_TYPE__", "char8") != 0 || define_utf8(table, "__CHAR16_TYPE__", "char16") != 0 || define_unsigned(table, "__CHAR8_WIDTH__", 8) != 0 || define_unsigned(table, "__CHAR16_WIDTH__", 16) != 0 || define_unsigned(table, "__SIZEOF_CHAR8__", 1) != 0 || define_unsigned(table, "__SIZEOF_CHAR16__", 2) != 0 || define_utf8(table, "__CHAR8_MAX__", "0xff") != 0 || define_utf8(table, "__CHAR16_MAX__", "0xffff") != 0 ) return( -1 ); for( bits = 8; bits != 0 && bits <= MCPU_CPP_MPU_INT_MAX_WIDTH; bits *= 2 ) { if( define_fixed_integer_family(table, bits) != 0 ) return( -1 ); } snprintf( type, sizeof(type), "uint%u", (unsigned)MCPU_CPP_SIZE_WIDTH ); if( define_utf8(table, "__MCPU_SIZE_TYPE__", type) != 0 ) return( -1 ); snprintf( ref, sizeof(ref), "__UINT%u_MAX__", (unsigned)MCPU_CPP_SIZE_WIDTH ); if( define_from_macro(table, "__MCPU_SIZE_MAX__", ref) != 0 ) return( -1 ); snprintf( type, sizeof(type), "int%u", (unsigned)MCPU_CPP_SSIZE_WIDTH ); if( define_utf8(table, "__MCPU_SSIZE_TYPE__", type) != 0 || define_unsigned(table, "__MCPU_SSIZE_WIDTH__", MCPU_CPP_SSIZE_WIDTH) != 0 || define_unsigned(table, "__MCPU_SIZEOF_SSIZE__", MCPU_CPP_SIZEOF_SSIZE_T) != 0 ) return( -1 ); if( MCPU_CPP_SSIZE_WIDTH <= 256 ) { snprintf( ref, sizeof(ref), "__INT%u_MAX__", (unsigned)MCPU_CPP_SSIZE_WIDTH ); if( define_from_macro(table, "__MCPU_SSIZE_MAX__", ref) != 0 ) return( -1 ); } if( define_utf8(table, "__PTRDIFF_TYPE__", "int64") != 0 || define_unsigned(table, "__PTRDIFF_WIDTH__", 64) != 0 || define_utf8(table, "__PTRDIFF_MAX__", "0x7fffffffffffffff") != 0 || define_utf8(table, "__INTPTR_TYPE__", "int64") != 0 || define_utf8(table, "__UINTPTR_TYPE__", "uint64") != 0 || define_unsigned(table, "__INTPTR_WIDTH__", 64) != 0 || define_unsigned(table, "__UINTPTR_WIDTH__", 64) != 0 || define_utf8(table, "__INTPTR_MAX__", "0x7fffffffffffffff") != 0 || define_utf8(table, "__UINTPTR_MAX__", "0xffffffffffffffff") != 0 ) return( -1 ); for( bits = 32; bits != 0 && bits <= MCPU_CPP_MPU_REAL_IO_LIMIT; bits *= 2 ) { if( define_real_family(table, bits) != 0 ) return( -1 ); } return( 0 ); }