Fixed bug in calculation of use/def classes. Still not tested.
[jitcs.git] / tools / x86_instool.lua
blob088a361ccf520e60f00b1d3fee27385b9c4a29f3
1 local _setfenv, _ipairs = setfenv, ipairs
2 local _open = io.open
3 local _match, _gmatch, _fmt = string.match, string.gmatch, string.format
4 local _gsub, _sub = string.gsub, string.sub
5 local _exec = os.execute
7 local function readfile(n)
8 local f = _open(n)
9 local ctnt = f:read("*a")
10 f:close()
11 return ctnt
12 end
14 local validModes = {ins2nasm32 = true, ins2nasm64 = true, nasm2as32 = true, nasm2as64 = true}
16 local MODE, insfile, p1, p2 = ...
17 MODE = string.lower(MODE or "")
18 assert(validModes[MODE], "mode missing")
19 assert(insfile, "insfile missing")
20 local nasmfile, nasmlstfile, asfile
21 if MODE == "ins2nasm32" or MODE == "nasm2as32" then
22 N = 32
23 else
24 N = 64
25 end
26 if MODE == "ins2nasm32" or MODE == "ins2nasm64" then
27 ALG = "ins2nasm"
28 nasmfile = p1
29 assert(nasmfile, ".nasm file not given")
30 else
31 ALG = "nasm2as"
32 nasmlstfile, asfile = p1, p2
33 assert(nasmlstfile and asfile, ".lst and .as file not given")
34 end
36 local prog, progcode = ""
37 local progstats = { scopes = {} }
38 local errors = false
39 local lineno = 1
40 for l in _gmatch(readfile(insfile), "([^\r\n]*)[\r\n]") do
41 --print(l)
42 if _match(l, "^|") then
43 if (_match(l, "^|%s*[Ff][Oo][Rr]%s*(%w+)%s*=%s*%[([^]]-)%]%s*[Dd][Oo]%s*$"))
44 then
45 local var, list = _match(l, "^|%s*[Ff][Oo][Rr]%s*(%w+)%s*=%s*%[([^]]-)%]%s*[Dd][Oo]%s*$")
46 local list2, list3 = {}, nil
47 for e in _gmatch(list, "([^,]+)") do
48 local e2 = _match(e, "^%s*(%S+)%s*$")
49 if e2 then
50 list2[#list2 + 1] = e2;
51 list3 = (list3 and (list3..", ") or "").._fmt("%q", e2)
52 end
53 end
54 if #list2 > 0 then
55 local sn = #progstats.scopes
56 progstats.scopes[sn+1] = {type = "for", start = lineno,
57 var = var, list = list2}
59 prog = prog .. "for k"..sn..",v"..sn.." in ipairs({"..list3.."}) do\n"
60 prog = prog .. " ENV."..var.." = v"..sn.."\n"
61 else
62 errors = true
63 print(lineno .. ": illegal list in for statement")
64 end
67 elseif (_match(l,"^|%s*[Ee][Nn][Dd][Ff][Oo][Rr]%s*$")) then
68 local sn = #progstats.scopes
69 if (sn > 0 and progstats.scopes[sn].type == "for") then
70 progstats.scopes[sn] = nil
71 prog = prog .. "end\n"
72 else
73 errors = true
74 print(lineno .. ": endfor without for scope")
75 end
76 else
77 errors = true
78 print(lineno .. ": illegal commandline "..l)
79 end
80 elseif _match(l,"^%s*$") then
81 -- ignore empty lines
82 else
83 prog = prog .. _fmt("ENV:handleLine(%q, %d)",l, lineno) .. "\n"
84 end
85 lineno = lineno + 1
86 end
87 if (#progstats.scopes > 0) then
88 errors = true
89 print(lineno .. ": not all scopes have been closed")
90 end
91 if errors then return end
92 progcode = loadstring(prog)
93 if not progcode then
94 print("cannot compile program")
95 return
96 end
97 local ENV = {}
98 function ENV.handleLine(self, line, lineno)
99 local function replacer(expr)
100 local repfnc = loadstring("return "..expr)
101 if not repfnc then
102 errors = true
103 print(lineno .. ": illegal expression "..expr)
104 return "ERROR"
105 else
106 _setfenv(repfnc, self)
107 return repfnc() or ""
110 local function filter(expr)
111 local repfnc = loadstring("return "..expr)
112 if not repfnc then
113 errors = true
114 print(lineno .. ": illegal expression "..expr)
115 return "??"
116 else
117 _setfenv(repfnc, self)
118 return repfnc() and "" or "??"
121 line = _gsub(line, "$(%b())", replacer)
123 if _match(line, "^(%d+):") then
124 local m = _match(line, "^(%d+):")
125 if m ~= "32" and m ~= "64" then
126 errors = true
127 print(lineno .. ": illegal architecture "..m)
128 return
129 else
130 if self.arch ~= m then return end
131 line = _sub(line, #m + 2)
134 line = _gsub(line, "%?(%b())", filter)
135 if (_sub(line,1,2) == "??") then return end
136 if self.tgt == "nasm" then
137 line = _gsub(line, "%b{}", "")
138 line = _gsub(line, "[<>]", "")
140 if self.tgt == "jitcs" then
141 line = _gsub(line, "%b<>", "")
142 line = _gsub(line, "[{}]", "")
145 self:write(line)
147 function ENV.write(self, line)
148 if self.tgt == "jitcs" and self.lst then
149 self.lstpos = self.lstpos + 1
150 line = line .. " ==> " .. (self.lst[self.lstpos] or "")
152 if self.file then
153 self.file:write(line)
154 self.file:write("\n")
155 else
156 print(line)
159 function ENV.X86(Y)
160 return tonumber(Y) == 64 and "64:" or ""
162 function ENV.MSZ(Y)
163 if Y == "DEF" then return "" end
164 return Y
166 function ENV.MVSZ(M)
167 if M == "S" then return "32" end
168 if M == "D" then return "64" end
169 return ""
171 function ENV.MVHSZ(Y, D)
172 if tonumber(Y) == 128 or tonumber(Y) == 256 or tonumber(Y) == 512 then
173 return tostring(tonumber(Y) / (D and tonumber(D) or 2))
175 print("VSZ error", Y)
176 return "ERROR"
178 function ENV.RSZ(Y)
179 if Y == "DEF" then return "R" end
180 if tonumber(Y) == 8 then return "B" end
181 if tonumber(Y) == 16 then return "H" end
182 if tonumber(Y) == 32 then return "W" end
183 if tonumber(Y) == 64 then return "D" end
184 print("RSZ error", Y)
185 return "ERROR"
187 function ENV.VSZ(Y)
188 if tonumber(Y) == 128 then return "X" end
189 if tonumber(Y) == 256 then return "Y" end
190 if tonumber(Y) == 512 then return "Z" end
191 print("VSZ error", Y)
192 return "ERROR"
194 function ENV.VHSZ(Y)
195 if tonumber(Y) == 128 then return "X" end
196 if tonumber(Y) == 256 then return "X" end
197 if tonumber(Y) == 512 then return "Y" end
198 print("VHSZ error", Y)
199 return "ERROR"
201 function ENV.VOSZ(Y,N)
202 if tonumber(Y) == tonumber(N) then return "" end
203 return tostring(N)
205 function ENV.RSZ2(Y)
206 -- if Y == "DEF" then return "ERROR" end
207 if tonumber(Y) == 8 then return "B" end
208 if tonumber(Y) == 16 then return "W" end
209 if tonumber(Y) == 32 then return "D" end
210 if tonumber(Y) == 64 then return "Q" end
211 return "ERROR"
213 function ENV.NASMSZ(Y)
214 if Y == "DEF" then Y = ENV.arch end
215 if tonumber(Y) == 8 then return "byte" end
216 if tonumber(Y) == 16 then return "word" end
217 if tonumber(Y) == 32 then return "dword" end
218 if tonumber(Y) == 64 then return "qword" end
219 if tonumber(Y) == 128 then return "oword" end
220 if tonumber(Y) == 256 then return "yword" end
221 return "ERROR"
223 function ENV.MAX(A,B)
224 if A == "DEF" then A = ENV.arch end
225 if B == "DEF" then B = ENV.arch end
226 return tonumber(A) > tonumber(B) and A or B
228 ENV.RNAME_Table_R2R = {ECX = "RCX", EAX = "RAX", EBX = "RBX", EDX = "RDX",
229 YMM0 = "XMM0", YMM1 = "XMM1", YMM2 = "XMM2", YMM3 = "XMM3"}
230 ENV.RNAME_Table_R2N = {
231 RAX = {["8"] = "AL", ["16"] = "AX", ["32"] = "EAX", ["64"]= "RAX"},
232 RBX = {["8"] = "BL", ["16"] = "BX", ["32"] = "EBX", ["64"]= "RBX"},
233 RCX = {["8"] = "CL", ["16"] = "CX", ["32"] = "ECX", ["64"]= "RCX"},
234 RDX = {["8"] = "DL", ["16"] = "DX", ["32"] = "EDX", ["64"]= "RDX"},
236 function ENV.RCONST(I, Y)
237 if Y == "DEF" then Y = ENV.arch end
238 return math.mod(I, math.pow(2,math.min(tonumber(Y),32)))
240 function ENV.RNAME(R, SZ)
241 if SZ == "DEF" then SZ = ENV.arch end
242 R = ENV.RNAME_Table_R2R[R] or R
243 return ENV.RNAME_Table_R2N[R][tostring(SZ)] or "ERROR"
245 function ENV.RNAMEDEF(R, SZ)
246 if SZ == "DEF" then SZ = 64 end
247 R = ENV.RNAME_Table_R2R[R] or R
248 return ENV.RNAME_Table_R2N[R][tostring(SZ)] or "ERROR"
250 ENV.VNAME_Table_R2N = {
251 XMM0 = {["64"] = "XMM0", ["128"] = "XMM0", ["256"] = "YMM0", ["512"] = "ZMM0"},
252 XMM1 = {["64"] = "XMM1", ["128"] = "XMM1", ["256"] = "YMM1", ["512"] = "ZMM1"},
253 XMM2 = {["64"] = "XMM2", ["128"] = "XMM2", ["256"] = "YMM2", ["512"] = "ZMM2"},
254 XMM3 = {["64"] = "XMM3", ["128"] = "XMM3", ["256"] = "YMM3", ["512"] = "ZMM3"},
255 XMM4 = {["64"] = "XMM4", ["128"] = "XMM4", ["256"] = "YMM4", ["512"] = "ZMM4"},
257 function ENV.VNAME(R, SZ)
258 if SZ == "DEF" then SZ = ENV.arch end
259 R = ENV.RNAME_Table_R2R[R] or R
260 return ENV.VNAME_Table_R2N[R][tostring(SZ)] or "ERROR"
262 function ENV.VHNAME(R, SZ)
263 if SZ == "DEF" then SZ = ENV.arch end
264 R = ENV.RNAME_Table_R2R[R] or R
265 return ENV.VNAME_Table_R2N[R][tostring(tonumber(SZ)/2)] or "ERROR"
268 local function readnasmlist(v)
269 local result = {}
270 for l in _gmatch(readfile(v), "([^\r\n]*)[\r\n]") do
271 local line, addr, opcodes, cmd = _match(l,
272 "^([ %d][ %d][ %d][ %d][ %d][ %d])"
273 .. " ([ %x][ %x][ %x][ %x][ %x][ %x][ %x][ %x])"
274 .. " (%x*)"
275 .. "%s*(.-)%s*$")
276 if opcodes and #opcodes > 0 then result[#result + 1] = opcodes end
278 return result
281 ENV.arch = tostring(N)
282 ENV.ipairs = _ipairs
283 ENV.ENV = ENV
284 _setfenv(progcode, ENV)
285 if ALG == "ins2nasm" then
286 ENV.file = _open(nasmfile, "w")
287 ENV.tgt = "nasm"
288 ENV:write(" bits " .. ENV.arch)
289 if N == 32 then
290 ENV:write("%define RAX EAX")
291 ENV:write("%define RBX EBX")
292 ENV:write("%define RCX ECX")
293 ENV:write("%define RDX EDX")
295 progcode()
296 ENV:write(" ret")
297 ENV.file:close()
298 elseif ALG == "nasm2as" then
299 local lst = readnasmlist(nasmlstfile)
301 ENV.file = _open(asfile, "w")
302 ENV.tgt = "jitcs"
303 ENV:write("<== test")
304 ENV:write("@fun: void -> void")
305 ENV:write("@strategy: direct")
306 ENV:write(":entry")
307 ENV.lst = lst
308 ENV.lstpos = 0
309 progcode()
310 ENV:write(" ret")
311 if ENV.lstpos ~= #ENV.lst then
312 print("length difference between nasm and jitcs versions ("..N.."bit)")
314 ENV.lst = nil
315 ENV.file:close()