Added dataflow info to instruction tests. It is not yet checked.
[jitcs.git] / tests / test_asm.cpp
blob020a4d30b3c5fc4e4cedbb9c0ab5eb83ae156ed8
1 #include "test_asm.h"
2 #include "jitcs_callingconvention.h"
3 #include "jitcs_machine.h"
4 #include "jitcs_tmpalloc.h"
5 #include "jitcs_memmgr.h"
6 #include "jitcs_function.h"
7 #include <stdio.h>
9 static bool readln(FILE* f, std::string& line, size_t& linecount) {
10 char c;
11 line.clear();
12 while (fread(&c, 1, 1, f) == 1) {
13 if (c == '\r') return true;
14 if (c == '\n') { linecount++; return true; }
15 line.append(1, c);
17 return (line.size() > 0);
19 static void trim(std::string& line) {
21 std::string::iterator i = line.begin(), e = line.end();
22 while (i != e && *i == ' ') ++i;
23 line.erase(line.begin(), i);
26 std::string::iterator i = line.begin(), e = line.end();
27 while (i != e && *(e - 1) == ' ') --e;
28 line.erase(e, line.end());
31 void TestAssembler::split(std::string const& line, std::string const& splitter, std::string& pin, std::string& pout) {
32 std::string::size_type pos = line.find(splitter);
33 if (pos != std::string::npos) {
34 pin = line.substr(0, pos);
35 pout = line.substr(pos + splitter.size());
36 trim(pin);
37 trim(pout);
38 } else {
39 pin = line;
40 trim(pin);
41 pout.clear();
44 void TestAssembler::split(std::string const& line, std::string const& splitter, std::vector<std::string>& parts) {
45 std::string worker = line;
46 std::string::size_type pos;
47 while ((pos = worker.find(splitter)) != std::string::npos) {
48 std::string pin = worker.substr(0, pos);
49 trim(pin);
50 parts.push_back(pin);
51 worker = worker.substr(pos + splitter.size());
53 trim(worker);
54 parts.push_back(worker);
56 static bool read_outbytes(std::string const& data, std::vector<jitcs::u8>& out) {
57 jitcs::u8 d = 0;
58 bool second = false;
59 for (std::string::const_iterator i = data.begin(), e = data.end(); i != e; ++i) {
60 char c = *i;
61 if (c == ' ') continue;
62 if (c >= '0' && c <= '9') {
63 d = d * 16 + (c - '0');
64 second = !second;
65 if (!second) out.push_back(d);
66 continue;
68 c = c | 0x20;
69 if (c >= 'a' && c <= 'f') {
70 d = d * 16 + 10 + (c - 'a');
71 second = !second;
72 if (!second) out.push_back(d);
73 continue;
75 return false;
77 return !second;
79 std::string TestAssembler::toLower(std::string const& z) {
80 std::string x = z;
81 for (std::string::iterator i = x.begin(), e = x.end(); i != e; ++i) {
82 if (*i >= 'A' && *i <= 'Z') *i |= 0x20;
84 return x;
87 TestAssembler::TestAssembler(jitcs::RefCounter<jitcs::MemoryMgr> m,
88 jitcs::RefCounter<jitcs::TempAllocator> a,
89 jitcs::RefCounter<jitcs::IMachineInfo> mi_)
90 : mem(m)
91 , alloc(a)
92 , mi(mi_) {
93 setup = false;
94 clear();
96 TestAssembler::~TestAssembler() {
99 void TestAssembler::clear() {
100 outbytes.clear();
101 bbnames.clear();
102 vrnames.clear();
103 fnc.reset();
104 curbb = nullptr;
106 const jitcs::VirtualRegister* TestAssembler::findReg(std::string const& n) {
107 std::string n2 = toLower(n);
108 if (constvrnames.find(n2) != constvrnames.end())
109 return constvrnames[n2];
110 if (vrnames.find(n2) != vrnames.end())
111 return vrnames[n2];
112 return nullptr;
114 //void printBytes(std::vector<u8> const& v) {
115 // for (std::vector<u8>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)
116 // printf("%02X", *i);
118 bool TestAssembler::_runTest(jitcs::UnitTest& t, std::string const& outputfile) {
119 std::vector<jitcs::u8> expected = outbytes, result;
120 jitcs::MemoryMgr::CodeAndData cda = fnc->generate(mem, jitcs::Function::Direct_Code_Gen);
121 result.insert(result.begin(), cda.code._ptr, cda.code._ptr + cda.code.size());
122 mem->deallocate(cda);
124 bool ok = true;
125 size_t i, n;
126 if (expected.size() > result.size()) ok = false;
127 if (ok) {
128 for (i = 0, n = expected.size(); i < n; ++i) {
129 if (expected[i] != result[i]) {
130 ok = false; break;
135 if (outputfile.size() > 0) {
136 FILE* f = fopen(outputfile.c_str(), "wb");
137 if (result.size() > 0)
138 fwrite(&result[0], result.size(), 1, f);
139 fclose(f);
142 if (!ok) {
143 char buffer[100];
144 sprintf(buffer, "byte test failed at byte %d", i);
145 _error(t, buffer);
147 clear();
148 return ok;
150 void TestAssembler::_error(jitcs::UnitTest& t, std::string const& text) {
151 std::string msg;
152 if (filename.size() > 0) {
153 msg += "file ";
154 msg += filename;
155 msg += ":";
157 if (funcname.size() > 0) {
158 msg += "fnc ";
159 msg += funcname;
160 msg += ":";
162 if (lineno >= 1) {
163 char buf[20];
164 sprintf(buf, "line %d: ", lineno);
165 msg += buf;
167 msg += text;
168 t.check(msg.c_str(), false);
170 static void _check(jitcs::UnitTest& t, std::string const& text, bool condition) {
171 t.check(text.c_str(), condition);
174 void TestAssembler::checkFile(jitcs::UnitTest& t, std::string const& filename_,
175 std::unordered_set<jitcs::u32>* used_insids,
176 std::string const& outputfile) {
177 if (!setup) {
178 setupInstructionNames(constinsnames);
179 setupRegisterClassNames(constrcnames);
180 setupFixedRegisterNames(constvrnames);
181 setup = true;
183 filename = filename_;
184 funcname = "";
185 lineno = 0;
186 clear();
188 jitcs::StreamAllocator<jitcs::MemoryReference> memstreamer(*alloc, 32);
189 memstream = &memstreamer;
191 FILE* f = fopen(filename.c_str(), "r");
192 if (!f) {
193 _error(t, "cannot open");
194 return;
196 bool dump = false;
197 bool empty = true;
198 bool inserror = false;
199 std::string line, pin, pout;
200 while (readln(f, line, lineno)) {
201 split(line, "==>", pin, pout);
203 if (pin.substr(0,3) == "<==") {
204 if (!empty) {
205 ibuf.end();
206 if (inserror) {
207 _error(t, "did not compile function due to previous errors");
208 clear();
209 } else {
210 if (!_runTest(t, outputfile)) return;
213 empty = true;
214 inserror = false;
215 funcname = pin.substr(3);
216 trim(funcname);
217 } else if (pin.substr(0,5) == "@fun:") {
218 jitcs::FTSubType sub = jitcs::FTS_CDecl;
219 std::vector<jitcs::FTTypeId> restypes, partypes;
220 std::string parms, result;
221 std::vector<std::string> parmvec, resvec;
222 split(pin.substr(5), "->", parms, result);
223 split(parms, ",", parmvec);
224 split(result, ",", resvec);
225 for (size_t i = 0, n = parmvec.size(); i < n; ++i) {
226 std::string type, varname;
227 split(parmvec[i], " ", type, varname);
228 if (type == "int") {
229 partypes.push_back(jitcs::FTT_Int);
230 } else if (type == "void*") {
231 partypes.push_back(jitcs::FTT_Ptr);
232 } else if (type == "ptrdiff_t") {
233 partypes.push_back(jitcs::FTT_PtrInt);
234 } else if (type == "void") {
235 partypes.push_back(jitcs::FTT_Void);
236 } else {
237 _error(t, "invalid param type "+type);
238 return;
241 if (partypes.size() == 1 && partypes[0] == jitcs::FTT_Void) {
242 partypes.clear();
244 for (size_t i = 0, n = resvec.size(); i < n; ++i) {
245 std::string type, varname;
246 split(resvec[i], " ", type, varname);
247 if (type == "int") {
248 restypes.push_back(jitcs::FTT_Int);
249 } else if (type == "void*") {
250 restypes.push_back(jitcs::FTT_Ptr);
251 } else if (type == "ptrdiff_t") {
252 restypes.push_back(jitcs::FTT_PtrInt);
253 } else if (type == "void") {
254 restypes.push_back(jitcs::FTT_Void);
255 } else {
256 _error(t, "invalid result type "+type);
257 return;
260 if (restypes.size() == 1 && restypes[0] == jitcs::FTT_Void) {
261 restypes.clear();
263 if (resvec.size() != 1) {
264 _error(t, "more than one result value");
265 return;
267 jitcs::Slice<jitcs::FTTypeId> resslice = restypes.size() > 0
268 ? jitcs::Slice<jitcs::FTTypeId>(&restypes[0], restypes.size())
269 : jitcs::Slice<jitcs::FTTypeId>();
270 jitcs::Slice<jitcs::FTTypeId> parslice = partypes.size() > 0
271 ? jitcs::Slice<jitcs::FTTypeId>(&partypes[0], partypes.size())
272 : jitcs::Slice<jitcs::FTTypeId>();
273 std::shared_ptr<const jitcs::CallingConvention> cc
274 = mi->getCC(sub, resslice, parslice);
275 fnc.reset();
276 fnc = std::move(mi->createFnc(alloc, cc));
278 //for (size_t i = 0, n = parmvec.size(); i < n; ++i) {
279 // std::string type, varname;
280 // split(parmvec[i], " ", type, varname);
281 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->arg(i);
283 //for (size_t i = 0, n = resvec.size(); i < n; ++i) {
284 // std::string type, varname;
285 // split(resvec[i], " ", type, varname);
286 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->result(i);
288 empty = false;
289 curbb = fnc->getStartBlock();
290 ibuf.start(curbb);
291 } else if (pin.substr(0,1) == ":") {
292 if (empty) {
293 _error(t, "cannot add basic block to empty function");
294 return;
296 std::string n2 = toLower(pin.substr(1));
297 jitcs::RefOrNull<jitcs::BasicBlock> bb = bbnames.find(n2) != bbnames.end() ? jitcs::Ref<jitcs::BasicBlock>(bbnames[n2]) : fnc->createBasicBlock();
298 ibuf.end();
299 curbb = bb;
300 ibuf.start(curbb);
301 bbnames[pin.substr(1)] = bb.ptr();
302 } else if (pin.substr(0,5) == "@var:") {
303 if (empty) {
304 _error(t, "cannot add var to empty function");
305 return;
307 std::string z = toLower(pin.substr(5));
308 trim(z);
309 std::string type, varname;
310 split(z, " ", type, varname);
311 if (varname == "") {
312 _error(t, "unnamed var");
313 return;
315 if (constrcnames.find(type) == constrcnames.end()) {
316 _error(t, "invalid register class for var " + varname);
317 return;
319 vrnames[toLower(varname)] = fnc->createRegister(static_cast<jitcs::RegClassId>(constrcnames[type]))._ptr;
320 } else if (pin.substr(0,10) == "@strategy:") {
321 if (empty) {
322 _error(t, "cannot set flags of empty function");
323 return;
325 } else if (pin == "") {
326 } else {
327 std::string pin1, pin2;
328 split(pin, "|", pin1, pin2);
329 pin = pin1;
331 std::string ins, parms;
332 std::vector<std::string> parmvec;
333 split(toLower(pin), " ", ins, parms);
334 if (parms != "")
335 split(parms, ",", parmvec);
336 if (constinsnames.find(ins) == constinsnames.end()) {
337 inserror = true;
338 _error(t, "invalid instruction " + ins);
339 } else {
340 if (used_insids)
341 used_insids->insert(constinsnames[ins]);
342 const char* x = addInstruction(constinsnames[ins], parmvec);
343 if (x != nullptr) {
344 inserror = true;
345 _error(t, "error in instruction '" + pin + "': " + x);
350 if (!read_outbytes(pout, outbytes)) {
351 _error(t, "invalid outbytes");
352 return;
355 if (!empty) {
356 ibuf.end();
357 if (!inserror) {
358 if (!_runTest(t, outputfile)) return;
361 return;