Files
epics-base/src/libCom/calc/calcPerform.c
2020-02-17 13:30:15 +00:00

520 lines
10 KiB
C

/*************************************************************************\
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Julie Sander and Bob Dalesio
* Date: 07-27-87
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define epicsExportSharedSymbols
#include "osiUnistd.h"
#include "dbDefs.h"
#include "epicsMath.h"
#include "epicsTypes.h"
#include "errlog.h"
#include "postfix.h"
#include "postfixPvt.h"
static double calcRandom(void);
static int cond_search(const char **ppinst, int match);
#ifndef PI
#define PI 3.14159265358979323
#endif
/* Turn off global optimization for 64-bit MSVC builds */
#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
# pragma optimize("g", off)
#endif
/* calcPerform
*
* Evalutate the postfix expression
*/
epicsShareFunc long
calcPerform(double *parg, double *presult, const char *pinst)
{
double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */
double *ptop; /* stack pointer */
double top; /* value from top of stack */
epicsInt32 itop; /* integer from top of stack */
int op;
int nargs;
/* initialize */
ptop = stack;
/* RPN evaluation loop */
while ((op = *pinst++) != END_EXPRESSION){
switch (op){
case LITERAL_DOUBLE:
memcpy(++ptop, pinst, sizeof(double));
pinst += sizeof(double);
break;
case LITERAL_INT:
memcpy(&itop, pinst, sizeof(epicsInt32));
*++ptop = itop;
pinst += sizeof(epicsInt32);
break;
case FETCH_VAL:
*++ptop = *presult;
break;
case FETCH_A:
case FETCH_B:
case FETCH_C:
case FETCH_D:
case FETCH_E:
case FETCH_F:
case FETCH_G:
case FETCH_H:
case FETCH_I:
case FETCH_J:
case FETCH_K:
case FETCH_L:
*++ptop = parg[op - FETCH_A];
break;
case STORE_A:
case STORE_B:
case STORE_C:
case STORE_D:
case STORE_E:
case STORE_F:
case STORE_G:
case STORE_H:
case STORE_I:
case STORE_J:
case STORE_K:
case STORE_L:
parg[op - STORE_A] = *ptop--;
break;
case CONST_PI:
*++ptop = PI;
break;
case CONST_D2R:
*++ptop = PI/180.;
break;
case CONST_R2D:
*++ptop = 180./PI;
break;
case UNARY_NEG:
*ptop = - *ptop;
break;
case ADD:
top = *ptop--;
*ptop += top;
break;
case SUB:
top = *ptop--;
*ptop -= top;
break;
case MULT:
top = *ptop--;
*ptop *= top;
break;
case DIV:
top = *ptop--;
*ptop /= top;
break;
case MODULO:
itop = (epicsInt32) *ptop--;
if (itop)
*ptop = (epicsInt32) *ptop % itop;
else
*ptop = epicsNAN;
break;
case POWER:
top = *ptop--;
*ptop = pow(*ptop, top);
break;
case ABS_VAL:
*ptop = fabs(*ptop);
break;
case EXP:
*ptop = exp(*ptop);
break;
case LOG_10:
*ptop = log10(*ptop);
break;
case LOG_E:
*ptop = log(*ptop);
break;
case MAX:
nargs = *pinst++;
while (--nargs) {
top = *ptop--;
if (*ptop < top || isnan(top))
*ptop = top;
}
break;
case MIN:
nargs = *pinst++;
while (--nargs) {
top = *ptop--;
if (*ptop > top || isnan(top))
*ptop = top;
}
break;
case SQU_RT:
*ptop = sqrt(*ptop);
break;
case ACOS:
*ptop = acos(*ptop);
break;
case ASIN:
*ptop = asin(*ptop);
break;
case ATAN:
*ptop = atan(*ptop);
break;
case ATAN2:
top = *ptop--;
*ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */
break;
case COS:
*ptop = cos(*ptop);
break;
case SIN:
*ptop = sin(*ptop);
break;
case TAN:
*ptop = tan(*ptop);
break;
case COSH:
*ptop = cosh(*ptop);
break;
case SINH:
*ptop = sinh(*ptop);
break;
case TANH:
*ptop = tanh(*ptop);
break;
case CEIL:
*ptop = ceil(*ptop);
break;
case FLOOR:
*ptop = floor(*ptop);
break;
case FINITE:
nargs = *pinst++;
top = finite(*ptop);
while (--nargs) {
--ptop;
top = top && finite(*ptop);
}
*ptop = top;
break;
case ISINF:
*ptop = isinf(*ptop);
break;
case ISNAN:
nargs = *pinst++;
top = isnan(*ptop);
while (--nargs) {
--ptop;
top = top || isnan(*ptop);
}
*ptop = top;
break;
case NINT:
top = *ptop;
*ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5);
break;
case RANDOM:
*++ptop = calcRandom();
break;
case REL_OR:
top = *ptop--;
*ptop = *ptop || top;
break;
case REL_AND:
top = *ptop--;
*ptop = *ptop && top;
break;
case REL_NOT:
*ptop = ! *ptop;
break;
/* Be VERY careful converting double to int in case bit 31 is set!
* Out-of-range errors give very different results on different sytems.
* Convert negative doubles to signed and positive doubles to unsigned
* first to avoid overflows if bit 32 is set.
* The result is always signed, values with bit 31 set are negative
* to avoid problems when writing the value to signed integer fields
* like longout.VAL or ao.RVAL. However unsigned fields may give
* problems on some architectures. (Fewer than giving problems with
* signed integer. Maybe the conversion functions should handle
* overflows better.)
*/
#define d2i(x) ((x)<0?(epicsInt32)(x):(epicsInt32)(epicsUInt32)(x))
#define d2ui(x) ((x)<0?(epicsUInt32)(epicsInt32)(x):(epicsUInt32)(x))
case BIT_OR:
top = *ptop--;
*ptop = (double)(d2i(*ptop) | d2i(top));
break;
case BIT_AND:
top = *ptop--;
*ptop = (double)(d2i(*ptop) & d2i(top));
break;
case BIT_EXCL_OR:
top = *ptop--;
*ptop = (double)(d2i(*ptop) ^ d2i(top));
break;
case BIT_NOT:
*ptop = (double)~d2i(*ptop);
break;
/* In C the shift operators decide on an arithmetic or logical shift
* based on whether the integer is signed or unsigned.
* With signed integers, a right-shift is arithmetic and will
* extend the sign bit into the left-hand end of the value. When used
* with unsigned values a logical shift is performed. The
* double-casting through signed/unsigned here is important, see above.
*/
case RIGHT_SHIFT_ARITH:
top = *ptop--;
*ptop = (double)(d2i(*ptop) >> (d2i(top) & 31));
break;
case LEFT_SHIFT_ARITH:
top = *ptop--;
*ptop = (double)(d2i(*ptop) << (d2i(top) & 31));
break;
case RIGHT_SHIFT_LOGIC:
top = *ptop--;
*ptop = (double)(d2ui(*ptop) >> (d2ui(top) & 31u));
break;
case NOT_EQ:
top = *ptop--;
*ptop = *ptop != top;
break;
case LESS_THAN:
top = *ptop--;
*ptop = *ptop < top;
break;
case LESS_OR_EQ:
top = *ptop--;
*ptop = *ptop <= top;
break;
case EQUAL:
top = *ptop--;
*ptop = *ptop == top;
break;
case GR_OR_EQ:
top = *ptop--;
*ptop = *ptop >= top;
break;
case GR_THAN:
top = *ptop--;
*ptop = *ptop > top;
break;
case COND_IF:
if (*ptop-- == 0.0 &&
cond_search(&pinst, COND_ELSE)) return -1;
break;
case COND_ELSE:
if (cond_search(&pinst, COND_END)) return -1;
break;
case COND_END:
break;
default:
errlogPrintf("calcPerform: Bad Opcode %d at %p\n", op, pinst-1);
return -1;
}
}
/* The stack should now have one item on it, the expression value */
if (ptop != stack + 1)
return -1;
*presult = *ptop;
return 0;
}
#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
# pragma optimize("", on)
#endif
epicsShareFunc long
calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores)
{
unsigned long inputs = 0;
unsigned long stores = 0;
char op;
while ((op = *pinst++) != END_EXPRESSION) {
switch (op) {
case LITERAL_DOUBLE:
pinst += sizeof(double);
break;
case LITERAL_INT:
pinst += sizeof(epicsInt32);
break;
case MIN:
case MAX:
case FINITE:
case ISNAN:
pinst++;
break;
case FETCH_A:
case FETCH_B:
case FETCH_C:
case FETCH_D:
case FETCH_E:
case FETCH_F:
case FETCH_G:
case FETCH_H:
case FETCH_I:
case FETCH_J:
case FETCH_K:
case FETCH_L:
/* Don't claim to use an arg we already stored to */
inputs |= (1 << (op - FETCH_A)) & ~stores;
break;
case STORE_A:
case STORE_B:
case STORE_C:
case STORE_D:
case STORE_E:
case STORE_F:
case STORE_G:
case STORE_H:
case STORE_I:
case STORE_J:
case STORE_K:
case STORE_L:
stores |= (1 << (op - STORE_A));
break;
default:
break;
}
}
if (pinputs) *pinputs = inputs;
if (pstores) *pstores = stores;
return 0;
}
/* Generate a random number between 0 and 1 using the algorithm
* seed = (multy * seed) + addy Random Number Generator by Knuth
* SemiNumerical Algorithms
* Chapter 1
* randy = seed / 65535.0 To normalize the number between 0 - 1
*/
static unsigned short seed = 0xa3bf;
static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */
static unsigned short addy = 0x3141;
static double calcRandom(void)
{
seed = (seed * multy) + addy;
/* between 0 - 1 */
return (double) seed / 65535.0;
}
/* Search the instruction stream for a matching operator, skipping any
* other conditional instructions found, and leave *ppinst pointing to
* the next instruction to be executed.
*/
static int cond_search(const char **ppinst, int match)
{
const char *pinst = *ppinst;
int count = 1;
int op;
while ((op = *pinst++) != END_EXPRESSION) {
if (op == match && --count == 0) {
*ppinst = pinst;
return 0;
}
switch (op) {
case LITERAL_DOUBLE:
pinst += sizeof(double);
break;
case LITERAL_INT:
pinst += sizeof(epicsInt32);
break;
case MIN:
case MAX:
case FINITE:
case ISNAN:
pinst++;
break;
case COND_IF:
count++;
break;
}
}
return 1;
}