Adding copyright notices to most files. Also add readme file, and some
[jitcs.git] / tools / x86_instool.lua
blob88eea0978dc712cab5e1e6b6a64ae82fd3d00482
1 --[[
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.
8 ]]
10 local _setfenv, _ipairs = setfenv, ipairs
11 local _open = io.open
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)
17 local f = _open(n)
18 local ctnt = f:read("*a")
19 f:close()
20 return ctnt
21 end
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
31 N = 32
32 else
33 N = 64
34 end
35 if MODE == "ins2nasm32" or MODE == "ins2nasm64" then
36 ALG = "ins2nasm"
37 nasmfile = p1
38 assert(nasmfile, ".nasm file not given")
39 else
40 ALG = "nasm2as"
41 nasmlstfile, asfile = p1, p2
42 assert(nasmlstfile and asfile, ".lst and .as file not given")
43 end
45 local prog, progcode = ""
46 local progstats = { scopes = {} }
47 local errors = false
48 local lineno = 1
49 for l in _gmatch(readfile(insfile), "([^\r\n]*)[\r\n]") do
50 --print(l)
51 if _match(l, "^|") then
52 if (_match(l, "^|%s*[Ff][Oo][Rr]%s*(%w+)%s*=%s*%[([^]]-)%]%s*[Dd][Oo]%s*$"))
53 then
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*$")
58 if e2 then
59 list2[#list2 + 1] = e2;
60 list3 = (list3 and (list3..", ") or "").._fmt("%q", e2)
61 end
62 end
63 if #list2 > 0 then
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"
70 else
71 errors = true
72 print(lineno .. ": illegal list in for statement")
73 end
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"
81 else
82 errors = true
83 print(lineno .. ": endfor without for scope")
84 end
85 elseif (_match(l,"^|%s*[Ll][Ee][Tt]%s*(%w+)%s*=%s*(.-)%s*$"))
86 then
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)
89 else
90 errors = true
91 print(lineno .. ": illegal commandline "..l)
92 end
93 elseif _match(l,"^%s*$") then
94 -- ignore empty lines
95 else
96 prog = prog .. _fmt("ENV:handleLine(%q, %d)",l, lineno) .. "\n"
97 end
98 lineno = lineno + 1
99 end
100 if (#progstats.scopes > 0) then
101 errors = true
102 print(lineno .. ": not all scopes have been closed")
104 if errors then return end
105 progcode = loadstring(prog)
106 if not progcode then
107 print("cannot compile program")
108 return
110 local ENV = {}
111 function ENV.handleLine(self, line, lineno)
112 local function replacer(expr)
113 local repfnc = loadstring("return "..expr)
114 if not repfnc then
115 errors = true
116 print(lineno .. ": illegal expression "..expr)
117 return "ERROR"
118 else
119 _setfenv(repfnc, self)
120 return repfnc() or ""
123 local function filter(expr)
124 local repfnc = loadstring("return "..expr)
125 if not repfnc then
126 errors = true
127 print(lineno .. ": illegal expression "..expr)
128 return "??"
129 else
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
139 errors = true
140 print(lineno .. ": illegal architecture "..m)
141 return
142 else
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, "[{}]", "")
158 self:write(line)
160 function ENV.handleExpr(self, line, lineno)
161 local function replacer(expr)
162 local repfnc = loadstring("return "..expr)
163 if not repfnc then
164 errors = true
165 print(lineno .. ": illegal expression "..expr)
166 return "ERROR"
167 else
168 _setfenv(repfnc, self)
169 return repfnc() or ""
172 line = _gsub(line, "$(%b())", replacer)
173 return line
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 "")
180 if self.file then
181 self.file:write(line)
182 self.file:write("\n")
183 else
184 print(line)
187 function ENV.X86(Y)
188 return tonumber(Y) == 64 and "64:" or ""
190 function ENV.MSZ(Y)
191 if Y == "DEF" then return "" end
192 return Y
194 function ENV.MVSZ(M)
195 if M == "S" then return "32" end
196 if M == "D" then return "64" end
197 return ""
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)
204 return "ERROR"
206 function ENV.RSZ(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)
213 return "ERROR"
215 function ENV.VSZ(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)
220 return "ERROR"
222 function ENV.VHSZ(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)
227 return "ERROR"
229 function ENV.VOSZ(Y,N)
230 if tonumber(Y) == tonumber(N) then return "" end
231 return tostring(N)
233 function ENV.RSZ2(Y)
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
239 return "ERROR"
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
249 return "ERROR"
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)
297 local result = {}
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])"
302 .. " (%x*)"
303 .. "%s*(.-)%s*$")
304 if opcodes and #opcodes > 0 then result[#result + 1] = opcodes end
306 return result
309 ENV.arch = tostring(N)
310 ENV.ipairs = _ipairs
311 ENV.ENV = ENV
312 _setfenv(progcode, ENV)
313 if ALG == "ins2nasm" then
314 ENV.file = _open(nasmfile, "w")
315 ENV.tgt = "nasm"
316 ENV:write(" bits " .. ENV.arch)
317 if N == 32 then
318 ENV:write("%define RAX EAX")
319 ENV:write("%define RBX EBX")
320 ENV:write("%define RCX ECX")
321 ENV:write("%define RDX EDX")
323 progcode()
324 ENV:write(" ret")
325 ENV.file:close()
326 elseif ALG == "nasm2as" then
327 local lst = readnasmlist(nasmlstfile)
329 ENV.file = _open(asfile, "w")
330 ENV.tgt = "jitcs"
331 ENV:write("<== test")
332 ENV:write("@fun: void -> void")
333 ENV:write("@strategy: direct")
334 ENV:write(":entry")
335 ENV.lst = lst
336 ENV.lstpos = 0
337 progcode()
338 ENV:write(" ret |")
339 if ENV.lstpos ~= #ENV.lst then
340 print("length difference between nasm and jitcs versions ("..N.."bit)")
342 ENV.lst = nil
343 ENV.file:close()