// C99 support definitions for Tock. // vim:set foldmethod=marker: #ifndef TOCK_SUPPORT_H #define TOCK_SUPPORT_H #include #include #include #include #include #include #include //For C++CSP: #ifndef NO_CIFCCSP #include #else #define EXTERNAL_CALLN(F,I,args...) (F)((I),##args) #define EXTERNAL_CALL(F) (F)() #define SetErr() #endif //{{{ mostneg/mostpos #define occam_mostneg_bool false #define occam_mostpos_bool true #define occam_mostneg_uint8_t 0 #define occam_mostpos_uint8_t UINT8_MAX #define occam_mostneg_int INT_MIN #define occam_mostpos_int INT_MAX #define occam_mostneg_int16_t INT16_MIN #define occam_mostpos_int16_t INT16_MAX #define occam_mostneg_int32_t INT32_MIN #define occam_mostpos_int32_t INT32_MAX #define occam_mostneg_int64_t INT64_MIN #define occam_mostpos_int64_t INT64_MAX #define occam_mostneg_float -FLT_MAX #define occam_mostpos_float FLT_MAX #define occam_mostneg_double -DBL_MAX #define occam_mostpos_double DBL_MAX //}}} //{{{ compiler-specific attributes #ifdef __GNUC__ #define occam_struct_packed __attribute__ ((packed)) #define occam_unused __attribute__ ((unused)) #else #warning No PACKED (or other compiler specials) implementation for this compiler #define occam_struct_packed #define occam_unused #endif //}}} //{{{ runtime check functions //C++CSP may have defined this function already: #ifndef occam_stop #define occam_stop(pos, format, args...) \ do { \ EXTERNAL_CALLN (fprintf, stderr, "Program stopped at %s: " format "\n", pos, ##args); \ SetErr (); \ } while (0) #endif //occam_stop static inline int occam_check_slice (int, int, int, const char *) occam_unused; static inline int occam_check_slice (int start, int count, int limit, const char *pos) { int end = start + count; if (count != 0 && (start < 0 || start >= limit || end < 0 || end > limit || count < 0)) { occam_stop (pos, "invalid array slice from %d to %d (should be 0 <= i <= %d)", start, end, limit); } return count; } static inline int occam_check_index (int, int, const char *) occam_unused; static inline int occam_check_index (int i, int limit, const char *pos) { if (i < 0 || i >= limit) { occam_stop (pos, "invalid array index %d (should be 0 <= i < %d)", i, limit); } return i; } static inline int occam_check_retype (int, int, const char *) occam_unused; static inline int occam_check_retype (int src, int dest, const char *pos) { if (src % dest != 0) { occam_stop (pos, "invalid size for RETYPES/RESHAPES (%d does not divide into %d)", dest, src); } return src / dest; } //}}} //{{{ type-specific arithmetic ops and runtime checks #define MAKE_RANGE_CHECK(type, format) \ static inline type occam_range_check_##type (type, type, type, const char *) occam_unused; \ static inline type occam_range_check_##type (type lower, type upper, type n, const char *pos) { \ if (n < lower || n > upper) { \ occam_stop (pos, "invalid value in conversion " format " (should be " format " <= i <= " format ")", n, lower, upper); \ } \ return n; \ } // FIXME All of these need to check for overflow and report errors appropriately. #define MAKE_ADD(type) \ static inline type occam_add_##type (type, type, const char *) occam_unused; \ static inline type occam_add_##type (type a, type b, const char *pos) { \ return a + b; \ } #define MAKE_SUBTR(type) \ static inline type occam_subtr_##type (type, type, const char *) occam_unused; \ static inline type occam_subtr_##type (type a, type b, const char *pos) { \ return a - b; \ } #define MAKE_MUL(type) \ static inline type occam_mul_##type (type, type, const char *) occam_unused; \ static inline type occam_mul_##type (type a, type b, const char *pos) { \ return a * b; \ } #define MAKE_DIV(type) \ static inline type occam_div_##type (type, type, const char *) occam_unused; \ static inline type occam_div_##type (type a, type b, const char *pos) { \ if (b == 0) { \ occam_stop (pos, "divide by zero"); \ } \ return a / b; \ } // occam's \ doesn't behave like C's %; it handles negative arguments. // (Effectively it ignores signs coming in, and the output sign is the sign of // the first argument.) #define MAKE_REM(type) \ static inline type occam_rem_##type (type, type, const char *) occam_unused; \ static inline type occam_rem_##type (type a, type b, const char *pos) { \ if (b == 0) { \ occam_stop (pos, "modulo by zero"); \ } \ if (a < 0) { \ return -((-a) % (b < 0 ? -b : b)); \ } else { \ return a % (b < 0 ? -b : b); \ } \ } // This is for types that C doesn't implement % for -- i.e. reals. // (The cgtests want to do \ with REAL32 and REAL64, although I've never seen it // in a real program.) #define MAKE_DUMB_REM(type) \ static inline type occam_rem_##type (type, type, const char *) occam_unused; \ static inline type occam_rem_##type (type a, type b, const char *pos) { \ if (b == 0) { \ occam_stop (pos, "modulo by zero"); \ } \ type i = trunc (a / b); \ return a - (i * b); \ } //{{{ uint8_t MAKE_RANGE_CHECK(uint8_t, "%d") MAKE_ADD(uint8_t) MAKE_SUBTR(uint8_t) MAKE_MUL(uint8_t) MAKE_DIV(uint8_t) // occam's only unsigned type, so we can use % directly. static inline uint8_t occam_rem_uint8_t (uint8_t, uint8_t, const char *) occam_unused; static inline uint8_t occam_rem_uint8_t (uint8_t a, uint8_t b, const char *pos) { if (b == 0) { occam_stop (pos, "modulo by zero"); } return a % b; } //}}} //{{{ int16_t MAKE_RANGE_CHECK(int16_t, "%d") MAKE_ADD(int16_t) MAKE_SUBTR(int16_t) MAKE_MUL(int16_t) MAKE_DIV(int16_t) MAKE_REM(int16_t) //}}} //{{{ int MAKE_RANGE_CHECK(int, "%d") MAKE_ADD(int) MAKE_SUBTR(int) MAKE_MUL(int) MAKE_DIV(int) MAKE_REM(int) //}}} //{{{ int32_t MAKE_RANGE_CHECK(int32_t, "%d") MAKE_ADD(int32_t) MAKE_SUBTR(int32_t) MAKE_MUL(int32_t) MAKE_DIV(int32_t) MAKE_REM(int32_t) //}}} //{{{ int64_t MAKE_RANGE_CHECK(int64_t, "%lld") MAKE_ADD(int64_t) MAKE_SUBTR(int64_t) MAKE_MUL(int64_t) MAKE_DIV(int64_t) MAKE_REM(int64_t) //}}} // FIXME range checks for float and double shouldn't work this way //{{{ float MAKE_RANGE_CHECK(float, "%d") MAKE_ADD(float) MAKE_SUBTR(float) MAKE_MUL(float) MAKE_DIV(float) MAKE_DUMB_REM(float) //}}} //{{{ double MAKE_RANGE_CHECK(double, "%d") MAKE_ADD(double) MAKE_SUBTR(double) MAKE_MUL(double) MAKE_DIV(double) MAKE_DUMB_REM(double) //}}} #undef MAKE_RANGE_CHECK #undef MAKE_ADD #undef MAKE_SUBTR #undef MAKE_MUL #undef MAKE_DIV #undef MAKE_REM //}}} //{{{ conversions to and from reals // FIXME: Again, all these should check. //{{{ float float occam_convert_int64_t_float_round (int64_t, const char *) occam_unused; float occam_convert_int64_t_float_round (int64_t v, const char *pos) { return (float) v; } float occam_convert_int64_t_float_trunc (int64_t, const char *) occam_unused; float occam_convert_int64_t_float_trunc (int64_t v, const char *pos) { return (float) v; } int64_t occam_convert_float_int64_t_round (float, const char *) occam_unused; int64_t occam_convert_float_int64_t_round (float v, const char *pos) { return (int64_t) roundf (v); } int64_t occam_convert_float_int64_t_trunc (float, const char *) occam_unused; int64_t occam_convert_float_int64_t_trunc (float v, const char *pos) { return (int64_t) truncf (v); } float occam_convert_double_float_round (double, const char *) occam_unused; float occam_convert_double_float_round (double v, const char *pos) { return (float) v; } float occam_convert_double_float_trunc (double, const char *) occam_unused; float occam_convert_double_float_trunc (double v, const char *pos) { return (float) v; } //}}} //{{{ double double occam_convert_int64_t_double_round (int64_t, const char *) occam_unused; double occam_convert_int64_t_double_round (int64_t v, const char *pos) { return (double) v; } double occam_convert_int64_t_double_trunc (int64_t, const char *) occam_unused; double occam_convert_int64_t_double_trunc (int64_t v, const char *pos) { return (double) v; } int64_t occam_convert_double_int64_t_round (double, const char *) occam_unused; int64_t occam_convert_double_int64_t_round (double v, const char *pos) { return (int64_t) round (v); } int64_t occam_convert_double_int64_t_trunc (double, const char *) occam_unused; int64_t occam_convert_double_int64_t_trunc (double v, const char *pos) { return (int64_t) trunc (v); } //}}} //}}} //{{{ intrinsics // FIXME These should do range checks. static inline float occam_SQRT (float, const char *) occam_unused; static inline float occam_SQRT (float v, const char *pos) { return sqrtf (v); } static inline double occam_DSQRT (double, const char *) occam_unused; static inline double occam_DSQRT (double v, const char *pos) { return sqrt (v); } //}}} #endif