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"
9 static bool readln(FILE* f
, std::string
& line
, size_t& linecount
) {
12 while (fread(&c
, 1, 1, f
) == 1) {
13 if (c
== '\r') return true;
14 if (c
== '\n') { linecount
++; return true; }
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());
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
);
51 worker
= worker
.substr(pos
+ splitter
.size());
54 parts
.push_back(worker
);
56 static bool read_outbytes(std::string
const& data
, std::vector
<jitcs::u8
>& out
) {
59 for (std::string::const_iterator i
= data
.begin(), e
= data
.end(); i
!= e
; ++i
) {
61 if (c
== ' ') continue;
62 if (c
>= '0' && c
<= '9') {
63 d
= d
* 16 + (c
- '0');
65 if (!second
) out
.push_back(d
);
69 if (c
>= 'a' && c
<= 'f') {
70 d
= d
* 16 + 10 + (c
- 'a');
72 if (!second
) out
.push_back(d
);
79 std::string
TestAssembler::toLower(std::string
const& z
) {
81 for (std::string::iterator i
= x
.begin(), e
= x
.end(); i
!= e
; ++i
) {
82 if (*i
>= 'A' && *i
<= 'Z') *i
|= 0x20;
87 TestAssembler::TestAssembler(jitcs::RefCounter
<jitcs::MemoryMgr
> m
,
88 jitcs::RefCounter
<jitcs::TempAllocator
> a
,
89 jitcs::RefCounter
<jitcs::IMachineInfo
> mi_
)
95 puts("TestAssembler constructor");
97 TestAssembler::~TestAssembler() {
98 puts("TestAssembler destructor");
101 void TestAssembler::clear() {
108 const jitcs::VirtualRegister
* TestAssembler::findReg(std::string
const& n
) {
109 std::string n2
= toLower(n
);
110 if (constvrnames
.find(n2
) != constvrnames
.end())
111 return constvrnames
[n2
];
112 if (vrnames
.find(n2
) != vrnames
.end())
116 //void printBytes(std::vector<u8> const& v) {
117 // for (std::vector<u8>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)
118 // printf("%02X", *i);
120 bool TestAssembler::_runTest(jitcs::UnitTest
& t
, std::string
const& outputfile
) {
121 std::vector
<jitcs::u8
> expected
= outbytes
, result
;
122 jitcs::MemoryMgr::CodeAndData cda
= fnc
->generate(mem
, jitcs::Function::Direct_Code_Gen
);
123 result
.insert(result
.begin(), cda
.code
._ptr
, cda
.code
._ptr
+ cda
.code
.size());
124 mem
->deallocate(cda
);
128 if (expected
.size() > result
.size()) ok
= false;
130 for (i
= 0, n
= expected
.size(); i
< n
; ++i
) {
131 if (expected
[i
] != result
[i
]) {
137 if (outputfile
.size() > 0) {
138 FILE* f
= fopen(outputfile
.c_str(), "wb");
139 if (result
.size() > 0)
140 fwrite(&result
[0], result
.size(), 1, f
);
146 sprintf(buffer
, "byte test failed at byte %d", i
);
152 void TestAssembler::_error(jitcs::UnitTest
& t
, std::string
const& text
) {
154 if (filename
.size() > 0) {
159 if (funcname
.size() > 0) {
166 sprintf(buf
, "line %d: ", lineno
);
170 t
.check(msg
.c_str(), false);
172 static void _check(jitcs::UnitTest
& t
, std::string
const& text
, bool condition
) {
173 t
.check(text
.c_str(), condition
);
176 void TestAssembler::checkFile(jitcs::UnitTest
& t
, std::string
const& filename_
,
177 std::unordered_set
<jitcs::u32
>* used_insids
,
178 std::string
const& outputfile
) {
180 setupInstructionNames(constinsnames
);
181 setupRegisterClassNames(constrcnames
);
182 setupFixedRegisterNames(constvrnames
);
185 filename
= filename_
;
189 jitcs::StreamAllocator
<jitcs::MemoryReference
> memstreamer(*alloc
, 32);
190 memstream
= &memstreamer
;
192 FILE* f
= fopen(filename
.c_str(), "r");
194 _error(t
, "cannot open");
199 std::string line
, pin
, pout
;
200 while (readln(f
, line
, lineno
)) {
201 split(line
, "==>", pin
, pout
);
203 if (pin
.substr(0,3) == "<==") {
206 if (!_runTest(t
, outputfile
)) return;
209 funcname
= pin
.substr(3);
211 } else if (pin
.substr(0,5) == "@fun:") {
212 jitcs::FTSubType sub
= jitcs::FTS_CDecl
;
213 std::vector
<jitcs::FTTypeId
> restypes
, partypes
;
214 std::string parms
, result
;
215 std::vector
<std::string
> parmvec
, resvec
;
216 split(pin
.substr(5), "->", parms
, result
);
217 split(parms
, ",", parmvec
);
218 split(result
, ",", resvec
);
219 for (size_t i
= 0, n
= parmvec
.size(); i
< n
; ++i
) {
220 std::string type
, varname
;
221 split(parmvec
[i
], " ", type
, varname
);
223 partypes
.push_back(jitcs::FTT_Int
);
224 } else if (type
== "void*") {
225 partypes
.push_back(jitcs::FTT_Ptr
);
226 } else if (type
== "ptrdiff_t") {
227 partypes
.push_back(jitcs::FTT_PtrInt
);
228 } else if (type
== "void") {
229 partypes
.push_back(jitcs::FTT_Void
);
231 _error(t
, "invalid param type "+type
);
235 if (partypes
.size() == 1 && partypes
[0] == jitcs::FTT_Void
) {
238 for (size_t i
= 0, n
= resvec
.size(); i
< n
; ++i
) {
239 std::string type
, varname
;
240 split(resvec
[i
], " ", type
, varname
);
242 restypes
.push_back(jitcs::FTT_Int
);
243 } else if (type
== "void*") {
244 restypes
.push_back(jitcs::FTT_Ptr
);
245 } else if (type
== "ptrdiff_t") {
246 restypes
.push_back(jitcs::FTT_PtrInt
);
247 } else if (type
== "void") {
248 restypes
.push_back(jitcs::FTT_Void
);
250 _error(t
, "invalid result type "+type
);
254 if (restypes
.size() == 1 && restypes
[0] == jitcs::FTT_Void
) {
257 if (resvec
.size() != 1) {
258 _error(t
, "more than one result value");
261 jitcs::Slice
<jitcs::FTTypeId
> resslice
= restypes
.size() > 0
262 ? jitcs::Slice
<jitcs::FTTypeId
>(&restypes
[0], restypes
.size())
263 : jitcs::Slice
<jitcs::FTTypeId
>(nullptr, 0);
264 jitcs::Slice
<jitcs::FTTypeId
> parslice
= partypes
.size() > 0
265 ? jitcs::Slice
<jitcs::FTTypeId
>(&partypes
[0], partypes
.size())
266 : jitcs::Slice
<jitcs::FTTypeId
>(nullptr, 0);
267 std::shared_ptr
<const jitcs::CallingConvention
> cc
268 = mi
->getCC(sub
, resslice
, parslice
);
270 fnc
= std::move(mi
->createFnc(alloc
, cc
));
272 //for (size_t i = 0, n = parmvec.size(); i < n; ++i) {
273 // std::string type, varname;
274 // split(parmvec[i], " ", type, varname);
275 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->arg(i);
277 //for (size_t i = 0, n = resvec.size(); i < n; ++i) {
278 // std::string type, varname;
279 // split(resvec[i], " ", type, varname);
280 // if (varname.size() > 0) vrnames[toLower(varname)] = fnc->result(i);
283 curbb
= fnc
->getStartBlock()._ptr
;
284 ibuf
.start(jitcs::RefOrNull
<jitcs::BasicBlock
>(curbb
));
285 } else if (pin
.substr(0,1) == ":") {
287 _error(t
, "cannot add basic block to empty function");
290 std::string n2
= toLower(pin
.substr(1));
291 jitcs::BasicBlock
* bb
= bbnames
.find(n2
) != bbnames
.end() ? bbnames
[n2
] : fnc
->createBasicBlock()._ptr
;
294 ibuf
.start(jitcs::RefOrNull
<jitcs::BasicBlock
>(curbb
));
295 bbnames
[pin
.substr(1)] = bb
;
296 } else if (pin
.substr(0,5) == "@var:") {
298 _error(t
, "cannot add var to empty function");
301 std::string z
= toLower(pin
.substr(5));
303 std::string type
, varname
;
304 split(z
, " ", type
, varname
);
306 _error(t
, "unnamed var");
309 if (constrcnames
.find(type
) == constrcnames
.end()) {
310 _error(t
, "invalid register class for var " + varname
);
313 vrnames
[toLower(varname
)] = fnc
->createRegister(static_cast<jitcs::RegClassId
>(constrcnames
[type
]))._ptr
;
314 } else if (pin
.substr(0,10) == "@strategy:") {
316 _error(t
, "cannot set flags of empty function");
319 } else if (pin
== "") {
324 std::string ins
, parms
;
325 std::vector
<std::string
> parmvec
;
326 split(toLower(pin
), " ", ins
, parms
);
328 split(parms
, ",", parmvec
);
329 if (constinsnames
.find(ins
) == constinsnames
.end()) {
330 _error(t
, "invalid instruction " + ins
);
334 used_insids
->insert(constinsnames
[ins
]);
335 const char* x
= addInstruction(constinsnames
[ins
], parmvec
);
337 _error(t
, "error in instruction '" + pin
+ "': " + x
);
342 if (!read_outbytes(pout
, outbytes
)) {
343 _error(t
, "invalid outbytes");
349 if (!_runTest(t
, outputfile
)) return;