| /* |
| @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; |
| } |