2 Instruction Tool for X86:
3 Copyright (C) 2013-2014 Dirk Steinke.
4 See copyright and license notice in COPYRIGHT or include/jitcs.h
6 The tool reads instruction files from tests/x86/*.ins, and transforms them
7 into _bin/data/*.as files for use in the unit test program.
10 local _setfenv
, _ipairs
= setfenv
, ipairs
12 local _match
, _gmatch
, _fmt
= string.match
, string.gmatch
, string.format
13 local _gsub
, _sub
= string.gsub, string.sub
14 local _exec
= os
.execute
16 local function readfile(n
)
18 local ctnt
= f
:read("*a")
23 local validModes
= {ins2nasm32
= true, ins2nasm64
= true, nasm2as32
= true, nasm2as64
= true}
25 local MODE
, insfile
, p1
, p2
= ...
26 MODE
= string.lower(MODE
or "")
27 assert(validModes
[MODE
], "mode missing")
28 assert(insfile
, "insfile missing")
29 local nasmfile
, nasmlstfile
, asfile
30 if MODE
== "ins2nasm32" or MODE
== "nasm2as32" then
35 if MODE
== "ins2nasm32" or MODE
== "ins2nasm64" then
38 assert(nasmfile
, ".nasm file not given")
41 nasmlstfile
, asfile
= p1
, p2
42 assert(nasmlstfile
and asfile
, ".lst and .as file not given")
45 local prog
, progcode
= ""
46 local progstats
= { scopes
= {} }
49 for l
in _gmatch(readfile(insfile
), "([^\r\n]*)[\r\n]") do
51 if _match(l
, "^|") then
52 if (_match(l
, "^|%s*[Ff][Oo][Rr]%s*(%w+)%s*=%s*%[([^]]-)%]%s*[Dd][Oo]%s*$"))
54 local var
, list
= _match(l
, "^|%s*[Ff][Oo][Rr]%s*(%w+)%s*=%s*%[([^]]-)%]%s*[Dd][Oo]%s*$")
55 local list2
, list3
= {}, nil
56 for e
in _gmatch(list
, "([^,]+)") do
57 local e2
= _match(e
, "^%s*(%S+)%s*$")
59 list2
[#list2
+ 1] = e2
;
60 list3
= (list3
and (list3
..", ") or "").._fmt("%q", e2
)
64 local sn
= #progstats
.scopes
65 progstats
.scopes
[sn
+1] = {type = "for", start
= lineno
,
66 var
= var
, list
= list2
}
68 prog
= prog
.. "for k"..sn
..",v"..sn
.." in ipairs({"..list3
.."}) do\n"
69 prog
= prog
.. " ENV."..var
.." = v"..sn
.."\n"
72 print(lineno
.. ": illegal list in for statement")
76 elseif (_match(l
,"^|%s*[Ee][Nn][Dd][Ff][Oo][Rr]%s*$")) then
77 local sn
= #progstats
.scopes
78 if (sn
> 0 and progstats
.scopes
[sn
].type == "for") then
79 progstats
.scopes
[sn
] = nil
80 prog
= prog
.. "end\n"
83 print(lineno
.. ": endfor without for scope")
85 elseif (_match(l
,"^|%s*[Ll][Ee][Tt]%s*(%w+)%s*=%s*(.-)%s*$"))
87 local var
, val
= _match(l
, "^|%s*[Ll][Ee][Tt]%s*(%w+)%s*=%s*(.-)%s*$")
88 prog
= prog
.. _fmt("ENV.%s = ENV:handleExpr(%q, %q, %d)\n", var
, val
, l
, lineno
)
91 print(lineno
.. ": illegal commandline "..l
)
93 elseif _match(l
,"^%s*$") then
96 prog
= prog
.. _fmt("ENV:handleLine(%q, %d)",l
, lineno
) .. "\n"
100 if (#progstats
.scopes
> 0) then
102 print(lineno
.. ": not all scopes have been closed")
104 if errors
then return end
105 progcode
= loadstring(prog
)
107 print("cannot compile program")
111 function ENV
.handleLine(self
, line
, lineno
)
112 local function replacer(expr
)
113 local repfnc
= loadstring("return "..expr
)
116 print(lineno
.. ": illegal expression "..expr
)
119 _setfenv(repfnc
, self
)
120 return repfnc() or ""
123 local function filter(expr
)
124 local repfnc
= loadstring("return "..expr
)
127 print(lineno
.. ": illegal expression "..expr
)
130 _setfenv(repfnc
, self
)
131 return repfnc() and "" or "??"
134 line
= _gsub(line
, "$(%b())", replacer
)
136 if _match(line
, "^(%d+):") then
137 local m
= _match(line
, "^(%d+):")
138 if m
~= "32" and m
~= "64" then
140 print(lineno
.. ": illegal architecture "..m
)
143 if self
.arch
~= m
then return end
144 line
= _sub(line
, #m
+ 2)
147 line
= _gsub(line
, "%?(%b())", filter
)
148 if (_sub(line
,1,2) == "??") then return end
149 if self
.tgt
== "nasm" then
150 line
= _gsub(line
, "%b{}", "")
151 line
= _gsub(line
, "[<>]", "")
153 if self
.tgt
== "jitcs" then
154 line
= _gsub(line
, "%b<>", "")
155 line
= _gsub(line
, "[{}]", "")
160 function ENV
.handleExpr(self
, line
, lineno
)
161 local function replacer(expr
)
162 local repfnc
= loadstring("return "..expr
)
165 print(lineno
.. ": illegal expression "..expr
)
168 _setfenv(repfnc
, self
)
169 return repfnc() or ""
172 line
= _gsub(line
, "$(%b())", replacer
)
175 function ENV
.write(self
, line
)
176 if self
.tgt
== "jitcs" and self
.lst
and _sub(line
,1,1) ~= ":" and not _match(line
,"^%s*$") then
177 self
.lstpos
= self
.lstpos
+ 1
178 line
= line
.. " ==> " .. (self
.lst
[self
.lstpos
] or "")
181 self
.file
:write(line
)
182 self
.file
:write("\n")
188 return tonumber(Y
) == 64 and "64:" or ""
191 if Y
== "DEF" then return "" end
195 if M
== "S" then return "32" end
196 if M
== "D" then return "64" end
199 function ENV
.MVHSZ(Y
, D
)
200 if tonumber(Y
) == 128 or tonumber(Y
) == 256 or tonumber(Y
) == 512 then
201 return tostring(tonumber(Y
) / (D
and tonumber(D
) or 2))
203 print("VSZ error", Y
)
207 if Y
== "DEF" then return "R" end
208 if tonumber(Y
) == 8 then return "B" end
209 if tonumber(Y
) == 16 then return "H" end
210 if tonumber(Y
) == 32 then return "W" end
211 if tonumber(Y
) == 64 then return "D" end
212 print("RSZ error", Y
)
216 if tonumber(Y
) == 128 then return "X" end
217 if tonumber(Y
) == 256 then return "Y" end
218 if tonumber(Y
) == 512 then return "Z" end
219 print("VSZ error", Y
)
223 if tonumber(Y
) == 128 then return "X" end
224 if tonumber(Y
) == 256 then return "X" end
225 if tonumber(Y
) == 512 then return "Y" end
226 print("VHSZ error", Y
)
229 function ENV
.VOSZ(Y
,N
)
230 if tonumber(Y
) == tonumber(N
) then return "" end
234 -- if Y == "DEF" then return "ERROR" end
235 if tonumber(Y
) == 8 then return "B" end
236 if tonumber(Y
) == 16 then return "W" end
237 if tonumber(Y
) == 32 then return "D" end
238 if tonumber(Y
) == 64 then return "Q" end
241 function ENV
.NASMSZ(Y
)
242 if Y
== "DEF" then Y
= ENV
.arch
end
243 if tonumber(Y
) == 8 then return "byte" end
244 if tonumber(Y
) == 16 then return "word" end
245 if tonumber(Y
) == 32 then return "dword" end
246 if tonumber(Y
) == 64 then return "qword" end
247 if tonumber(Y
) == 128 then return "oword" end
248 if tonumber(Y
) == 256 then return "yword" end
251 function ENV
.MAX(A
,B
)
252 if A
== "DEF" then A
= ENV
.arch
end
253 if B
== "DEF" then B
= ENV
.arch
end
254 return tonumber(A
) > tonumber(B
) and A
or B
256 ENV
.RNAME_Table_R2R
= {ECX
= "RCX", EAX
= "RAX", EBX
= "RBX", EDX
= "RDX",
257 YMM0
= "XMM0", YMM1
= "XMM1", YMM2
= "XMM2", YMM3
= "XMM3"}
258 ENV
.RNAME_Table_R2N
= {
259 RAX
= {["8"] = "AL", ["16"] = "AX", ["32"] = "EAX", ["64"]= "RAX"},
260 RBX
= {["8"] = "BL", ["16"] = "BX", ["32"] = "EBX", ["64"]= "RBX"},
261 RCX
= {["8"] = "CL", ["16"] = "CX", ["32"] = "ECX", ["64"]= "RCX"},
262 RDX
= {["8"] = "DL", ["16"] = "DX", ["32"] = "EDX", ["64"]= "RDX"},
264 function ENV
.RCONST(I
, Y
)
265 if Y
== "DEF" then Y
= ENV
.arch
end
266 return math
.mod(I
, math
.pow(2,math
.min(tonumber(Y
),32)))
268 function ENV
.RNAME(R
, SZ
)
269 if SZ
== "DEF" then SZ
= ENV
.arch
end
270 R
= ENV
.RNAME_Table_R2R
[R
] or R
271 return ENV
.RNAME_Table_R2N
[R
][tostring(SZ
)] or "ERROR"
273 function ENV
.RNAMEDEF(R
, SZ
)
274 if SZ
== "DEF" then SZ
= 64 end
275 R
= ENV
.RNAME_Table_R2R
[R
] or R
276 return ENV
.RNAME_Table_R2N
[R
][tostring(SZ
)] or "ERROR"
278 ENV
.VNAME_Table_R2N
= {
279 XMM0
= {["64"] = "XMM0", ["128"] = "XMM0", ["256"] = "YMM0", ["512"] = "ZMM0"},
280 XMM1
= {["64"] = "XMM1", ["128"] = "XMM1", ["256"] = "YMM1", ["512"] = "ZMM1"},
281 XMM2
= {["64"] = "XMM2", ["128"] = "XMM2", ["256"] = "YMM2", ["512"] = "ZMM2"},
282 XMM3
= {["64"] = "XMM3", ["128"] = "XMM3", ["256"] = "YMM3", ["512"] = "ZMM3"},
283 XMM4
= {["64"] = "XMM4", ["128"] = "XMM4", ["256"] = "YMM4", ["512"] = "ZMM4"},
285 function ENV
.VNAME(R
, SZ
)
286 if SZ
== "DEF" then SZ
= ENV
.arch
end
287 R
= ENV
.RNAME_Table_R2R
[R
] or R
288 return ENV
.VNAME_Table_R2N
[R
][tostring(SZ
)] or "ERROR"
290 function ENV
.VHNAME(R
, SZ
)
291 if SZ
== "DEF" then SZ
= ENV
.arch
end
292 R
= ENV
.RNAME_Table_R2R
[R
] or R
293 return ENV
.VNAME_Table_R2N
[R
][tostring(tonumber(SZ
)/2)] or "ERROR"
296 local function readnasmlist(v
)
298 for l
in _gmatch(readfile(v
), "([^\r\n]*)[\r\n]") do
299 local line
, addr
, opcodes
, cmd
= _match(l
,
300 "^([ %d][ %d][ %d][ %d][ %d][ %d])"
301 .. " ([ %x][ %x][ %x][ %x][ %x][ %x][ %x][ %x])"
304 if opcodes
and #opcodes
> 0 then result
[#result
+ 1] = opcodes
end
309 ENV
.arch
= tostring(N
)
312 _setfenv(progcode
, ENV
)
313 if ALG
== "ins2nasm" then
314 ENV
.file
= _open(nasmfile
, "w")
316 ENV
:write(" bits " .. ENV
.arch
)
318 ENV
:write("%define RAX EAX")
319 ENV
:write("%define RBX EBX")
320 ENV
:write("%define RCX ECX")
321 ENV
:write("%define RDX EDX")
326 elseif ALG
== "nasm2as" then
327 local lst
= readnasmlist(nasmlstfile
)
329 ENV
.file
= _open(asfile
, "w")
331 ENV
:write("<== test")
332 ENV
:write("@fun: void -> void")
333 ENV
:write("@strategy: direct")
339 if ENV
.lstpos
~= #ENV
.lst
then
340 print("length difference between nasm and jitcs versions ("..N
.."bit)")