230 lines
5.6 KiB
C++
230 lines
5.6 KiB
C++
|
/*
|
||
|
@author herumi
|
||
|
|
||
|
tiny calculator
|
||
|
This program generates a function to calc the value of
|
||
|
polynomial given by user in run-time.
|
||
|
use boost::spirit::classic
|
||
|
see calc2.cpp for new version of boost::spirit
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <sstream>
|
||
|
#include <map>
|
||
|
#define XBYAK_NO_OP_NAMES
|
||
|
#include "xbyak/xbyak.h"
|
||
|
#ifdef _MSC_VER
|
||
|
#pragma warning(disable : 4127) // for boost(constant condition)
|
||
|
#pragma warning(disable : 4512) // for boost
|
||
|
#endif
|
||
|
#include <boost/spirit/include/classic_file_iterator.hpp>
|
||
|
#include <boost/spirit/include/classic_core.hpp>
|
||
|
#include <boost/bind.hpp>
|
||
|
|
||
|
enum Error {
|
||
|
UNDEFINED_VARIABLE = 1
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
JIT assemble of given polynomial for VC or gcc
|
||
|
*/
|
||
|
class FuncGen : public Xbyak::CodeGenerator {
|
||
|
public:
|
||
|
typedef std::map<std::string, int> Map;
|
||
|
private:
|
||
|
enum {
|
||
|
MAX_CONST_NUM = 32
|
||
|
};
|
||
|
double constTbl_[MAX_CONST_NUM];
|
||
|
size_t constTblPos_;
|
||
|
int regIdx_;
|
||
|
Map varMap_; // map var name to index
|
||
|
#ifdef XBYAK32
|
||
|
const Xbyak::Reg32& valTbl_;
|
||
|
const Xbyak::Reg32& tbl_;
|
||
|
#else
|
||
|
const Xbyak::Reg64& valTbl_;
|
||
|
const Xbyak::Reg64& tbl_;
|
||
|
#endif
|
||
|
public:
|
||
|
/*
|
||
|
@param y [out] the value of f(var)
|
||
|
@param var [in] table of input variables
|
||
|
func(double *y, const double var[]);
|
||
|
@note func does not return double to avoid difference of compiler
|
||
|
*/
|
||
|
FuncGen(const std::vector<std::string>& varTbl)
|
||
|
: constTblPos_(0)
|
||
|
, regIdx_(-1)
|
||
|
#ifdef XBYAK32
|
||
|
, valTbl_(eax)
|
||
|
, tbl_(edx)
|
||
|
#elif defined(XBYAK64_WIN)
|
||
|
, valTbl_(rcx)
|
||
|
, tbl_(rdx)
|
||
|
#else
|
||
|
, valTbl_(rdi)
|
||
|
, tbl_(rsi)
|
||
|
#endif
|
||
|
{
|
||
|
#ifdef XBYAK32
|
||
|
mov(valTbl_, ptr[esp+8]); // eax == varTbl
|
||
|
mov(tbl_, (size_t)constTbl_);
|
||
|
#else
|
||
|
#ifdef XBYAK64_WIN
|
||
|
movaps(ptr [rsp + 8], xm6); // save xm6, xm7
|
||
|
movaps(ptr [rsp + 8 + 16], xm7);
|
||
|
#endif
|
||
|
mov(tbl_, (size_t)constTbl_);
|
||
|
#endif
|
||
|
for (int i = 0, n = static_cast<int>(varTbl.size()); i < n; i++) {
|
||
|
varMap_[varTbl[i]] = i;
|
||
|
}
|
||
|
}
|
||
|
// use edx
|
||
|
void genPush(double n)
|
||
|
{
|
||
|
if (constTblPos_ >= MAX_CONST_NUM) throw;
|
||
|
constTbl_[constTblPos_] = n;
|
||
|
if (regIdx_ == 7) throw;
|
||
|
movsd(Xbyak::Xmm(++regIdx_), ptr[tbl_ + (int)(constTblPos_ * sizeof(double))]);
|
||
|
constTblPos_++;
|
||
|
}
|
||
|
// use eax
|
||
|
void genVal(const char *begin, const char *end)
|
||
|
{
|
||
|
std::string var(begin, end);
|
||
|
if (varMap_.find(var) == varMap_.end()) throw UNDEFINED_VARIABLE;
|
||
|
if (regIdx_ == 7) throw;
|
||
|
movsd(Xbyak::Xmm(++regIdx_), ptr[valTbl_ + varMap_[var] * sizeof(double)]);
|
||
|
}
|
||
|
void genAdd(const char*, const char*)
|
||
|
{
|
||
|
addsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||
|
}
|
||
|
void genSub(const char*, const char*)
|
||
|
{
|
||
|
subsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||
|
}
|
||
|
void genMul(const char*, const char*)
|
||
|
{
|
||
|
mulsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||
|
}
|
||
|
void genDiv(const char*, const char*)
|
||
|
{
|
||
|
divsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||
|
}
|
||
|
void complete()
|
||
|
{
|
||
|
#ifdef XBYAK32
|
||
|
mov(eax, ptr [esp + 4]); // eax = valTbl
|
||
|
movsd(ptr [eax], xm0);
|
||
|
#else
|
||
|
#ifdef XBYAK64_WIN
|
||
|
movaps(xm6, ptr [rsp + 8]);
|
||
|
movaps(xm7, ptr [rsp + 8 + 16]);
|
||
|
#endif
|
||
|
#endif
|
||
|
ret();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct Grammar : public boost::spirit::classic::grammar<Grammar> {
|
||
|
FuncGen& f_;
|
||
|
Grammar(FuncGen& f) : f_(f) { }
|
||
|
template<typename ScannerT>
|
||
|
struct definition {
|
||
|
boost::spirit::classic::rule<ScannerT> poly0, poly1, poly2, var;
|
||
|
|
||
|
definition(const Grammar& self)
|
||
|
{
|
||
|
using namespace boost;
|
||
|
using namespace boost::spirit::classic;
|
||
|
|
||
|
poly0 = poly1 >> *(('+' >> poly1)[bind(&FuncGen::genAdd, ref(self.f_), _1, _2)]
|
||
|
| ('-' >> poly1)[bind(&FuncGen::genSub, ref(self.f_), _1, _2)]);
|
||
|
poly1 = poly2 >> *(('*' >> poly2)[bind(&FuncGen::genMul, ref(self.f_), _1, _2)]
|
||
|
| ('/' >> poly2)[bind(&FuncGen::genDiv, ref(self.f_), _1, _2)]);
|
||
|
var = (+alpha_p)[bind(&FuncGen::genVal, ref(self.f_), _1, _2)];
|
||
|
poly2 = real_p[bind(&FuncGen::genPush, ref(self.f_), _1)]
|
||
|
| var
|
||
|
| '(' >> poly0 >> ')';
|
||
|
}
|
||
|
const boost::spirit::classic::rule<ScannerT>& start() const { return poly0; }
|
||
|
};
|
||
|
};
|
||
|
|
||
|
void put(const std::vector<double>& x)
|
||
|
{
|
||
|
for (size_t i = 0, n = x.size(); i < n; i++) {
|
||
|
if (i > 0) printf(", ");
|
||
|
printf("%f", x[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
if (argc <= 2) {
|
||
|
fprintf(stderr, "calc \"var1 var2 ...\" \"function of var\"\n");
|
||
|
fprintf(stderr, "eg. calc x \"x*x\"\n");
|
||
|
fprintf(stderr, "eg. calc \"x y z\" \"x*x + y - z\"\n");
|
||
|
return 1;
|
||
|
}
|
||
|
const char *poly = argv[2];
|
||
|
try {
|
||
|
std::vector<std::string> varTbl;
|
||
|
|
||
|
// get varTbl from argv[1]
|
||
|
{
|
||
|
std::istringstream is(argv[1]);
|
||
|
int i = 0;
|
||
|
printf("varTbl = { ");
|
||
|
while (is) {
|
||
|
std::string var;
|
||
|
is >> var;
|
||
|
if (var.empty()) break;
|
||
|
printf("%s:%d, ", var.c_str(), i);
|
||
|
varTbl.push_back(var);
|
||
|
i++;
|
||
|
}
|
||
|
printf("}\n");
|
||
|
}
|
||
|
FuncGen funcGen(varTbl);
|
||
|
Grammar calc(funcGen);
|
||
|
boost::spirit::classic::parse_info<> r = parse(poly, calc, boost::spirit::classic::space_p);
|
||
|
if (!r.full) {
|
||
|
printf("err poly=%s\n", poly);
|
||
|
return 1;
|
||
|
}
|
||
|
funcGen.complete();
|
||
|
std::vector<double> valTbl;
|
||
|
valTbl.resize(varTbl.size());
|
||
|
#ifdef XBYAK32
|
||
|
puts("32bit mode");
|
||
|
void (*func)(double *ret, const double *valTbl) = funcGen.getCode<void (*)(double *, const double*)>();
|
||
|
#else
|
||
|
puts("64bit mode");
|
||
|
double (*func)(const double *valTbl) = funcGen.getCode<double (*)(const double*)>();
|
||
|
#endif
|
||
|
for (int i = 0; i < 10; i++) {
|
||
|
for (size_t j = 0, n = valTbl.size(); j < n; j++) {
|
||
|
valTbl[j] = rand() % 7;
|
||
|
}
|
||
|
double y;
|
||
|
#ifdef XBYAK32
|
||
|
func(&y, &valTbl[0]);
|
||
|
#else
|
||
|
y = func(&valTbl[0]);
|
||
|
#endif
|
||
|
printf("f("); put(valTbl); printf(")=%f\n", y);
|
||
|
}
|
||
|
} catch (std::exception& e) {
|
||
|
printf("ERR:%s\n", e.what());
|
||
|
} catch (Error err) {
|
||
|
printf("ERR:%d\n", err);
|
||
|
} catch (...) {
|
||
|
printf("unknown error\n");
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|