Changed current relation from BasicBlock to BasicBlockImpl, and Function
[jitcs.git] / tests / test_asm.cpp
blobb91fc8ed8f4b31abe3026e402cf279c10bc9c05a
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 "jitcs_int_dfg_analysis.h"
8 #include "jitcs_int_machine.h"
9 #include "jitcs_int_adt_dynslice.h"
10 #include "jitcs_int_bblock_impl.h"
11 #include "jitcs_dumper.h"
12 #include <stdio.h>
14 static bool readln(FILE* f, std::string& line, size_t& linecount) {
15 char c;
16 line.clear();
17 while (fread(&c, 1, 1, f) == 1) {
18 if (c == '\r') return true;
19 if (c == '\n') { linecount++; return true; }
20 line.append(1, c);
22 return (line.size() > 0);
24 static void trim(std::string& line) {
26 std::string::iterator i = line.begin(), e = line.end();
27 while (i != e && *i == ' ') ++i;
28 line.erase(line.begin(), i);
31 std::string::iterator i = line.begin(), e = line.end();
32 while (i != e && *(e - 1) == ' ') --e;
33 line.erase(e, line.end());
36 void TestAssembler::split(std::string const& line, std::string const& splitter, std::string& pin, std::string& pout) {
37 std::string::size_type pos = line.find(splitter);
38 if (pos != std::string::npos) {
39 pin = line.substr(0, pos);
40 pout = line.substr(pos + splitter.size());
41 trim(pin);
42 trim(pout);
43 } else {
44 pin = line;
45 trim(pin);
46 pout.clear();
49 void TestAssembler::split(std::string const& line, std::string const& splitter, std::vector<std::string>& parts) {
50 std::string worker = line;
51 std::string::size_type pos;
52 while ((pos = worker.find(splitter)) != std::string::npos) {
53 std::string pin = worker.substr(0, pos);
54 trim(pin);
55 parts.push_back(pin);
56 worker = worker.substr(pos + splitter.size());
58 trim(worker);
59 parts.push_back(worker);
61 static bool read_outbytes(std::string const& data, std::vector<jitcs::u8>& out) {
62 jitcs::u8 d = 0;
63 bool second = false;
64 for (std::string::const_iterator i = data.begin(), e = data.end(); i != e; ++i) {
65 char c = *i;
66 if (c == ' ') continue;
67 if (c >= '0' && c <= '9') {
68 d = d * 16 + (c - '0');
69 second = !second;
70 if (!second) out.push_back(d);
71 continue;
73 c = c | 0x20;
74 if (c >= 'a' && c <= 'f') {
75 d = d * 16 + 10 + (c - 'a');
76 second = !second;
77 if (!second) out.push_back(d);
78 continue;
80 return false;
82 return !second;
84 std::string TestAssembler::toLower(std::string const& z) {
85 std::string x = z;
86 for (std::string::iterator i = x.begin(), e = x.end(); i != e; ++i) {
87 if (*i >= 'A' && *i <= 'Z') *i |= 0x20;
89 return x;
92 TestAssembler::TestAssembler(jitcs::RefCounter<jitcs::MemoryMgr> m,
93 jitcs::RefCounter<jitcs::TempAllocator> a,
94 jitcs::RefCounter<jitcs::IMachineInfo> mi_)
95 : mem(m)
96 , alloc(a)
97 , mi(mi_) {
98 setup = false;
99 clear();
101 TestAssembler::~TestAssembler() {
104 void TestAssembler::clear() {
105 outbytes.clear();
106 bbnames.clear();
107 vrnames.clear();
108 fnc.reset();
109 curbb = nullptr;
111 const jitcs::VirtualRegister* TestAssembler::findReg(std::string const& n) {
112 std::string n2 = toLower(n);
113 if (constvrnames.find(n2) != constvrnames.end())
114 return constvrnames[n2];
115 if (vrnames.find(n2) != vrnames.end())
116 return vrnames[n2];
117 return nullptr;
119 //void printBytes(std::vector<u8> const& v) {
120 // for (std::vector<u8>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)
121 // printf("%02X", *i);
123 bool TestAssembler::_runTest(jitcs::UnitTest& t, std::string const& outputfile) {
124 std::vector<jitcs::u8> expected = outbytes, result;
125 jitcs::MemoryMgr::CodeAndData cda = fnc->generate(mem, jitcs::Function::Direct_Code_Gen);
126 result.insert(result.begin(), cda.code._ptr, cda.code._ptr + cda.code.size());
127 mem->deallocate(cda);
129 bool ok = true;
130 size_t i, n;
131 if (expected.size() > result.size()) ok = false;
132 if (ok) {
133 for (i = 0, n = expected.size(); i < n; ++i) {
134 if (expected[i] != result[i]) {
135 ok = false; break;
140 if (outputfile.size() > 0) {
141 FILE* f = fopen(outputfile.c_str(), "wb");
142 if (result.size() > 0)
143 fwrite(&result[0], result.size(), 1, f);
144 fclose(f);
147 if (!ok) {
148 char buffer[100];
149 sprintf(buffer, "byte test failed at byte %d", i);
150 _error(t, buffer);
152 clear();
153 return ok;
155 void TestAssembler::_error(jitcs::UnitTest& t, std::string const& text) {
156 std::string msg;
157 if (filename.size() > 0) {
158 msg += "file ";
159 msg += filename;
160 msg += ":";
162 if (funcname.size() > 0) {
163 msg += "fnc ";
164 msg += funcname;
165 msg += ":";
167 if (lineno >= 1) {
168 char buf[20];
169 sprintf(buf, "line %d: ", lineno);
170 msg += buf;
172 msg += text;
173 t.check(msg.c_str(), false);
175 static void _check(jitcs::UnitTest& t, std::string const& text, bool condition) {
176 t.check(text.c_str(), condition);
179 void TestAssembler::_setupInstructionDFAnnotation
180 (jitcs::UnitTest& t, DFAnnotationInstruction& ann, const std::string& text) {
181 jitcs::Ref<const jitcs::IMachineDetails> det = mi->details();
182 jitcs::FunctionImpl& f = reinterpret_cast<jitcs::FunctionImpl&>(*fnc);
183 // allocate space for 2 bits for all resources
184 size_t rcnt = det->getResCount() + f.vregs_size();
185 ann.resize(jitcs::DivRoundedUpByPowerOf2<jitcs::BitSlice::E_BitsPerWord>(rcnt) * 2);
186 jitcs::BitSlice bits_used(&ann[0], rcnt);
187 jitcs::BitSlice bits_defined(&ann[ann.size() / 2], rcnt);
188 // clear def/use bits
189 bits_used.clearAll();
190 bits_defined.clearAll();
192 std::vector<std::string> regvec;
193 split(toLower(text), ",", regvec);
194 for (size_t i = 0, n = regvec.size(); i < n; ++i) {
195 if (regvec[i] == "") continue;
196 std::string reg, mode;
197 split(regvec[i], "/", reg, mode);
198 jitcs::ResId res;
199 if (constvrnames.find(reg) != constvrnames.end()) {
200 res = constvrnames.find(reg)->second->res;
201 } else if (vrnames.find(reg) != vrnames.end()) {
202 res = vrnames.find(reg)->second->res;
203 } else {
204 _error(t, "DF Annotation failed for unknown register " + reg);
205 continue;
207 if (res >= rcnt) {
208 _error(t, "DF Annotation: wrong resource for register " + reg);
209 continue;
211 if (mode == "r") {
212 bits_used.mark(res);
213 } else if (mode == "w") {
214 bits_defined.mark(res);
215 } else if (mode == "rw") {
216 bits_used.mark(res);
217 bits_defined.mark(res);
218 } else {
219 _error(t, "DF Annotation: wrong mode " + mode);
223 bool TestAssembler::_runDFTest(jitcs::UnitTest& t, DFAnnotationFunction& ann) {
224 if (ann.size() == 0) return true;
225 jitcs::Ref<const jitcs::IMachineDetails> det = mi->details();
226 jitcs::FunctionImpl& f = reinterpret_cast<jitcs::FunctionImpl&>(*fnc);
227 size_t rcnt = det->getResCount() + f.vregs_size();
229 jitcs::DynSlice<jitcs::u32,8> touchedFixed(det->getResClassCount());
230 jitcs::DFGAnalysis a;
231 a.init(f, jitcs::DFGAnalysis::M_Local, touchedFixed);
233 jitcs::DynBitSlice<128> used(rcnt, false), defined(rcnt, false);
235 bool result = true;
236 for (jitcs::FunctionImpl::bb_range r = f.bbs_range(); !!r; ++r) {
237 jitcs::BasicBlockImpl* bb = *r;
238 if (bb->instr_size() > 0 && ann.find(bb->id()) == ann.end()) {
239 char buffer[100];
240 sprintf(buffer, "missing DF annotations for bblock %d", bb->id());
241 _error(t, buffer);
242 result = false;
243 continue;
245 DFAnnotationBlock& bann = ann[bb->id()];
246 if (bann.size() != bb->instr_size()) {
247 char buffer[100];
248 sprintf(buffer, "length mismatch for DF annotations of bblock %d", bb->id());
249 _error(t, buffer);
250 result = false;
251 continue;
253 for (size_t j = 0, m = bb->instr_size(); j < m; ++j) {
254 used.clearAll();
255 defined.clearAll();
256 a.extractUseDef(bb, j, used, defined);
257 jitcs::BitSlice comp_used(&bann[j][0], rcnt);
258 jitcs::BitSlice comp_defined(&bann[j][bann[j].size() / 2], rcnt);
259 if (!comp_used.equals(used)
260 || !comp_defined.equals(defined)) {
261 char buffer[100];
262 sprintf(buffer, "df mismatch in bblock %d, instructon %d", bb->id(), j);
263 _error(t, buffer);
264 jitcs::PrintFDumper dumper;
265 printf("comp used ");
266 comp_used.dump(dumper);
267 printf("\n");
268 printf("comp define ");
269 comp_defined.dump(dumper);
270 printf("\n");
271 printf("use ");
272 used.dump(dumper);
273 printf("\n");
274 printf("def ");
275 defined.dump(dumper);
276 printf("\n");
277 result = false;
281 return result;
284 void TestAssembler::checkFile(jitcs::UnitTest& t, std::string const& filename_,
285 std::unordered_set<jitcs::u32>* used_insids,
286 std::string const& outputfile) {
287 DFAnnotationFunction ann;
289 if (!setup) {
290 setupInstructionNames(constinsnames);
291 setupRegisterClassNames(constrcnames);
292 setupFixedRegisterNames(constvrnames);
293 setup = true;
295 filename = filename_;
296 funcname = "";
297 lineno = 0;
298 clear();
300 jitcs::StreamAllocator<jitcs::MemoryReference> memstreamer(*alloc, 32);
301 memstream = &memstreamer;
303 FILE* f = fopen(filename.c_str(), "r");
304 if (!f) {
305 _error(t, "cannot open");
306 return;
308 bool dump = false;
309 bool empty = true;
310 bool inserror = false;
311 std::string line, pin, pout;
312 while (readln(f, line, lineno)) {
313 split(line, "==>", pin, pout);
315 if (pin.substr(0,3) == "<==") {
316 if (!empty) {
317 ibuf.end();
318 if (inserror) {
319 _error(t, "did not compile function due to previous errors");
320 clear();
321 ann.clear();
322 } else {
323 _runDFTest(t, ann);
324 if (!_runTest(t, outputfile)) return;
327 empty = true;
328 inserror = false;
329 funcname = pin.substr(3);
330 trim(funcname);
331 } else if (pin.substr(0,5) == "@fun:") {
332 jitcs::FTSubType sub = jitcs::FTS_CDecl;
333 std::vector<jitcs::FTTypeId> restypes, partypes;
334 std::string parms, result;
335 std::vector<std::string> parmvec, resvec;
336 split(pin.substr(5), "->", parms, result);
337 split(parms, ",", parmvec);
338 split(result, ",", resvec);
339 for (size_t i = 0, n = parmvec.size(); i < n; ++i) {
340 std::string type, varname;
341 split(parmvec[i], " ", type, varname);
342 if (type == "int") {
343 partypes.push_back(jitcs::FTT_Int);
344 } else if (type == "void*") {
345 partypes.push_back(jitcs::FTT_Ptr);
346 } else if (type == "ptrdiff_t") {
347 partypes.push_back(jitcs::FTT_PtrInt);
348 } else if (type == "void") {
349 partypes.push_back(jitcs::FTT_Void);
350 } else {
351 _error(t, "invalid param type "+type);
352 return;
355 if (partypes.size() == 1 && partypes[0] == jitcs::FTT_Void) {
356 partypes.clear();
358 for (size_t i = 0, n = resvec.size(); i < n; ++i) {
359 std::string type, varname;
360 split(resvec[i], " ", type, varname);
361 if (type == "int") {
362 restypes.push_back(jitcs::FTT_Int);
363 } else if (type == "void*") {
364 restypes.push_back(jitcs::FTT_Ptr);
365 } else if (type == "ptrdiff_t") {
366 restypes.push_back(jitcs::FTT_PtrInt);
367 } else if (type == "void") {
368 restypes.push_back(jitcs::FTT_Void);
369 } else {
370 _error(t, "invalid result type "+type);
371 return;
374 if (restypes.size() == 1 && restypes[0] == jitcs::FTT_Void) {
375 restypes.clear();
377 if (resvec.size() != 1) {
378 _error(t, "more than one result value");
379 return;
381 jitcs::Slice<jitcs::FTTypeId> resslice = restypes.size() > 0
382 ? jitcs::Slice<jitcs::FTTypeId>(&restypes[0], restypes.size())
383 : jitcs::Slice<jitcs::FTTypeId>();
384 jitcs::Slice<jitcs::FTTypeId> parslice = partypes.size() > 0
385 ? jitcs::Slice<jitcs::FTTypeId>(&partypes[0], partypes.size())
386 : jitcs::Slice<jitcs::FTTypeId>();
387 std::shared_ptr<const jitcs::CallingConvention> cc
388 = mi->getCC(sub, resslice, parslice);
389 fnc.reset();
390 fnc = std::move(mi->createFnc(alloc, cc));
392 //for (size_t i = 0, n = parmvec.size(); i < n; ++i) {
393 // std::string type, varname;
394 // split(parmvec[i], " ", type, varname);
395 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->arg(i);
397 //for (size_t i = 0, n = resvec.size(); i < n; ++i) {
398 // std::string type, varname;
399 // split(resvec[i], " ", type, varname);
400 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->result(i);
402 empty = false;
403 curbb = fnc->getStartBlock();
404 ibuf.start(curbb);
405 } else if (pin.substr(0,1) == ":") {
406 if (empty) {
407 _error(t, "cannot add basic block to empty function");
408 return;
410 std::string n2 = toLower(pin.substr(1));
411 jitcs::RefOrNull<jitcs::BasicBlock> bb = bbnames.find(n2) != bbnames.end() ? jitcs::Ref<jitcs::BasicBlock>(bbnames[n2]) : fnc->createBasicBlock();
412 ibuf.end();
413 curbb = bb;
414 ibuf.start(curbb);
415 bbnames[pin.substr(1)] = bb.ptr();
416 } else if (pin.substr(0,5) == "@var:") {
417 if (empty) {
418 _error(t, "cannot add var to empty function");
419 return;
421 std::string z = toLower(pin.substr(5));
422 trim(z);
423 std::string type, varname;
424 split(z, " ", type, varname);
425 if (varname == "") {
426 _error(t, "unnamed var");
427 return;
429 if (constrcnames.find(type) == constrcnames.end()) {
430 _error(t, "invalid register class for var " + varname);
431 return;
433 vrnames[toLower(varname)] = fnc->createRegister(static_cast<jitcs::RegClassId>(constrcnames[type]))._ptr;
434 } else if (pin.substr(0,10) == "@strategy:") {
435 if (empty) {
436 _error(t, "cannot set flags of empty function");
437 return;
439 } else if (pin == "") {
440 } else {
441 std::string pin1, pin2;
442 split(pin, "|", pin1, pin2);
443 pin = pin1;
445 std::string ins, parms;
446 std::vector<std::string> parmvec;
447 split(toLower(pin), " ", ins, parms);
448 if (parms != "")
449 split(parms, ",", parmvec);
450 if (constinsnames.find(ins) == constinsnames.end()) {
451 inserror = true;
452 _error(t, "invalid instruction " + ins);
453 } else {
454 if (used_insids)
455 used_insids->insert(constinsnames[ins]);
456 const char* x = addInstruction(constinsnames[ins], parmvec);
457 if (x != nullptr) {
458 inserror = true;
459 _error(t, "error in instruction '" + pin + "': " + x);
460 } else if (!curbb.isNull()) {
461 DFAnnotationBlock& dfab = ann[curbb.removeNullType()->id()];
462 dfab.push_back(DFAnnotationInstruction());
463 _setupInstructionDFAnnotation(t, dfab[dfab.size() - 1], pin2);
468 if (!read_outbytes(pout, outbytes)) {
469 _error(t, "invalid outbytes");
470 return;
473 if (!empty) {
474 ibuf.end();
475 if (!inserror) {
476 _runDFTest(t, ann);
477 if (!_runTest(t, outputfile)) return;
480 return;