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