tock-mirror/support/tock_support.h

540 lines
17 KiB
C

/*
* C99/C++ common support code for Tock programs
* Copyright (C) 2007, 2008 University of Kent
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOCK_SUPPORT_H
#define TOCK_SUPPORT_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <math.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
//{{{ 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
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, 4, "invalid array slice from %d to %d (should be 0 <= i <= %d)", start, end, limit);
}
return start;
}
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, 3, "invalid array index %d (should be 0 <= i < %d)", i, limit);
}
return i;
}
static inline int occam_check_index_lower (int, const char *) occam_unused;
static inline int occam_check_index_lower (int i, const char *pos) {
if (i < 0) {
occam_stop (pos, 2, "invalid array index %d (should be 0 <= i)", i);
}
return i;
}
static inline int occam_check_index_upper (int, int, const char *) occam_unused;
static inline int occam_check_index_upper (int i, int limit, const char *pos) {
if (i >= limit) {
occam_stop (pos, 3, "invalid array index %d (should be 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, 3, "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, 4, "invalid value in conversion " format " (should be " format " <= i <= " format ")", n, lower, upper); \
} \
return n; \
}
// Some things taken from http://www.fefe.de/intof.html
#define __HALF_MAX_SIGNED(type) ((type)1 << (sizeof(type)*CHAR_BIT-2))
#define __MAX_SIGNED(type) (__HALF_MAX_SIGNED(type) - 1 + __HALF_MAX_SIGNED(type))
#define __MIN_SIGNED(type) (-1 - __MAX_SIGNED(type))
#define __MIN(type) ((type)-1 < 1?__MIN_SIGNED(type):(type)0)
#define __MAX(type) ((type)~__MIN(type))
#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) { \
if (((b<1)&&(__MIN(type)-b<=a)) || ((b>=1)&&(__MAX(type)-b>=a))) {return a + b;} \
else { occam_stop(pos, 3, "integer overflow when doing %d + %d", a, b); return 0; } \
}
#define MAKE_ADDF(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) { \
if (((b<1)&&(__MAX(type)+b>=a)) || ((b>=1)&&(__MIN(type)+b<=a))) {return a - b;} \
else { occam_stop(pos, 3, "integer overflow when doing %d - %d", a, b); } \
}
#define MAKE_SUBTRF(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 (const type a, const type b, const char *pos) { \
if (sizeof(type) < sizeof(long)) /*should be statically known*/ { \
const long r = (long)a * (long) b; \
if (r < (long)__MIN(type) || r > (long)__MAX(type)) { \
occam_stop(pos, 3, "integer overflow when doing %d * %d", a, b); \
} else { \
return (type)r; \
} \
} else { \
/* Taken from: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg326431.html */ \
const type r = a * b; \
if (b != 0 && r / b != a) { \
occam_stop(pos, 3, "integer overflow when doing %d * %d", a, b); \
} else { \
return r; \
} \
} \
}
#define MAKE_MULF(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, 1, "divide by zero"); \
} \
else if (b == -1 && a == __MIN(type)) /* only overflow I can think of */ { \
occam_stop (pos, 1, "overflow in division"); \
} else { return a / b; } \
}
#define MAKE_DIVF(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) { return a / b;}
#define MAKE_NEGATE(type) \
static inline type occam_negate_##type (type, const char *) occam_unused; \
static inline type occam_negate_##type (type a, const char *pos) { \
if (a == __MIN(type)) { \
occam_stop (pos, 1, "overflow in negation"); \
} else {return - a;} \
}
#define MAKE_NEGATEF(type) \
static inline type occam_negate_##type (type, const char *) occam_unused; \
static inline type occam_negate_##type (type a, const char *pos) { return - a; }
// 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, 1, "modulo by zero"); \
} else if (a == __MIN(type)) { \
return a % (b < 0 ? -b : b); \
} else 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, 1, "modulo by zero"); \
} \
type i = round (a / b); \
return a - (i * b); \
}
#define MAKE_SHIFT(utype, type) \
static inline type occam_lshift_##type (type, int, const char*) occam_unused; \
static inline type occam_lshift_##type (type a, int b, const char* pos) { \
if (b < 0 || b > (int)(sizeof(type) * CHAR_BIT)) { \
occam_stop (pos, 1, "left shift by negative value or value (strictly) greater than number of bits in type"); \
} else if (b == (int)(sizeof(type) * CHAR_BIT)) { \
return 0; \
} else { \
return (a << b); \
} \
} \
static inline type occam_rshift_##type (type, int, const char*) occam_unused; \
static inline type occam_rshift_##type (type a, int b, const char* pos) { \
if (b < 0 || b > (int)(sizeof(type) * CHAR_BIT)) { \
occam_stop (pos, 1, "right shift by negative value or value (strictly) greater than number of bits in type"); \
} else if (b == (int)(sizeof(type) * CHAR_BIT)) { \
return 0; \
} else { \
return (type)(((utype)a) >> b); \
} \
}
// The main purpose of these three - since they don't need to check for overflow -
// is to constrain the types of the results to prevent unexpected integer promotions
#define MAKE_PLUS(type) \
static inline type occam_plus_##type (type, type, const char *) occam_unused; \
static inline type occam_plus_##type (type a, type b, const char *pos) { \
return a + b; \
}
#define MAKE_MINUS(type) \
static inline type occam_minus_##type (type, type, const char *) occam_unused; \
static inline type occam_minus_##type (type a, type b, const char *pos) { \
return a - b; \
}
#define MAKE_TIMES(type) \
static inline type occam_times_##type (type, type, const char *) occam_unused; \
static inline type occam_times_##type (type a, type b, const char *pos) { \
return a * b; \
}
#define MAKE_ALL_SIGNED(type,flag,utype) \
MAKE_RANGE_CHECK(type,flag) \
MAKE_ADD(type) \
MAKE_SUBTR(type) \
MAKE_MUL(type) \
MAKE_DIV(type) \
MAKE_REM(type) \
MAKE_NEGATE(type) \
MAKE_SHIFT(utype, type) \
MAKE_PLUS(type) \
MAKE_MINUS(type) \
MAKE_TIMES(type)
//{{{ uint8_t
MAKE_RANGE_CHECK(uint8_t, "%d")
MAKE_ADD(uint8_t)
MAKE_SUBTR(uint8_t)
MAKE_MUL(uint8_t)
MAKE_DIV(uint8_t)
MAKE_SHIFT(uint8_t,uint8_t)
MAKE_PLUS(uint8_t)
MAKE_MINUS(uint8_t)
MAKE_TIMES(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, 1, "modulo by zero");
}
return a % b;
}
// we don't define negate for unsigned types
//}}}
//{{{ int8_t
MAKE_ALL_SIGNED(int8_t, "%d", uint8_t)
//}}}
//{{{ int16_t
MAKE_ALL_SIGNED(int16_t, "%d", uint16_t)
//}}}
//{{{ int
MAKE_ALL_SIGNED(int, "%d", unsigned int)
//}}}
//{{{ int32_t
MAKE_ALL_SIGNED(int32_t, "%d", uint32_t)
//}}}
//{{{ int64_t
MAKE_ALL_SIGNED(int64_t, "%lld", uint64_t)
//}}}
// FIXME range checks for float and double shouldn't work this way
//{{{ float
MAKE_RANGE_CHECK(float, "%f")
MAKE_ADDF(float)
MAKE_SUBTRF(float)
MAKE_MULF(float)
MAKE_DIVF(float)
MAKE_NEGATEF(float)
MAKE_DUMB_REM(float)
//}}}
//{{{ double
MAKE_RANGE_CHECK(double, "%f")
MAKE_ADDF(double)
MAKE_SUBTRF(double)
MAKE_MULF(double)
MAKE_DIVF(double)
MAKE_NEGATEF(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);
}
//We use #define so we can #undef afterwards
#if occam_INT_size == 4
#define INT int32_t
#define UINT uint32_t
#elif occam_INT_size == 8
#define INT int64_t
#define UINT uint64_t
#else
#error You must define occam_INT_size when using this header
#endif
//Note that in the occam 2 manual, there is an error on page 104.
//They say that range is the number of storable values in INTEGER,
//their conceptual infinite type, so range would be infinity!
//However, it is clear from the following lines that range is
//the number of storable values in INT.
#define occam_unsign(x) ((UINT)(x))
#define occam_sign(x) ((INT)(x))
static inline INT occam_LONGADD (INT, INT, INT, const char *) occam_unused;
static inline INT occam_LONGADD (INT left, INT right, INT carry_in, const char *pos) {
if (left == __MAX(INT)) {
if (right == __MAX(INT)) {
occam_stop(pos, 3, "Overflow in LONGADD: %d + %d", left, right);
} else right += carry_in & 1;
} else left += carry_in & 1;
if (((right<1)&&(__MIN(INT)-right<=left)) || ((right>=1)&&(__MAX(INT)-right>=left))) {
return left + right;
} else {
occam_stop(pos, 3, "Overflow in LONGADD: %d + %d", left, right);
}
}
static inline INT occam_LONGSUM (INT, INT, INT, INT*, const char *) occam_unused;
static inline INT occam_LONGSUM (INT left, INT right, INT carry_in, INT* result1, const char *pos) {
const UINT leftu = occam_unsign(left);
const UINT rightu = occam_unsign(right);
if (leftu == __MAX(UINT)) {
if (rightu == __MAX(UINT)) {
*result1 = -2;
return 1;
} else rightu += carry_in & 1;
} else leftu += carry_in & 1;
if (__MAX(UINT)-rightu>=leftu) {
*result1 = occam_sign(leftu + rightu);
return 0;
} else {
*result1 = occam_sign(leftu + rightu);
return 1;
}
}
static inline INT occam_NORMALISE (INT, INT, INT*, INT*,const char *) occam_unused;
static inline INT occam_NORMALISE (INT hi_in, INT lo_in, INT* result1, INT* result2, const char *pos) {
if (hi_in == 0 && lo_in == 0) {
*result1 = *result2 = 0;
return 2*CHAR_BIT*sizeof(INT);
} else {
const INT highest_bit = __MIN(INT);
INT hi = hi_in;
INT lo = lo_in;
INT places = 0;
while ((hi & highest_bit) == 0) {
hi <<= 1;
hi |= (lo & highest_bit) >> ((CHAR_BIT*sizeof(INT))-1);
lo <<= 1;
places++;
}
*result1 = hi;
*result2 = lo;
return places;
}
}
#undef INT
#undef UINT
//}}}
//{{{ Terminal handling
static bool tock_uses_tty;
static struct termios tock_saved_termios;
static void tock_restore_terminal () occam_unused;
static void tock_restore_terminal ()
{
//{{{ restore terminal
if (tock_uses_tty) {
if (tcsetattr (0, TCSAFLUSH, &tock_saved_termios) != 0) {
fprintf (stderr, "Tock: tcsetattr failed\n");
exit (1);
}
tock_uses_tty = false;
}
//}}}
}
static void tock_configure_terminal (bool) occam_unused;
static void tock_configure_terminal (bool uses_stdin)
{
//{{{ configure terminal
tock_uses_tty = uses_stdin && isatty (0);
if (tock_uses_tty) {
struct termios term;
if (tcgetattr (0, &term) != 0) {
fprintf (stderr, "Tock: tcgetattr failed\n");
exit (1);
}
tock_saved_termios = term;
// Disable canonicalised input and echoing.
term.c_lflag &= ~(ICANON | ECHO);
// Satisfy a read request when one character is available.
term.c_cc[VMIN] = 1;
// Block read requests until VMIN characters are available.
term.c_cc[VTIME] = 0;
if (tcsetattr (0, TCSANOW, &term) != 0) {
fprintf (stderr, "Tock: tcsetattr failed\n");
exit (1);
}
}
//}}}
}
#endif