1 /*
2 @author herumi
3
4 tiny calculator
5 This program generates a function to calc the value of
6 polynomial given by user in run-time.
7 use boost::spirit::classic
8 see calc2.cpp for new version of boost::spirit
9 */
10 #include <stdio.h>
11 #include <sstream>
12 #include <map>
13 #define XBYAK_NO_OP_NAMES
14 #include "xbyak/xbyak.h"
15 #ifdef _MSC_VER
16 #pragma warning(disable : 4127) // for boost(constant condition)
17 #pragma warning(disable : 4512) // for boost
18 #endif
19 #include <boost/spirit/include/classic_file_iterator.hpp>
20 #include <boost/spirit/include/classic_core.hpp>
21 #include <boost/bind.hpp>
22
23 enum Error {
24 UNDEFINED_VARIABLE = 1
25 };
26
27 /*
28 JIT assemble of given polynomial for VC or gcc
29 */
30 class FuncGen : public Xbyak::CodeGenerator {
31 public:
32 typedef std::map<std::string, int> Map;
33 private:
34 enum {
35 MAX_CONST_NUM = 32
36 };
37 double constTbl_[MAX_CONST_NUM];
38 size_t constTblPos_;
39 int regIdx_;
40 Map varMap_; // map var name to index
41 #ifdef XBYAK32
42 const Xbyak::Reg32& valTbl_;
43 const Xbyak::Reg32& tbl_;
44 #else
45 const Xbyak::Reg64& valTbl_;
46 const Xbyak::Reg64& tbl_;
47 #endif
48 public:
49 /*
50 @param y [out] the value of f(var)
51 @param var [in] table of input variables
52 func(double *y, const double var[]);
53 @note func does not return double to avoid difference of compiler
54 */
FuncGen(const std::vector<std::string> & varTbl)55 FuncGen(const std::vector<std::string>& varTbl)
56 : constTblPos_(0)
57 , regIdx_(-1)
58 #ifdef XBYAK32
59 , valTbl_(eax)
60 , tbl_(edx)
61 #elif defined(XBYAK64_WIN)
62 , valTbl_(rcx)
63 , tbl_(rdx)
64 #else
65 , valTbl_(rdi)
66 , tbl_(rsi)
67 #endif
68 {
69 #ifdef XBYAK32
70 mov(valTbl_, ptr[esp+8]); // eax == varTbl
71 mov(tbl_, (size_t)constTbl_);
72 #else
73 #ifdef XBYAK64_WIN
74 movaps(ptr [rsp + 8], xm6); // save xm6, xm7
75 movaps(ptr [rsp + 8 + 16], xm7);
76 #endif
77 mov(tbl_, (size_t)constTbl_);
78 #endif
79 for (int i = 0, n = static_cast<int>(varTbl.size()); i < n; i++) {
80 varMap_[varTbl[i]] = i;
81 }
82 }
83 // use edx
genPush(double n)84 void genPush(double n)
85 {
86 if (constTblPos_ >= MAX_CONST_NUM) throw;
87 constTbl_[constTblPos_] = n;
88 if (regIdx_ == 7) throw;
89 movsd(Xbyak::Xmm(++regIdx_), ptr[tbl_ + (int)(constTblPos_ * sizeof(double))]);
90 constTblPos_++;
91 }
92 // use eax
genVal(const char * begin,const char * end)93 void genVal(const char *begin, const char *end)
94 {
95 std::string var(begin, end);
96 if (varMap_.find(var) == varMap_.end()) throw UNDEFINED_VARIABLE;
97 if (regIdx_ == 7) throw;
98 movsd(Xbyak::Xmm(++regIdx_), ptr[valTbl_ + varMap_[var] * sizeof(double)]);
99 }
genAdd(const char *,const char *)100 void genAdd(const char*, const char*)
101 {
102 addsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
103 }
genSub(const char *,const char *)104 void genSub(const char*, const char*)
105 {
106 subsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
107 }
genMul(const char *,const char *)108 void genMul(const char*, const char*)
109 {
110 mulsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
111 }
genDiv(const char *,const char *)112 void genDiv(const char*, const char*)
113 {
114 divsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
115 }
complete()116 void complete()
117 {
118 #ifdef XBYAK32
119 mov(eax, ptr [esp + 4]); // eax = valTbl
120 movsd(ptr [eax], xm0);
121 #else
122 #ifdef XBYAK64_WIN
123 movaps(xm6, ptr [rsp + 8]);
124 movaps(xm7, ptr [rsp + 8 + 16]);
125 #endif
126 #endif
127 ret();
128 }
129 };
130
131 struct Grammar : public boost::spirit::classic::grammar<Grammar> {
132 FuncGen& f_;
GrammarGrammar133 Grammar(FuncGen& f) : f_(f) { }
134 template<typename ScannerT>
135 struct definition {
136 boost::spirit::classic::rule<ScannerT> poly0, poly1, poly2, var;
137
definitionGrammar::definition138 definition(const Grammar& self)
139 {
140 using namespace boost;
141 using namespace boost::spirit::classic;
142
143 poly0 = poly1 >> *(('+' >> poly1)[bind(&FuncGen::genAdd, ref(self.f_), _1, _2)]
144 | ('-' >> poly1)[bind(&FuncGen::genSub, ref(self.f_), _1, _2)]);
145 poly1 = poly2 >> *(('*' >> poly2)[bind(&FuncGen::genMul, ref(self.f_), _1, _2)]
146 | ('/' >> poly2)[bind(&FuncGen::genDiv, ref(self.f_), _1, _2)]);
147 var = (+alpha_p)[bind(&FuncGen::genVal, ref(self.f_), _1, _2)];
148 poly2 = real_p[bind(&FuncGen::genPush, ref(self.f_), _1)]
149 | var
150 | '(' >> poly0 >> ')';
151 }
startGrammar::definition152 const boost::spirit::classic::rule<ScannerT>& start() const { return poly0; }
153 };
154 };
155
put(const std::vector<double> & x)156 void put(const std::vector<double>& x)
157 {
158 printf("%f", x[0]);
159 for (size_t i = 1, n = x.size(); i < n; i++) {
160 printf(", %f", x[i]);
161 }
162 }
163
main(int argc,char * argv[])164 int main(int argc, char *argv[])
165 {
166 if (argc <= 2) {
167 fprintf(stderr, "calc \"var1 var2 ...\" \"function of var\"\n");
168 fprintf(stderr, "eg. calc x \"x*x\"\n");
169 fprintf(stderr, "eg. calc \"x y z\" \"x*x + y - z\"\n");
170 return 1;
171 }
172 const char *poly = argv[2];
173 try {
174 std::vector<std::string> varTbl;
175
176 // get varTbl from argv[1]
177 {
178 std::istringstream is(argv[1]);
179 int i = 0;
180 printf("varTbl = { ");
181 while (is) {
182 std::string var;
183 is >> var;
184 if (var.empty()) break;
185 printf("%s:%d, ", var.c_str(), i);
186 varTbl.push_back(var);
187 i++;
188 }
189 printf("}\n");
190 }
191 FuncGen funcGen(varTbl);
192 Grammar calc(funcGen);
193 boost::spirit::classic::parse_info<> r = parse(poly, calc, boost::spirit::classic::space_p);
194 if (!r.full) {
195 printf("err poly=%s\n", poly);
196 return 1;
197 }
198 funcGen.complete();
199 std::vector<double> valTbl;
200 valTbl.resize(varTbl.size());
201 #ifdef XBYAK32
202 puts("32bit mode");
203 void (*func)(double *ret, const double *valTbl) = funcGen.getCode<void (*)(double *, const double*)>();
204 #else
205 puts("64bit mode");
206 double (*func)(const double *valTbl) = funcGen.getCode<double (*)(const double*)>();
207 #endif
208 for (int i = 0; i < 10; i++) {
209 for (size_t j = 0, n = valTbl.size(); j < n; j++) {
210 valTbl[j] = rand() % 7;
211 }
212 double y;
213 #ifdef XBYAK32
214 func(&y, &valTbl[0]);
215 #else
216 y = func(&valTbl[0]);
217 #endif
218 printf("f("); put(valTbl); printf(")=%f\n", y);
219 }
220 } catch (std::exception& e) {
221 printf("ERR:%s\n", e.what());
222 } catch (Error err) {
223 printf("ERR:%d\n", err);
224 } catch (...) {
225 printf("unknown error\n");
226 }
227
228 return 0;
229 }
230