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_
)
96 TestAssembler::~TestAssembler() {
99 void TestAssembler::clear() {
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())
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
);
126 if (expected
.size() > result
.size()) ok
= false;
128 for (i
= 0, n
= expected
.size(); i
< n
; ++i
) {
129 if (expected
[i
] != result
[i
]) {
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
);
144 sprintf(buffer
, "byte test failed at byte %d", i
);
150 void TestAssembler::_error(jitcs::UnitTest
& t
, std::string
const& text
) {
152 if (filename
.size() > 0) {
157 if (funcname
.size() > 0) {
164 sprintf(buf
, "line %d: ", lineno
);
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
) {
178 setupInstructionNames(constinsnames
);
179 setupRegisterClassNames(constrcnames
);
180 setupFixedRegisterNames(constvrnames
);
183 filename
= filename_
;
188 jitcs::StreamAllocator
<jitcs::MemoryReference
> memstreamer(*alloc
, 32);
189 memstream
= &memstreamer
;
191 FILE* f
= fopen(filename
.c_str(), "r");
193 _error(t
, "cannot open");
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) == "<==") {
207 _error(t
, "did not compile function due to previous errors");
210 if (!_runTest(t
, outputfile
)) return;
215 funcname
= pin
.substr(3);
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
);
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
);
237 _error(t
, "invalid param type "+type
);
241 if (partypes
.size() == 1 && partypes
[0] == jitcs::FTT_Void
) {
244 for (size_t i
= 0, n
= resvec
.size(); i
< n
; ++i
) {
245 std::string type
, varname
;
246 split(resvec
[i
], " ", type
, varname
);
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
);
256 _error(t
, "invalid result type "+type
);
260 if (restypes
.size() == 1 && restypes
[0] == jitcs::FTT_Void
) {
263 if (resvec
.size() != 1) {
264 _error(t
, "more than one result value");
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
);
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);
289 curbb
= fnc
->getStartBlock();
291 } else if (pin
.substr(0,1) == ":") {
293 _error(t
, "cannot add basic block to empty function");
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();
301 bbnames
[pin
.substr(1)] = bb
.ptr();
302 } else if (pin
.substr(0,5) == "@var:") {
304 _error(t
, "cannot add var to empty function");
307 std::string z
= toLower(pin
.substr(5));
309 std::string type
, varname
;
310 split(z
, " ", type
, varname
);
312 _error(t
, "unnamed var");
315 if (constrcnames
.find(type
) == constrcnames
.end()) {
316 _error(t
, "invalid register class for var " + varname
);
319 vrnames
[toLower(varname
)] = fnc
->createRegister(static_cast<jitcs::RegClassId
>(constrcnames
[type
]))._ptr
;
320 } else if (pin
.substr(0,10) == "@strategy:") {
322 _error(t
, "cannot set flags of empty function");
325 } else if (pin
== "") {
327 std::string pin1
, pin2
;
328 split(pin
, "|", pin1
, pin2
);
331 std::string ins
, parms
;
332 std::vector
<std::string
> parmvec
;
333 split(toLower(pin
), " ", ins
, parms
);
335 split(parms
, ",", parmvec
);
336 if (constinsnames
.find(ins
) == constinsnames
.end()) {
338 _error(t
, "invalid instruction " + ins
);
341 used_insids
->insert(constinsnames
[ins
]);
342 const char* x
= addInstruction(constinsnames
[ins
], parmvec
);
345 _error(t
, "error in instruction '" + pin
+ "': " + x
);
350 if (!read_outbytes(pout
, outbytes
)) {
351 _error(t
, "invalid outbytes");
358 if (!_runTest(t
, outputfile
)) return;