xref: /PHP-8.0/ext/opcache/jit/dynasm/dynasm.lua (revision afdaa911)
1------------------------------------------------------------------------------
2-- DynASM. A dynamic assembler for code generation engines.
3-- Originally designed and implemented for LuaJIT.
4--
5-- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
6-- See below for full copyright notice.
7------------------------------------------------------------------------------
8
9-- Application information.
10local _info = {
11  name =	"DynASM",
12  description =	"A dynamic assembler for code generation engines",
13  version =	"1.4.0",
14  vernum =	 10400,
15  release =	"2015-10-18",
16  author =	"Mike Pall",
17  url =		"http://luajit.org/dynasm.html",
18  license =	"MIT",
19  copyright =	[[
20Copyright (C) 2005-2016 Mike Pall. All rights reserved.
21
22Permission is hereby granted, free of charge, to any person obtaining
23a copy of this software and associated documentation files (the
24"Software"), to deal in the Software without restriction, including
25without limitation the rights to use, copy, modify, merge, publish,
26distribute, sublicense, and/or sell copies of the Software, and to
27permit persons to whom the Software is furnished to do so, subject to
28the following conditions:
29
30The above copyright notice and this permission notice shall be
31included in all copies or substantial portions of the Software.
32
33THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
36IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
37CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
38TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40
41[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
42]],
43}
44
45-- Cache library functions.
46local type, pairs, ipairs = type, pairs, ipairs
47local pcall, error, assert = pcall, error, assert
48local _s = string
49local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub
50local format, rep, upper = _s.format, _s.rep, _s.upper
51local _t = table
52local insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sort
53local exit = os.exit
54local io = io
55local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr
56
57------------------------------------------------------------------------------
58
59-- Program options.
60local g_opt = {}
61
62-- Global state for current file.
63local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch
64local g_errcount = 0
65
66-- Write buffer for output file.
67local g_wbuffer, g_capbuffer
68
69------------------------------------------------------------------------------
70
71-- Write an output line (or callback function) to the buffer.
72local function wline(line, needindent)
73  local buf = g_capbuffer or g_wbuffer
74  buf[#buf+1] = needindent and g_indent..line or line
75  g_synclineno = g_synclineno + 1
76end
77
78-- Write assembler line as a comment, if requested.
79local function wcomment(aline)
80  if g_opt.comment then
81    wline(g_opt.comment..aline..g_opt.endcomment, true)
82  end
83end
84
85-- Resync CPP line numbers.
86local function wsync()
87  if g_synclineno ~= g_lineno and g_opt.cpp then
88    wline("#line "..g_lineno..' "'..g_fname..'"')
89    g_synclineno = g_lineno
90  end
91end
92
93-- Dummy action flush function. Replaced with arch-specific function later.
94local function wflush(term)
95end
96
97-- Dump all buffered output lines.
98local function wdumplines(out, buf)
99  for _,line in ipairs(buf) do
100    if type(line) == "string" then
101      assert(out:write(line, "\n"))
102    else
103      -- Special callback to dynamically insert lines after end of processing.
104      line(out)
105    end
106  end
107end
108
109------------------------------------------------------------------------------
110
111-- Emit an error. Processing continues with next statement.
112local function werror(msg)
113  error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0)
114end
115
116-- Emit a fatal error. Processing stops.
117local function wfatal(msg)
118  g_errcount = "fatal"
119  werror(msg)
120end
121
122-- Print a warning. Processing continues.
123local function wwarn(msg)
124  stderr:write(format("%s:%s: warning: %s:\n%s\n",
125    g_fname, g_lineno, msg, g_curline))
126end
127
128-- Print caught error message. But suppress excessive errors.
129local function wprinterr(...)
130  if type(g_errcount) == "number" then
131    -- Regular error.
132    g_errcount = g_errcount + 1
133    if g_errcount < 21 then -- Seems to be a reasonable limit.
134      stderr:write(...)
135    elseif g_errcount == 21 then
136      stderr:write(g_fname,
137	":*: warning: too many errors (suppressed further messages).\n")
138    end
139  else
140    -- Fatal error.
141    stderr:write(...)
142    return true -- Stop processing.
143  end
144end
145
146------------------------------------------------------------------------------
147
148-- Map holding all option handlers.
149local opt_map = {}
150local opt_current
151
152-- Print error and exit with error status.
153local function opterror(...)
154  stderr:write("dynasm.lua: ERROR: ", ...)
155  stderr:write("\n")
156  exit(1)
157end
158
159-- Get option parameter.
160local function optparam(args)
161  local argn = args.argn
162  local p = args[argn]
163  if not p then
164    opterror("missing parameter for option `", opt_current, "'.")
165  end
166  args.argn = argn + 1
167  return p
168end
169
170------------------------------------------------------------------------------
171
172-- Core pseudo-opcodes.
173local map_coreop = {}
174-- Dummy opcode map. Replaced by arch-specific map.
175local map_op = {}
176
177-- Forward declarations.
178local dostmt
179local readfile
180
181------------------------------------------------------------------------------
182
183-- Map for defines (initially empty, chains to arch-specific map).
184local map_def = {}
185
186-- Pseudo-opcode to define a substitution.
187map_coreop[".define_2"] = function(params, nparams)
188  if not params then return nparams == 1 and "name" or "name, subst" end
189  local name, def = params[1], params[2] or "1"
190  if not match(name, "^[%a_][%w_]*$") then werror("bad or duplicate define") end
191  map_def[name] = def
192end
193map_coreop[".define_1"] = map_coreop[".define_2"]
194
195-- Define a substitution on the command line.
196function opt_map.D(args)
197  local namesubst = optparam(args)
198  local name, subst = match(namesubst, "^([%a_][%w_]*)=(.*)$")
199  if name then
200    map_def[name] = subst
201  elseif match(namesubst, "^[%a_][%w_]*$") then
202    map_def[namesubst] = "1"
203  else
204    opterror("bad define")
205  end
206end
207
208-- Undefine a substitution on the command line.
209function opt_map.U(args)
210  local name = optparam(args)
211  if match(name, "^[%a_][%w_]*$") then
212    map_def[name] = nil
213  else
214    opterror("bad define")
215  end
216end
217
218-- Helper for definesubst.
219local gotsubst
220
221local function definesubst_one(word)
222  local subst = map_def[word]
223  if subst then gotsubst = word; return subst else return word end
224end
225
226-- Iteratively substitute defines.
227local function definesubst(stmt)
228  -- Limit number of iterations.
229  for i=1,100 do
230    gotsubst = false
231    stmt = gsub(stmt, "#?[%w_]+", definesubst_one)
232    if not gotsubst then break end
233  end
234  if gotsubst then wfatal("recursive define involving `"..gotsubst.."'") end
235  return stmt
236end
237
238-- Dump all defines.
239local function dumpdefines(out, lvl)
240  local t = {}
241  for name in pairs(map_def) do
242    t[#t+1] = name
243  end
244  sort(t)
245  out:write("Defines:\n")
246  for _,name in ipairs(t) do
247    local subst = map_def[name]
248    if g_arch then subst = g_arch.revdef(subst) end
249    out:write(format("  %-20s %s\n", name, subst))
250  end
251  out:write("\n")
252end
253
254------------------------------------------------------------------------------
255
256-- Support variables for conditional assembly.
257local condlevel = 0
258local condstack = {}
259
260-- Evaluate condition with a Lua expression. Substitutions already performed.
261local function cond_eval(cond)
262  local func, err
263  if setfenv then
264    func, err = loadstring("return "..cond, "=expr")
265  else
266    -- No globals. All unknown identifiers evaluate to nil.
267    func, err = load("return "..cond, "=expr", "t", {})
268  end
269  if func then
270    if setfenv then
271      setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil.
272    end
273    local ok, res = pcall(func)
274    if ok then
275      if res == 0 then return false end -- Oh well.
276      return not not res
277    end
278    err = res
279  end
280  wfatal("bad condition: "..err)
281end
282
283-- Skip statements until next conditional pseudo-opcode at the same level.
284local function stmtskip()
285  local dostmt_save = dostmt
286  local lvl = 0
287  dostmt = function(stmt)
288    local op = match(stmt, "^%s*(%S+)")
289    if op == ".if" then
290      lvl = lvl + 1
291    elseif lvl ~= 0 then
292      if op == ".endif" then lvl = lvl - 1 end
293    elseif op == ".elif" or op == ".else" or op == ".endif" then
294      dostmt = dostmt_save
295      dostmt(stmt)
296    end
297  end
298end
299
300-- Pseudo-opcodes for conditional assembly.
301map_coreop[".if_1"] = function(params)
302  if not params then return "condition" end
303  local lvl = condlevel + 1
304  local res = cond_eval(params[1])
305  condlevel = lvl
306  condstack[lvl] = res
307  if not res then stmtskip() end
308end
309
310map_coreop[".elif_1"] = function(params)
311  if not params then return "condition" end
312  if condlevel == 0 then wfatal(".elif without .if") end
313  local lvl = condlevel
314  local res = condstack[lvl]
315  if res then
316    if res == "else" then wfatal(".elif after .else") end
317  else
318    res = cond_eval(params[1])
319    if res then
320      condstack[lvl] = res
321      return
322    end
323  end
324  stmtskip()
325end
326
327map_coreop[".else_0"] = function(params)
328  if condlevel == 0 then wfatal(".else without .if") end
329  local lvl = condlevel
330  local res = condstack[lvl]
331  condstack[lvl] = "else"
332  if res then
333    if res == "else" then wfatal(".else after .else") end
334    stmtskip()
335  end
336end
337
338map_coreop[".endif_0"] = function(params)
339  local lvl = condlevel
340  if lvl == 0 then wfatal(".endif without .if") end
341  condlevel = lvl - 1
342end
343
344-- Check for unfinished conditionals.
345local function checkconds()
346  if g_errcount ~= "fatal" and condlevel ~= 0 then
347    wprinterr(g_fname, ":*: error: unbalanced conditional\n")
348  end
349end
350
351------------------------------------------------------------------------------
352
353-- Search for a file in the given path and open it for reading.
354local function pathopen(path, name)
355  local dirsep = package and match(package.path, "\\") and "\\" or "/"
356  for _,p in ipairs(path) do
357    local fullname = p == "" and name or p..dirsep..name
358    local fin = io.open(fullname, "r")
359    if fin then
360      g_fname = fullname
361      return fin
362    end
363  end
364end
365
366-- Include a file.
367map_coreop[".include_1"] = function(params)
368  if not params then return "filename" end
369  local name = params[1]
370  -- Save state. Ugly, I know. but upvalues are fast.
371  local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent
372  -- Read the included file.
373  local fatal = readfile(pathopen(g_opt.include, name) or
374			 wfatal("include file `"..name.."' not found"))
375  -- Restore state.
376  g_synclineno = -1
377  g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi
378  if fatal then wfatal("in include file") end
379end
380
381-- Make .include and conditionals initially available, too.
382map_op[".include_1"] = map_coreop[".include_1"]
383map_op[".if_1"] = map_coreop[".if_1"]
384map_op[".elif_1"] = map_coreop[".elif_1"]
385map_op[".else_0"] = map_coreop[".else_0"]
386map_op[".endif_0"] = map_coreop[".endif_0"]
387
388------------------------------------------------------------------------------
389
390-- Support variables for macros.
391local mac_capture, mac_lineno, mac_name
392local mac_active = {}
393local mac_list = {}
394
395-- Pseudo-opcode to define a macro.
396map_coreop[".macro_*"] = function(mparams)
397  if not mparams then return "name [, params...]" end
398  -- Split off and validate macro name.
399  local name = remove(mparams, 1)
400  if not name then werror("missing macro name") end
401  if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]*$")) then
402    wfatal("bad macro name `"..name.."'")
403  end
404  -- Validate macro parameter names.
405  local mdup = {}
406  for _,mp in ipairs(mparams) do
407    if not match(mp, "^[%a_][%w_]*$") then
408      wfatal("bad macro parameter name `"..mp.."'")
409    end
410    if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end
411    mdup[mp] = true
412  end
413  -- Check for duplicate or recursive macro definitions.
414  local opname = name.."_"..#mparams
415  if map_op[opname] or map_op[name.."_*"] then
416    wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)")
417  end
418  if mac_capture then wfatal("recursive macro definition") end
419
420  -- Enable statement capture.
421  local lines = {}
422  mac_lineno = g_lineno
423  mac_name = name
424  mac_capture = function(stmt) -- Statement capture function.
425    -- Stop macro definition with .endmacro pseudo-opcode.
426    if not match(stmt, "^%s*.endmacro%s*$") then
427      lines[#lines+1] = stmt
428      return
429    end
430    mac_capture = nil
431    mac_lineno = nil
432    mac_name = nil
433    mac_list[#mac_list+1] = opname
434    -- Add macro-op definition.
435    map_op[opname] = function(params)
436      if not params then return mparams, lines end
437      -- Protect against recursive macro invocation.
438      if mac_active[opname] then wfatal("recursive macro invocation") end
439      mac_active[opname] = true
440      -- Setup substitution map.
441      local subst = {}
442      for i,mp in ipairs(mparams) do subst[mp] = params[i] end
443      local mcom
444      if g_opt.maccomment and g_opt.comment then
445	mcom = " MACRO "..name.." ("..#mparams..")"
446	wcomment("{"..mcom)
447      end
448      -- Loop through all captured statements
449      for _,stmt in ipairs(lines) do
450	-- Substitute macro parameters.
451	local st = gsub(stmt, "[%w_]+", subst)
452	st = definesubst(st)
453	st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b.
454	if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end
455	-- Emit statement. Use a protected call for better diagnostics.
456	local ok, err = pcall(dostmt, st)
457	if not ok then
458	  -- Add the captured statement to the error.
459	  wprinterr(err, "\n", g_indent, "|  ", stmt,
460		    "\t[MACRO ", name, " (", #mparams, ")]\n")
461	end
462      end
463      if mcom then wcomment("}"..mcom) end
464      mac_active[opname] = nil
465    end
466  end
467end
468
469-- An .endmacro pseudo-opcode outside of a macro definition is an error.
470map_coreop[".endmacro_0"] = function(params)
471  wfatal(".endmacro without .macro")
472end
473
474-- Dump all macros and their contents (with -PP only).
475local function dumpmacros(out, lvl)
476  sort(mac_list)
477  out:write("Macros:\n")
478  for _,opname in ipairs(mac_list) do
479    local name = sub(opname, 1, -3)
480    local params, lines = map_op[opname]()
481    out:write(format("  %-20s %s\n", name, concat(params, ", ")))
482    if lvl > 1 then
483      for _,line in ipairs(lines) do
484	out:write("  |", line, "\n")
485      end
486      out:write("\n")
487    end
488  end
489  out:write("\n")
490end
491
492-- Check for unfinished macro definitions.
493local function checkmacros()
494  if mac_capture then
495    wprinterr(g_fname, ":", mac_lineno,
496	      ": error: unfinished .macro `", mac_name ,"'\n")
497  end
498end
499
500------------------------------------------------------------------------------
501
502-- Support variables for captures.
503local cap_lineno, cap_name
504local cap_buffers = {}
505local cap_used = {}
506
507-- Start a capture.
508map_coreop[".capture_1"] = function(params)
509  if not params then return "name" end
510  wflush()
511  local name = params[1]
512  if not match(name, "^[%a_][%w_]*$") then
513    wfatal("bad capture name `"..name.."'")
514  end
515  if cap_name then
516    wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno)
517  end
518  cap_name = name
519  cap_lineno = g_lineno
520  -- Create or continue a capture buffer and start the output line capture.
521  local buf = cap_buffers[name]
522  if not buf then buf = {}; cap_buffers[name] = buf end
523  g_capbuffer = buf
524  g_synclineno = 0
525end
526
527-- Stop a capture.
528map_coreop[".endcapture_0"] = function(params)
529  wflush()
530  if not cap_name then wfatal(".endcapture without a valid .capture") end
531  cap_name = nil
532  cap_lineno = nil
533  g_capbuffer = nil
534  g_synclineno = 0
535end
536
537-- Dump a capture buffer.
538map_coreop[".dumpcapture_1"] = function(params)
539  if not params then return "name" end
540  wflush()
541  local name = params[1]
542  if not match(name, "^[%a_][%w_]*$") then
543    wfatal("bad capture name `"..name.."'")
544  end
545  cap_used[name] = true
546  wline(function(out)
547    local buf = cap_buffers[name]
548    if buf then wdumplines(out, buf) end
549  end)
550  g_synclineno = 0
551end
552
553-- Dump all captures and their buffers (with -PP only).
554local function dumpcaptures(out, lvl)
555  out:write("Captures:\n")
556  for name,buf in pairs(cap_buffers) do
557    out:write(format("  %-20s %4s)\n", name, "("..#buf))
558    if lvl > 1 then
559      local bar = rep("=", 76)
560      out:write("  ", bar, "\n")
561      for _,line in ipairs(buf) do
562	out:write("  ", line, "\n")
563      end
564      out:write("  ", bar, "\n\n")
565    end
566  end
567  out:write("\n")
568end
569
570-- Check for unfinished or unused captures.
571local function checkcaptures()
572  if cap_name then
573    wprinterr(g_fname, ":", cap_lineno,
574	      ": error: unfinished .capture `", cap_name,"'\n")
575    return
576  end
577  for name in pairs(cap_buffers) do
578    if not cap_used[name] then
579      wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n")
580    end
581  end
582end
583
584------------------------------------------------------------------------------
585
586-- Sections names.
587local map_sections = {}
588
589-- Pseudo-opcode to define code sections.
590-- TODO: Data sections, BSS sections. Needs extra C code and API.
591map_coreop[".section_*"] = function(params)
592  if not params then return "name..." end
593  if #map_sections > 0 then werror("duplicate section definition") end
594  wflush()
595  for sn,name in ipairs(params) do
596    local opname = "."..name.."_0"
597    if not match(name, "^[%a][%w_]*$") or
598       map_op[opname] or map_op["."..name.."_*"] then
599      werror("bad section name `"..name.."'")
600    end
601    map_sections[#map_sections+1] = name
602    wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1))
603    map_op[opname] = function(params) g_arch.section(sn-1) end
604  end
605  wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))
606end
607
608-- Dump all sections.
609local function dumpsections(out, lvl)
610  out:write("Sections:\n")
611  for _,name in ipairs(map_sections) do
612    out:write(format("  %s\n", name))
613  end
614  out:write("\n")
615end
616
617------------------------------------------------------------------------------
618
619-- Replacement for customized Lua, which lacks the package library.
620local prefix = ""
621if not require then
622  function require(name)
623    local fp = assert(io.open(prefix..name..".lua"))
624    local s = fp:read("*a")
625    assert(fp:close())
626    return assert(loadstring(s, "@"..name..".lua"))()
627  end
628end
629
630-- Load architecture-specific module.
631local function loadarch(arch)
632  if not match(arch, "^[%w_]+$") then return "bad arch name" end
633  local ok, m_arch = pcall(require, "dasm_"..arch)
634  if not ok then return "cannot load module: "..m_arch end
635  g_arch = m_arch
636  wflush = m_arch.passcb(wline, werror, wfatal, wwarn)
637  m_arch.setup(arch, g_opt)
638  map_op, map_def = m_arch.mergemaps(map_coreop, map_def)
639end
640
641-- Dump architecture description.
642function opt_map.dumparch(args)
643  local name = optparam(args)
644  if not g_arch then
645    local err = loadarch(name)
646    if err then opterror(err) end
647  end
648
649  local t = {}
650  for name in pairs(map_coreop) do t[#t+1] = name end
651  for name in pairs(map_op) do t[#t+1] = name end
652  sort(t)
653
654  local out = stdout
655  local _arch = g_arch._info
656  out:write(format("%s version %s, released %s, %s\n",
657    _info.name, _info.version, _info.release, _info.url))
658  g_arch.dumparch(out)
659
660  local pseudo = true
661  out:write("Pseudo-Opcodes:\n")
662  for _,sname in ipairs(t) do
663    local name, nparam = match(sname, "^(.+)_([0-9%*])$")
664    if name then
665      if pseudo and sub(name, 1, 1) ~= "." then
666	out:write("\nOpcodes:\n")
667	pseudo = false
668      end
669      local f = map_op[sname]
670      local s
671      if nparam ~= "*" then nparam = nparam + 0 end
672      if nparam == 0 then
673	s = ""
674      elseif type(f) == "string" then
675	s = map_op[".template__"](nil, f, nparam)
676      else
677	s = f(nil, nparam)
678      end
679      if type(s) == "table" then
680	for _,s2 in ipairs(s) do
681	  out:write(format("  %-12s %s\n", name, s2))
682	end
683      else
684	out:write(format("  %-12s %s\n", name, s))
685      end
686    end
687  end
688  out:write("\n")
689  exit(0)
690end
691
692-- Pseudo-opcode to set the architecture.
693-- Only initially available (map_op is replaced when called).
694map_op[".arch_1"] = function(params)
695  if not params then return "name" end
696  local err = loadarch(params[1])
697  if err then wfatal(err) end
698  wline(format("#if DASM_VERSION != %d", _info.vernum))
699  wline('#error "Version mismatch between DynASM and included encoding engine"')
700  wline("#endif")
701end
702
703-- Dummy .arch pseudo-opcode to improve the error report.
704map_coreop[".arch_1"] = function(params)
705  if not params then return "name" end
706  wfatal("duplicate .arch statement")
707end
708
709------------------------------------------------------------------------------
710
711-- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'.
712map_coreop[".nop_*"] = function(params)
713  if not params then return "[ignored...]" end
714end
715
716-- Pseudo-opcodes to raise errors.
717map_coreop[".error_1"] = function(params)
718  if not params then return "message" end
719  werror(params[1])
720end
721
722map_coreop[".fatal_1"] = function(params)
723  if not params then return "message" end
724  wfatal(params[1])
725end
726
727-- Dump all user defined elements.
728local function dumpdef(out)
729  local lvl = g_opt.dumpdef
730  if lvl == 0 then return end
731  dumpsections(out, lvl)
732  dumpdefines(out, lvl)
733  if g_arch then g_arch.dumpdef(out, lvl) end
734  dumpmacros(out, lvl)
735  dumpcaptures(out, lvl)
736end
737
738------------------------------------------------------------------------------
739
740-- Helper for splitstmt.
741local splitlvl
742
743local function splitstmt_one(c)
744  if c == "(" then
745    splitlvl = ")"..splitlvl
746  elseif c == "[" then
747    splitlvl = "]"..splitlvl
748  elseif c == "{" then
749    splitlvl = "}"..splitlvl
750  elseif c == ")" or c == "]" or c == "}" then
751    if sub(splitlvl, 1, 1) ~= c then werror("unbalanced (), [] or {}") end
752    splitlvl = sub(splitlvl, 2)
753  elseif splitlvl == "" then
754    return " \0 "
755  end
756  return c
757end
758
759-- Split statement into (pseudo-)opcode and params.
760local function splitstmt(stmt)
761  -- Convert label with trailing-colon into .label statement.
762  local label = match(stmt, "^%s*(.+):%s*$")
763  if label then return ".label", {label} end
764
765  -- Split at commas and equal signs, but obey parentheses and brackets.
766  splitlvl = ""
767  stmt = gsub(stmt, "[,%(%)%[%]{}]", splitstmt_one)
768  if splitlvl ~= "" then werror("unbalanced () or []") end
769
770  -- Split off opcode.
771  local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$")
772  if not op then werror("bad statement syntax") end
773
774  -- Split parameters.
775  local params = {}
776  for p in gmatch(other, "%s*(%Z+)%z?") do
777    params[#params+1] = gsub(p, "%s+$", "")
778  end
779  if #params > 16 then werror("too many parameters") end
780
781  params.op = op
782  return op, params
783end
784
785-- Process a single statement.
786dostmt = function(stmt)
787  -- Ignore empty statements.
788  if match(stmt, "^%s*$") then return end
789
790  -- Capture macro defs before substitution.
791  if mac_capture then return mac_capture(stmt) end
792  stmt = definesubst(stmt)
793
794  -- Emit C code without parsing the line.
795  if sub(stmt, 1, 1) == "|" then
796    local tail = sub(stmt, 2)
797    wflush()
798    if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end
799    return
800  end
801
802  -- Split into (pseudo-)opcode and params.
803  local op, params = splitstmt(stmt)
804
805  -- Get opcode handler (matching # of parameters or generic handler).
806  local f = map_op[op.."_"..#params] or map_op[op.."_*"]
807  if not f then
808    if not g_arch then wfatal("first statement must be .arch") end
809    -- Improve error report.
810    for i=0,9 do
811      if map_op[op.."_"..i] then
812	werror("wrong number of parameters for `"..op.."'")
813      end
814    end
815    werror("unknown statement `"..op.."'")
816  end
817
818  -- Call opcode handler or special handler for template strings.
819  if type(f) == "string" then
820    map_op[".template__"](params, f)
821  else
822    f(params)
823  end
824end
825
826-- Process a single line.
827local function doline(line)
828  if g_opt.flushline then wflush() end
829
830  -- Assembler line?
831  local indent, aline = match(line, "^(%s*)%|(.*)$")
832  if not aline then
833    -- No, plain C code line, need to flush first.
834    wflush()
835    wsync()
836    wline(line, false)
837    return
838  end
839
840  g_indent = indent -- Remember current line indentation.
841
842  -- Emit C code (even from macros). Avoids echo and line parsing.
843  if sub(aline, 1, 1) == "|" then
844    if not mac_capture then
845      wsync()
846    elseif g_opt.comment then
847      wsync()
848      wcomment(aline)
849    end
850    dostmt(aline)
851    return
852  end
853
854  -- Echo assembler line as a comment.
855  if g_opt.comment then
856    wsync()
857    wcomment(aline)
858  end
859
860  -- Strip assembler comments.
861  aline = gsub(aline, "//.*$", "")
862
863  -- Split line into statements at semicolons.
864  if match(aline, ";") then
865    for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end
866  else
867    dostmt(aline)
868  end
869end
870
871------------------------------------------------------------------------------
872
873-- Write DynASM header.
874local function dasmhead(out)
875  out:write(format([[
876/*
877** This file has been pre-processed with DynASM.
878** %s
879** DynASM version %s, DynASM %s version %s
880** DO NOT EDIT! The original file is in "%s".
881*/
882
883]], _info.url,
884    _info.version, g_arch._info.arch, g_arch._info.version,
885    g_fname))
886end
887
888-- Read input file.
889readfile = function(fin)
890  g_indent = ""
891  g_lineno = 0
892  g_synclineno = -1
893
894  -- Process all lines.
895  for line in fin:lines() do
896    g_lineno = g_lineno + 1
897    g_curline = line
898    local ok, err = pcall(doline, line)
899    if not ok and wprinterr(err, "\n") then return true end
900  end
901  wflush()
902
903  -- Close input file.
904  assert(fin == stdin or fin:close())
905end
906
907-- Write output file.
908local function writefile(outfile)
909  local fout
910
911  -- Open output file.
912  if outfile == nil or outfile == "-" then
913    fout = stdout
914  else
915    fout = assert(io.open(outfile, "w"))
916  end
917
918  -- Write all buffered lines
919  wdumplines(fout, g_wbuffer)
920
921  -- Close output file.
922  assert(fout == stdout or fout:close())
923
924  -- Optionally dump definitions.
925  dumpdef(fout == stdout and stderr or stdout)
926end
927
928-- Translate an input file to an output file.
929local function translate(infile, outfile)
930  g_wbuffer = {}
931  g_indent = ""
932  g_lineno = 0
933  g_synclineno = -1
934
935  -- Put header.
936  wline(dasmhead)
937
938  -- Read input file.
939  local fin
940  if infile == "-" then
941    g_fname = "(stdin)"
942    fin = stdin
943  else
944    g_fname = infile
945    fin = assert(io.open(infile, "r"))
946  end
947  readfile(fin)
948
949  -- Check for errors.
950  if not g_arch then
951    wprinterr(g_fname, ":*: error: missing .arch directive\n")
952  end
953  checkconds()
954  checkmacros()
955  checkcaptures()
956
957  if g_errcount ~= 0 then
958    stderr:write(g_fname, ":*: info: ", g_errcount, " error",
959      (type(g_errcount) == "number" and g_errcount > 1) and "s" or "",
960      " in input file -- no output file generated.\n")
961    dumpdef(stderr)
962    exit(1)
963  end
964
965  -- Write output file.
966  writefile(outfile)
967end
968
969------------------------------------------------------------------------------
970
971-- Print help text.
972function opt_map.help()
973  stdout:write("DynASM -- ", _info.description, ".\n")
974  stdout:write("DynASM ", _info.version, " ", _info.release, "  ", _info.url, "\n")
975  stdout:write[[
976
977Usage: dynasm [OPTION]... INFILE.dasc|-
978
979  -h, --help           Display this help text.
980  -V, --version        Display version and copyright information.
981
982  -o, --outfile FILE   Output file name (default is stdout).
983  -I, --include DIR    Add directory to the include search path.
984
985  -c, --ccomment       Use /* */ comments for assembler lines.
986  -C, --cppcomment     Use // comments for assembler lines (default).
987  -N, --nocomment      Suppress assembler lines in output.
988  -M, --maccomment     Show macro expansions as comments (default off).
989
990  -L, --nolineno       Suppress CPP line number information in output.
991  -F, --flushline      Flush action list for every line.
992
993  -D NAME[=SUBST]      Define a substitution.
994  -U NAME              Undefine a substitution.
995
996  -P, --dumpdef        Dump defines, macros, etc. Repeat for more output.
997  -A, --dumparch ARCH  Load architecture ARCH and dump description.
998]]
999  exit(0)
1000end
1001
1002-- Print version information.
1003function opt_map.version()
1004  stdout:write(format("%s version %s, released %s\n%s\n\n%s",
1005    _info.name, _info.version, _info.release, _info.url, _info.copyright))
1006  exit(0)
1007end
1008
1009-- Misc. options.
1010function opt_map.outfile(args) g_opt.outfile = optparam(args) end
1011function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end
1012function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end
1013function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end
1014function opt_map.nocomment() g_opt.comment = false end
1015function opt_map.maccomment() g_opt.maccomment = true end
1016function opt_map.nolineno() g_opt.cpp = false end
1017function opt_map.flushline() g_opt.flushline = true end
1018function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end
1019
1020------------------------------------------------------------------------------
1021
1022-- Short aliases for long options.
1023local opt_alias = {
1024  h = "help", ["?"] = "help", V = "version",
1025  o = "outfile", I = "include",
1026  c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment",
1027  L = "nolineno", F = "flushline",
1028  P = "dumpdef", A = "dumparch",
1029}
1030
1031-- Parse single option.
1032local function parseopt(opt, args)
1033  opt_current = #opt == 1 and "-"..opt or "--"..opt
1034  local f = opt_map[opt] or opt_map[opt_alias[opt]]
1035  if not f then
1036    opterror("unrecognized option `", opt_current, "'. Try `--help'.\n")
1037  end
1038  f(args)
1039end
1040
1041-- Parse arguments.
1042local function parseargs(args)
1043  -- Default options.
1044  g_opt.comment = "//|"
1045  g_opt.endcomment = ""
1046  g_opt.cpp = true
1047  g_opt.dumpdef = 0
1048  g_opt.include = { "" }
1049
1050  -- Process all option arguments.
1051  args.argn = 1
1052  repeat
1053    local a = args[args.argn]
1054    if not a then break end
1055    local lopt, opt = match(a, "^%-(%-?)(.+)")
1056    if not opt then break end
1057    args.argn = args.argn + 1
1058    if lopt == "" then
1059      -- Loop through short options.
1060      for o in gmatch(opt, ".") do parseopt(o, args) end
1061    else
1062      -- Long option.
1063      parseopt(opt, args)
1064    end
1065  until false
1066
1067  -- Check for proper number of arguments.
1068  local nargs = #args - args.argn + 1
1069  if nargs ~= 1 then
1070    if nargs == 0 then
1071      if g_opt.dumpdef > 0 then return dumpdef(stdout) end
1072    end
1073    opt_map.help()
1074  end
1075
1076  -- Translate a single input file to a single output file
1077  -- TODO: Handle multiple files?
1078  translate(args[args.argn], g_opt.outfile)
1079end
1080
1081------------------------------------------------------------------------------
1082
1083-- Add the directory dynasm.lua resides in to the Lua module search path.
1084local arg = arg
1085if arg and arg[0] then
1086  prefix = match(arg[0], "^(.*[/\\])")
1087  if package and prefix then package.path = prefix.."?.lua;"..package.path end
1088end
1089
1090-- Start DynASM.
1091parseargs{...}
1092
1093------------------------------------------------------------------------------
1094
1095