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"
14 static bool readln(FILE* f
, std::string
& line
, size_t& linecount
) {
17 while (fread(&c
, 1, 1, f
) == 1) {
18 if (c
== '\r') return true;
19 if (c
== '\n') { linecount
++; return true; }
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());
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
);
56 worker
= worker
.substr(pos
+ splitter
.size());
59 parts
.push_back(worker
);
61 static bool read_outbytes(std::string
const& data
, std::vector
<jitcs::u8
>& out
) {
64 for (std::string::const_iterator i
= data
.begin(), e
= data
.end(); i
!= e
; ++i
) {
66 if (c
== ' ') continue;
67 if (c
>= '0' && c
<= '9') {
68 d
= d
* 16 + (c
- '0');
70 if (!second
) out
.push_back(d
);
74 if (c
>= 'a' && c
<= 'f') {
75 d
= d
* 16 + 10 + (c
- 'a');
77 if (!second
) out
.push_back(d
);
84 std::string
TestAssembler::toLower(std::string
const& z
) {
86 for (std::string::iterator i
= x
.begin(), e
= x
.end(); i
!= e
; ++i
) {
87 if (*i
>= 'A' && *i
<= 'Z') *i
|= 0x20;
92 TestAssembler::TestAssembler(jitcs::RefCounter
<jitcs::MemoryMgr
> m
,
93 jitcs::RefCounter
<jitcs::TempAllocator
> a
,
94 jitcs::RefCounter
<jitcs::IMachineInfo
> mi_
)
101 TestAssembler::~TestAssembler() {
104 void TestAssembler::clear() {
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())
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
);
131 if (expected
.size() > result
.size()) ok
= false;
133 for (i
= 0, n
= expected
.size(); i
< n
; ++i
) {
134 if (expected
[i
] != result
[i
]) {
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
);
149 sprintf(buffer
, "byte test failed at byte %d", i
);
155 void TestAssembler::_error(jitcs::UnitTest
& t
, std::string
const& text
) {
157 if (filename
.size() > 0) {
162 if (funcname
.size() > 0) {
169 sprintf(buf
, "line %d: ", lineno
);
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
);
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
;
204 _error(t
, "DF Annotation failed for unknown register " + reg
);
208 _error(t
, "DF Annotation: wrong resource for register " + reg
);
213 } else if (mode
== "w") {
214 bits_defined
.mark(res
);
215 } else if (mode
== "rw") {
217 bits_defined
.mark(res
);
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);
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()) {
240 sprintf(buffer
, "missing DF annotations for bblock %d", bb
->id());
245 DFAnnotationBlock
& bann
= ann
[bb
->id()];
246 if (bann
.size() != bb
->instr_size()) {
248 sprintf(buffer
, "length mismatch for DF annotations of bblock %d", bb
->id());
253 for (size_t j
= 0, m
= bb
->instr_size(); j
< m
; ++j
) {
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
)) {
262 sprintf(buffer
, "df mismatch in bblock %d, instructon %d", bb
->id(), j
);
264 jitcs::PrintFDumper dumper
;
265 printf("comp used ");
266 comp_used
.dump(dumper
);
268 printf("comp define ");
269 comp_defined
.dump(dumper
);
275 defined
.dump(dumper
);
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
;
290 setupInstructionNames(constinsnames
);
291 setupRegisterClassNames(constrcnames
);
292 setupFixedRegisterNames(constvrnames
);
295 filename
= filename_
;
300 jitcs::StreamAllocator
<jitcs::MemoryReference
> memstreamer(*alloc
, 32);
301 memstream
= &memstreamer
;
303 FILE* f
= fopen(filename
.c_str(), "r");
305 _error(t
, "cannot open");
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) == "<==") {
319 _error(t
, "did not compile function due to previous errors");
324 if (!_runTest(t
, outputfile
)) return;
329 funcname
= pin
.substr(3);
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
);
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
);
351 _error(t
, "invalid param type "+type
);
355 if (partypes
.size() == 1 && partypes
[0] == jitcs::FTT_Void
) {
358 for (size_t i
= 0, n
= resvec
.size(); i
< n
; ++i
) {
359 std::string type
, varname
;
360 split(resvec
[i
], " ", type
, varname
);
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
);
370 _error(t
, "invalid result type "+type
);
374 if (restypes
.size() == 1 && restypes
[0] == jitcs::FTT_Void
) {
377 if (resvec
.size() != 1) {
378 _error(t
, "more than one result value");
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
);
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);
403 curbb
= fnc
->getStartBlock();
405 } else if (pin
.substr(0,1) == ":") {
407 _error(t
, "cannot add basic block to empty function");
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();
415 bbnames
[pin
.substr(1)] = bb
.ptr();
416 } else if (pin
.substr(0,5) == "@var:") {
418 _error(t
, "cannot add var to empty function");
421 std::string z
= toLower(pin
.substr(5));
423 std::string type
, varname
;
424 split(z
, " ", type
, varname
);
426 _error(t
, "unnamed var");
429 if (constrcnames
.find(type
) == constrcnames
.end()) {
430 _error(t
, "invalid register class for var " + varname
);
433 vrnames
[toLower(varname
)] = fnc
->createRegister(static_cast<jitcs::RegClassId
>(constrcnames
[type
]))._ptr
;
434 } else if (pin
.substr(0,10) == "@strategy:") {
436 _error(t
, "cannot set flags of empty function");
439 } else if (pin
== "") {
441 std::string pin1
, pin2
;
442 split(pin
, "|", pin1
, pin2
);
445 std::string ins
, parms
;
446 std::vector
<std::string
> parmvec
;
447 split(toLower(pin
), " ", ins
, parms
);
449 split(parms
, ",", parmvec
);
450 if (constinsnames
.find(ins
) == constinsnames
.end()) {
452 _error(t
, "invalid instruction " + ins
);
455 used_insids
->insert(constinsnames
[ins
]);
456 const char* x
= addInstruction(constinsnames
[ins
], parmvec
);
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");
477 if (!_runTest(t
, outputfile
)) return;