📄 dynasm.lua
字号:
-------------------------------------------------------------------------------- DynASM. A dynamic assembler for code generation engines.-- Originally designed and implemented for LuaJIT.---- Copyright (C) 2005-2008 Mike Pall. All rights reserved.-- See below for full copyright notice.-------------------------------------------------------------------------------- Application information.local _info = { name = "DynASM", description = "A dynamic assembler for code generation engines", version = "1.1.4", vernum = 10104, release = "2008-01-29", author = "Mike Pall", url = "http://luajit.org/dynasm.html", license = "MIT", copyright = [[Copyright (C) 2005-2008 Mike Pall. All rights reserved.Permission is hereby granted, free of charge, to any person obtaininga copy of this software and associated documentation files (the"Software"), to deal in the Software without restriction, includingwithout limitation the rights to use, copy, modify, merge, publish,distribute, sublicense, and/or sell copies of the Software, and topermit persons to whom the Software is furnished to do so, subject tothe following conditions:The above copyright notice and this permission notice shall beincluded in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OFMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANYCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THESOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.[ MIT license: http://www.opensource.org/licenses/mit-license.php ]]],}-- Cache library functions.local type, pairs, ipairs = type, pairs, ipairslocal pcall, error, assert = pcall, error, assertlocal _s = stringlocal sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsublocal format, rep, upper = _s.format, _s.rep, _s.upperlocal _t = tablelocal insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sortlocal exit = os.exitlocal io = iolocal stdin, stdout, stderr = io.stdin, io.stdout, io.stderr-------------------------------------------------------------------------------- Program options.local g_opt = {}-- Global state for current file.local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_archlocal g_errcount = 0-- Write buffer for output file.local g_wbuffer, g_capbuffer-------------------------------------------------------------------------------- Write an output line (or callback function) to the buffer.local function wline(line, needindent) local buf = g_capbuffer or g_wbuffer buf[#buf+1] = needindent and g_indent..line or line g_synclineno = g_synclineno + 1end-- Write assembler line as a comment, if requestd.local function wcomment(aline) if g_opt.comment then wline(g_opt.comment..aline..g_opt.endcomment, true) endend-- Resync CPP line numbers.local function wsync() if g_synclineno ~= g_lineno and g_opt.cpp then wline("# "..g_lineno..' "'..g_fname..'"') g_synclineno = g_lineno endend-- Dummy action flush function. Replaced with arch-specific function later.local function wflush(term)end-- Dump all buffered output lines.local function wdumplines(out, buf) for _,line in ipairs(buf) do if type(line) == "string" then assert(out:write(line, "\n")) else -- Special callback to dynamically insert lines after end of processing. line(out) end endend-------------------------------------------------------------------------------- Emit an error. Processing continues with next statement.local function werror(msg) error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0)end-- Emit a fatal error. Processing stops.local function wfatal(msg) g_errcount = "fatal" werror(msg)end-- Print a warning. Processing continues.local function wwarn(msg) stderr:write(format("%s:%s: warning: %s:\n%s\n", g_fname, g_lineno, msg, g_curline))end-- Print caught error message. But suppress excessive errors.local function wprinterr(...) if type(g_errcount) == "number" then -- Regular error. g_errcount = g_errcount + 1 if g_errcount < 21 then -- Seems to be a reasonable limit. stderr:write(...) elseif g_errcount == 21 then stderr:write(g_fname, ":*: warning: too many errors (suppressed further messages).\n") end else -- Fatal error. stderr:write(...) return true -- Stop processing. endend-------------------------------------------------------------------------------- Map holding all option handlers.local opt_map = {}local opt_current-- Print error and exit with error status.local function opterror(...) stderr:write("dynasm.lua: ERROR: ", ...) stderr:write("\n") exit(1)end-- Get option parameter.local function optparam(args) local argn = args.argn local p = args[argn] if not p then opterror("missing parameter for option `", opt_current, "'.") end args.argn = argn + 1 return pend-------------------------------------------------------------------------------- Core pseudo-opcodes.local map_coreop = {}-- Dummy opcode map. Replaced by arch-specific map.local map_op = {}-- Forward declarations.local dostmtlocal readfile-------------------------------------------------------------------------------- Map for defines (initially empty, chains to arch-specific map).local map_def = {}-- Pseudo-opcode to define a substitution.map_coreop[".define_2"] = function(params, nparams) if not params then return nparams == 1 and "name" or "name, subst" end local name, def = params[1], params[2] or "1" if not match(name, "^[%a_][%w_]*$") then werror("bad or duplicate define") end map_def[name] = defendmap_coreop[".define_1"] = map_coreop[".define_2"]-- Define a substitution on the command line.function opt_map.D(args) local namesubst = optparam(args) local name, subst = match(namesubst, "^([%a_][%w_]*)=(.*)$") if name then map_def[name] = subst elseif match(namesubst, "^[%a_][%w_]*$") then map_def[namesubst] = "1" else opterror("bad define") endend-- Undefine a substitution on the command line.function opt_map.U(args) local name = optparam(args) if match(name, "^[%a_][%w_]*$") then map_def[name] = nil else opterror("bad define") endend-- Helper for definesubst.local gotsubstlocal function definesubst_one(word) local subst = map_def[word] if subst then gotsubst = word; return subst else return word endend-- Iteratively substitute defines.local function definesubst(stmt) -- Limit number of iterations. for i=1,100 do gotsubst = false stmt = gsub(stmt, "#?[%w_]+", definesubst_one) if not gotsubst then break end end if gotsubst then wfatal("recursive define involving `"..gotsubst.."'") end return stmtend-- Dump all defines.local function dumpdefines(out, lvl) local t = {} for name in pairs(map_def) do t[#t+1] = name end sort(t) out:write("Defines:\n") for _,name in ipairs(t) do local subst = map_def[name] if g_arch then subst = g_arch.revdef(subst) end out:write(format(" %-20s %s\n", name, subst)) end out:write("\n")end-------------------------------------------------------------------------------- Support variables for conditional assembly.local condlevel = 0local condstack = {}-- Evaluate condition with a Lua expression. Substitutions already performed.local function cond_eval(cond) local func, err = loadstring("return "..cond) if func then setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil. local ok, res = pcall(func) if ok then if res == 0 then return false end -- Oh well. return not not res end err = res end wfatal("bad condition: "..err)end-- Skip statements until next conditional pseudo-opcode at the same level.local function stmtskip() local dostmt_save = dostmt local lvl = 0 dostmt = function(stmt) local op = match(stmt, "^%s*(%S+)") if op == ".if" then lvl = lvl + 1 elseif lvl ~= 0 then if op == ".endif" then lvl = lvl - 1 end elseif op == ".elif" or op == ".else" or op == ".endif" then dostmt = dostmt_save dostmt(stmt) end endend-- Pseudo-opcodes for conditional assembly.map_coreop[".if_1"] = function(params) if not params then return "condition" end local lvl = condlevel + 1 local res = cond_eval(params[1]) condlevel = lvl condstack[lvl] = res if not res then stmtskip() endendmap_coreop[".elif_1"] = function(params) if not params then return "condition" end if condlevel == 0 then wfatal(".elif without .if") end local lvl = condlevel local res = condstack[lvl] if res then if res == "else" then wfatal(".elif after .else") end else res = cond_eval(params[1]) if res then condstack[lvl] = res return end end stmtskip()endmap_coreop[".else_0"] = function(params) if condlevel == 0 then wfatal(".else without .if") end local lvl = condlevel local res = condstack[lvl] condstack[lvl] = "else" if res then if res == "else" then wfatal(".else after .else") end stmtskip() endendmap_coreop[".endif_0"] = function(params) local lvl = condlevel if lvl == 0 then wfatal(".endif without .if") end condlevel = lvl - 1end-- Check for unfinished conditionals.local function checkconds() if g_errcount ~= "fatal" and condlevel ~= 0 then wprinterr(g_fname, ":*: error: unbalanced conditional\n") endend-------------------------------------------------------------------------------- Search for a file in the given path and open it for reading.local function pathopen(path, name) local dirsep = match(package.path, "\\") and "\\" or "/" for _,p in ipairs(path) do local fullname = p == "" and name or p..dirsep..name local fin = io.open(fullname, "r") if fin then g_fname = fullname return fin end endend-- Include a file.map_coreop[".include_1"] = function(params) if not params then return "filename" end local name = params[1] -- Save state. Ugly, I know. but upvalues are fast. local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent -- Read the included file. local fatal = readfile(pathopen(g_opt.include, name) or wfatal("include file `"..name.."' not found")) -- Restore state. g_synclineno = -1 g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi if fatal then wfatal("in include file") endend-- Make .include initially available, too.map_op[".include_1"] = map_coreop[".include_1"]-------------------------------------------------------------------------------- Support variables for macros.local mac_capture, mac_lineno, mac_namelocal mac_active = {}local mac_list = {}-- Pseudo-opcode to define a macro.map_coreop[".macro_*"] = function(mparams) if not mparams then return "name [, params...]" end -- Split off and validate macro name. local name = remove(mparams, 1) if not name then werror("missing macro name") end if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]+$")) then wfatal("bad macro name `"..name.."'") end -- Validate macro parameter names. local mdup = {} for _,mp in ipairs(mparams) do if not match(mp, "^[%a_][%w_]*$") then wfatal("bad macro parameter name `"..mp.."'") end if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end mdup[mp] = true end -- Check for duplicate or recursive macro definitions. local opname = name.."_"..#mparams if map_op[opname] or map_op[name.."_*"] then wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)") end if mac_capture then wfatal("recursive macro definition") end -- Enable statement capture. local lines = {} mac_lineno = g_lineno mac_name = name mac_capture = function(stmt) -- Statement capture function. -- Stop macro definition with .endmacro pseudo-opcode. if not match(stmt, "^%s*.endmacro%s*$") then lines[#lines+1] = stmt return end mac_capture = nil mac_lineno = nil mac_name = nil mac_list[#mac_list+1] = opname -- Add macro-op definition. map_op[opname] = function(params) if not params then return mparams, lines end -- Protect against recursive macro invocation. if mac_active[opname] then wfatal("recursive macro invocation") end mac_active[opname] = true -- Setup substitution map. local subst = {} for i,mp in ipairs(mparams) do subst[mp] = params[i] end local mcom if g_opt.maccomment and g_opt.comment then mcom = " MACRO "..name.." ("..#mparams..")" wcomment("{"..mcom) end -- Loop through all captured statements for _,stmt in ipairs(lines) do -- Substitute macro parameters. local st = gsub(stmt, "[%w_]+", subst) st = definesubst(st) st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b. if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end -- Emit statement. Use a protected call for better diagnostics. local ok, err = pcall(dostmt, st) if not ok then -- Add the captured statement to the error. wprinterr(err, "\n", g_indent, "| ", stmt, "\t[MACRO ", name, " (", #mparams, ")]\n") end end if mcom then wcomment("}"..mcom) end mac_active[opname] = nil end endend-- An .endmacro pseudo-opcode outside of a macro definition is an error.map_coreop[".endmacro_0"] = function(params) wfatal(".endmacro without .macro")end-- Dump all macros and their contents (with -PP only).local function dumpmacros(out, lvl) sort(mac_list) out:write("Macros:\n") for _,opname in ipairs(mac_list) do local name = sub(opname, 1, -3) local params, lines = map_op[opname]() out:write(format(" %-20s %s\n", name, concat(params, ", "))) if lvl > 1 then for _,line in ipairs(lines) do out:write(" |", line, "\n") end out:write("\n") end end out:write("\n")end-- Check for unfinished macro definitions.local function checkmacros() if mac_capture then wprinterr(g_fname, ":", mac_lineno, ": error: unfinished .macro `", mac_name ,"'\n") endend-------------------------------------------------------------------------------- Support variables for captures.local cap_lineno, cap_namelocal cap_buffers = {}local cap_used = {}-- Start a capture.map_coreop[".capture_1"] = function(params) if not params then return "name" end wflush() local name = params[1] if not match(name, "^[%a_][%w_]*$") then wfatal("bad capture name `"..name.."'") end if cap_name then wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno) end cap_name = name cap_lineno = g_lineno -- Create or continue a capture buffer and start the output line capture. local buf = cap_buffers[name] if not buf then buf = {}; cap_buffers[name] = buf end g_capbuffer = buf g_synclineno = 0end-- Stop a capture.map_coreop[".endcapture_0"] = function(params) wflush() if not cap_name then wfatal(".endcapture without a valid .capture") end cap_name = nil cap_lineno = nil g_capbuffer = nil g_synclineno = 0end-- Dump a capture buffer.map_coreop[".dumpcapture_1"] = function(params) if not params then return "name" end wflush() local name = params[1] if not match(name, "^[%a_][%w_]*$") then wfatal("bad capture name `"..name.."'") end cap_used[name] = true wline(function(out) local buf = cap_buffers[name] if buf then wdumplines(out, buf) end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -