Remove uxn-fast.c since new reference core is almost as fast.

This commit is contained in:
Andrew Alderwick 2022-01-02 23:05:28 +00:00
parent 6980abacfc
commit 52598bdad7
4 changed files with 1 additions and 4711 deletions

View File

@ -57,7 +57,7 @@ then
CORE='src/uxn.c'
else
CFLAGS="${CFLAGS} -DNDEBUG -Os -g0 -s"
CORE='src/uxn-fast.c'
CORE='src/uxn.c'
fi
echo "Building.."

View File

@ -1,389 +0,0 @@
local generate_labels = false
local replacements = {
op_and16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d & b); push8(u->src, c & a); }',
op_ora16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d | b); push8(u->src, c | a); }',
op_eor16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d ^ b); push8(u->src, c ^ a); }',
op_lit16 = '{ push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); }',
op_swp16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }',
op_ovr16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }',
op_dup16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, b); push8(u->src, a); }',
op_rot16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src), e = pop8(u->src), f = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, f); push8(u->src, e); }',
op_sth16 = '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->dst, b); push8(u->dst, a); }'
}
local top, bottom, pushtop
local offset
offset = function(n, s)
if s == nil then
s = ''
end
if n < 0 then
return (' -%s %d'):format(s, -n)
elseif n > 0 then
return (' +%s %d'):format(s, n)
elseif s ~= '' then
return (' +%s 0'):format(s)
else
return ''
end
end
local pop_push
pop_push = function(k, n, s)
local _exp_0 = k
if 'pop' == _exp_0 then
s = s:match('^%((%S+)%)$')
assert(s == 'src')
local _exp_1 = n
if '8' == _exp_1 then
top[s] = top[s] - 1
if bottom[s] > top[s] then
bottom[s] = top[s]
end
return ('%s.dat[%s.ptr%s]'):format(s, s, offset(top[s]))
elseif '16' == _exp_1 then
top[s] = top[s] - 2
if bottom[s] > top[s] then
bottom[s] = top[s]
end
return ('(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'):format(s, s, offset(top[s] + 1), s, s, offset(top[s]))
end
elseif 'push' == _exp_0 then
local v
s, v = s:match('^%((%S+), (.*)%)$')
assert(s == 'src' or s == 'dst', s)
local _exp_1 = n
if '8' == _exp_1 then
pushtop[s] = pushtop[s] + 1
return ('%s.dat[%s.ptr%s] = %s'):format(s, s, offset(pushtop[s] - 1), v)
elseif '16' == _exp_1 then
if v:match('%+%+') or v:match('%-%-') then
error('push16 has side effects: ' .. v)
end
local peek, args = v:match('^([md]e[mv]peek)16(%b())$')
if peek then
args = args:sub(2, -2)
return pop_push('push', '8', ('(%s, %s8(%s))'):format(s, peek, args)) .. ';\n' .. pop_push('push', '8', ('(%s, %s8(%s + 1))'):format(s, peek, args))
end
pushtop[s] = pushtop[s] + 2
if v:match(' ') then
v = '(' .. v .. ')'
end
return ('%s.dat[%s.ptr%s] = %s >> 8;\n%s.dat[%s.ptr%s] = %s & 0xff'):format(s, s, offset(pushtop[s] - 2), v, s, s, offset(pushtop[s] - 1), v)
end
else
return nil
end
end
local indented_block
indented_block = function(s)
s = s:gsub('^%{ *', '{\n'):gsub('\n', '\n\t'):gsub('\t%} *$', '}\n')
s = s:gsub('\n[^\n]+%.error = [^\n]+', '%0\n#ifndef NO_STACK_CHECKS\n\tgoto error;\n#endif')
return s
end
local process
process = function(body)
local out_body = body:gsub('^%{ *', ''):gsub(' *%}$', ''):gsub('; ', ';\n'):gsub('%b{} *', indented_block):gsub('(%a+)(%d+)(%b())', pop_push)
local in_ifdef = false
local _list_0 = {
'src',
'dst'
}
for _index_0 = 1, #_list_0 do
local k = _list_0[_index_0]
if bottom[k] ~= 0 then
if not in_ifdef then
out_body = out_body .. '\n#ifndef NO_STACK_CHECKS'
in_ifdef = true
end
out_body = out_body .. ('\nif(__builtin_expect(%s.ptr < %d, 0)) {\n\t%s.error = 1;\n\tgoto error;\n}'):format(k, -bottom[k], k)
end
if pushtop[k] ~= 0 then
if pushtop[k] > 0 then
if not in_ifdef then
out_body = out_body .. '\n#ifndef NO_STACK_CHECKS'
in_ifdef = true
end
out_body = out_body .. ('\nif(__builtin_expect(%s.ptr > %d, 0)) {\n\t%s.error = 2;\n\tgoto error;\n}'):format(k, 255 - pushtop[k], k)
end
if in_ifdef then
out_body = out_body .. '\n#endif'
in_ifdef = false
end
out_body = out_body .. ('\n%s.ptr %s= %d;'):format(k, pushtop[k] < 0 and '-' or '+', math.abs(pushtop[k]))
end
end
if in_ifdef then
out_body = out_body .. '\n#endif'
in_ifdef = false
end
local t = { }
out_body:gsub('[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', function(v, k)
t[k] = v
end)
out_body = out_body:gsub('(src%.dat%[[^]]+%]) = ([a-f]);\n', function(k, v)
if t[k] and t[k] == v then
return ''
end
return nil
end)
return out_body
end
local ops = { }
for l in assert(io.lines('src/uxn.c')) do
local _continue_0 = false
repeat
local name, body = l:match('void (op_%S*)%(Uxn %*u%) (%b{})')
if not name then
_continue_0 = true
break
end
if replacements[name] then
body = replacements[name]
end
body = body:gsub('u%-%>src%-%>', 'src.')
body = body:gsub('u%-%>dst%-%>', 'dst.')
body = body:gsub('u%-%>src', 'src')
body = body:gsub('u%-%>dst', 'dst')
top = {
src = 0,
dst = 0
}
bottom = {
src = 0,
dst = 0
}
pushtop = top
ops[name] = process(body)
top = {
src = 0,
dst = 0
}
bottom = {
src = 0,
dst = 0
}
pushtop = {
src = 0,
dst = 0
}
ops['keep_' .. name] = process(body)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
local dump
dump = function(s, src, dst)
local ret = '\t\t\t{\n'
for l in s:gmatch('[^\n]+') do
if not l:match('^%#') then
ret = ret .. '\t\t\t\t'
end
ret = ret .. ('%s\n'):format(l)
end
ret = ret .. '\t\t\t}\n\t\t\tbreak;\n'
return (ret:gsub('src', src):gsub('dst', dst))
end
local i = 0
local allops = { }
local wanted = false
for l in assert(io.lines('src/uxn.c')) do
if l == 'static void (*ops[])(Uxn *u) = {' then
wanted = true
elseif l == '};' then
wanted = false
elseif wanted then
l = l:gsub('%/%b**%/', '')
for op in l:gmatch('[%w_]+') do
if not ops[op] then
error('missing ' .. op)
end
allops[i + 0x00 + 1] = {
n = {
i + 0x00
},
body = dump(ops[op], 'u->wst', 'u->rst')
}
allops[i + 0x20 + 1] = {
n = {
i + 0x20
},
body = dump(ops[op], 'u->rst', 'u->wst')
}
allops[i + 0x80 + 1] = {
n = {
i + 0x80
},
body = dump(ops['keep_' .. op], 'u->wst', 'u->rst')
}
allops[i + 0xa0 + 1] = {
n = {
i + 0xa0
},
body = dump(ops['keep_' .. op], 'u->rst', 'u->wst')
}
i = i + 1
if i == 0x20 then
i = i + 0x20
end
end
end
end
i = 0
wanted = false
for l in assert(io.lines('src/uxnasm.c')) do
if l == 'static char ops[][4] = {' then
wanted = true
elseif l == '};' then
wanted = false
elseif wanted then
for op in l:gmatch('"(...)"') do
i = i + 1
allops[i + 0x00].name = op
allops[i + 0x20].name = op .. 'r'
allops[i + 0x40].name = op .. '2'
allops[i + 0x60].name = op .. '2r'
allops[i + 0x80].name = op .. 'k'
allops[i + 0xa0].name = op .. 'kr'
allops[i + 0xc0].name = op .. '2k'
allops[i + 0xe0].name = op .. '2kr'
end
end
end
for i = 1, 256 do
local _continue_0 = false
repeat
if not allops[i] then
_continue_0 = true
break
end
for j = i + 1, 256 do
if allops[i].body == allops[j].body then
table.insert(allops[i].n, (table.remove(allops[j].n)))
allops[j].body = nil
end
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
do
local _with_0 = assert(io.open('src/uxn-fast.c', 'w'))
local f = assert(io.open('src/uxn.c'))
while true do
local l = f:read('*l')
_with_0:write(('%s\n'):format(l))
if l == '*/' then
break
end
end
_with_0:write('\n')
_with_0:write([[/*
^
/!\ THIS FILE IS AUTOMATICALLY GENERATED
---
Its contents can get overwritten with the processed contents of src/uxn.c.
See etc/mkuxn-fast.moon for instructions.
*/
]])
wanted = true
while true do
local _continue_0 = false
repeat
local l = f:read('*l')
if l:match(' push') or l:match('[ *]pop') or l:match('devr16') then
_continue_0 = true
break
end
if l == '/* Stack */' then
wanted = false
end
if wanted then
_with_0:write(('%s\n'):format(l))
end
if l == '}' then
_with_0:write('\n')
break
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
_with_0:write([[/* clang-format on */
#pragma mark - Core
int
uxn_eval(Uxn *u, Uint16 vec)
{
Uint8 instr;
if(!vec || u->dev[0].dat[0xf])
return 0;
u->ram.ptr = vec;
if(u->wst.ptr > 0xf8) u->wst.ptr = 0xf8;
while((instr = u->ram.dat[u->ram.ptr++])) {
switch(instr) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-value"
#pragma GCC diagnostic ignored "-Wunused-variable"
]])
for i = 1, 256 do
local _continue_0 = false
repeat
if not allops[i].body then
_continue_0 = true
break
end
local _list_0 = allops[i].n
for _index_0 = 1, #_list_0 do
local n = _list_0[_index_0]
_with_0:write(('\t\tcase 0x%02x: /* %s */\n'):format(n, allops[n + 1].name))
end
if generate_labels then
_with_0:write(('\t\t\t__asm__("evaluxn_%02x_%s:");\n'):format(allops[i].n[1], allops[i].name))
end
_with_0:write(allops[i].body)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
_with_0:write([[#pragma GCC diagnostic pop
}
}
return 1;
#ifndef NO_STACK_CHECKS
error:
if(u->wst.error)
return uxn_halt(u, u->wst.error, "Working-stack", instr);
else
return uxn_halt(u, u->rst.error, "Return-stack", instr);
#endif
}
int
]])
wanted = false
while true do
local l = f:read('*l')
if not l then
break
end
if l:match('^uxn_boot') then
wanted = true
end
if wanted then
_with_0:write(('%s\n'):format(l))
end
end
f:close()
_with_0:close()
return _with_0
end

View File

@ -1,280 +0,0 @@
--
-- Uxn core unroller script
--
-- This script updates src/uxn-fast.c when Uxn's opcode set changes, so that
-- updates in the human-readable src/uxn.c core can be easily converted into
-- high-performance code.
--
-- To run, you need Lua or LuaJIT, and just run etc/mkuxn-fast.lua from the top
-- directory of Uxn's git repository:
--
-- lua etc/mkuxn-fast.lua
--
-- This file is written in MoonScript, which is a language that compiles to
-- Lua, the same way as e.g. CoffeeScript compiles to JavaScript. Since
-- installing MoonScript has more dependencies than Lua, the compiled
-- etc/mkuxn-fast.lua is kept in Uxn's repository and will be kept updated as
-- this file changes.
--
generate_labels = false -- add labels to each opcode to inspect disassembled code
replacements =
op_and16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d & b); push8(u->src, c & a); }'
op_ora16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d | b); push8(u->src, c | a); }'
op_eor16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d ^ b); push8(u->src, c ^ a); }'
op_lit16: '{ push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); }'
op_swp16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }'
op_ovr16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, d); push8(u->src, c); }'
op_dup16: '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, b); push8(u->src, a); }'
op_rot16: '{ Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src), d = pop8(u->src), e = pop8(u->src), f = pop8(u->src); push8(u->src, d); push8(u->src, c); push8(u->src, b); push8(u->src, a); push8(u->src, f); push8(u->src, e); }'
op_sth16: '{ Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->dst, b); push8(u->dst, a); }'
local top, bottom, pushtop
offset = (n, s = '') ->
if n < 0
' -%s %d'\format s, -n
elseif n > 0
' +%s %d'\format s, n
elseif s != ''
' +%s 0'\format s
else
''
pop_push = (k, n, s) ->
switch k
when 'pop'
s = s\match '^%((%S+)%)$'
assert s == 'src'
switch n
when '8'
top[s] -= 1
if bottom[s] > top[s]
bottom[s] = top[s]
'%s.dat[%s.ptr%s]'\format s, s, offset(top[s])
when '16'
top[s] -= 2
if bottom[s] > top[s]
bottom[s] = top[s]
'(%s.dat[%s.ptr%s] | (%s.dat[%s.ptr%s] << 8))'\format s, s, offset(top[s] + 1), s, s, offset(top[s])
when 'push'
s, v = s\match '^%((%S+), (.*)%)$'
assert s == 'src' or s == 'dst', s
switch n
when '8'
pushtop[s] += 1
'%s.dat[%s.ptr%s] = %s'\format s, s, offset(pushtop[s] - 1), v
when '16'
if v\match'%+%+' or v\match'%-%-'
error 'push16 has side effects: ' .. v
peek, args = v\match '^([md]e[mv]peek)16(%b())$'
if peek
args = args\sub 2, -2
return pop_push('push', '8', '(%s, %s8(%s))'\format s, peek, args) .. ';\n' .. pop_push('push', '8', '(%s, %s8(%s + 1))'\format s, peek, args)
pushtop[s] += 2
if v\match ' '
v = '(' .. v .. ')'
'%s.dat[%s.ptr%s] = %s >> 8;\n%s.dat[%s.ptr%s] = %s & 0xff'\format s, s, offset(pushtop[s] - 2), v, s, s, offset(pushtop[s] - 1), v
else
nil
indented_block = (s) ->
s = s\gsub('^%{ *', '{\n')\gsub('\n', '\n\t')\gsub('\t%} *$', '}\n')
s = s\gsub('\n[^\n]+%.error = [^\n]+', '%0\n#ifndef NO_STACK_CHECKS\n\tgoto error;\n#endif')
s
process = (body) ->
out_body = body\gsub('^%{ *', '')\gsub(' *%}$', '')\gsub('; ', ';\n')\gsub('%b{} *', indented_block)\gsub '(%a+)(%d+)(%b())', pop_push
in_ifdef = false
for k in *{'src', 'dst'}
if bottom[k] != 0
if not in_ifdef
out_body ..= '\n#ifndef NO_STACK_CHECKS'
in_ifdef = true
out_body ..= '\nif(__builtin_expect(%s.ptr < %d, 0)) {\n\t%s.error = 1;\n\tgoto error;\n}'\format k, -bottom[k], k
if pushtop[k] != 0
if pushtop[k] > 0
if not in_ifdef
out_body ..= '\n#ifndef NO_STACK_CHECKS'
in_ifdef = true
out_body ..= '\nif(__builtin_expect(%s.ptr > %d, 0)) {\n\t%s.error = 2;\n\tgoto error;\n}'\format k, 255 - pushtop[k], k
if in_ifdef
out_body ..= '\n#endif'
in_ifdef = false
out_body ..= '\n%s.ptr %s= %d;'\format k, pushtop[k] < 0 and '-' or '+', math.abs pushtop[k]
if in_ifdef
out_body ..= '\n#endif'
in_ifdef = false
t = {}
out_body\gsub '[^%w_]([a-f]) = (src%.dat%[[^]]+%])[,;]', (v, k) -> t[k] = v
out_body = out_body\gsub '(src%.dat%[[^]]+%]) = ([a-f]);\n', (k, v) ->
if t[k] and t[k] == v
return ''
return nil
out_body
ops = {}
for l in assert io.lines 'src/uxn.c'
name, body = l\match 'void (op_%S*)%(Uxn %*u%) (%b{})'
if not name
continue
if replacements[name]
body = replacements[name]
body = body\gsub 'u%-%>src%-%>', 'src.'
body = body\gsub 'u%-%>dst%-%>', 'dst.'
body = body\gsub 'u%-%>src', 'src'
body = body\gsub 'u%-%>dst', 'dst'
top = { src: 0, dst: 0 }
bottom = { src: 0, dst: 0 }
pushtop = top
ops[name] = process body
top = { src: 0, dst: 0 }
bottom = { src: 0, dst: 0 }
pushtop = { src: 0, dst: 0 }
ops['keep_' .. name] = process body
dump = (s, src, dst) ->
ret = '\t\t\t{\n'
for l in s\gmatch '[^\n]+'
if not l\match '^%#'
ret ..= '\t\t\t\t'
ret ..= '%s\n'\format l
ret ..= '\t\t\t}\n\t\t\tbreak;\n'
(ret\gsub('src', src)\gsub('dst', dst))
i = 0
allops = {}
wanted = false
for l in assert io.lines 'src/uxn.c'
if l == 'static void (*ops[])(Uxn *u) = {'
wanted = true
elseif l == '};'
wanted = false
elseif wanted
l = l\gsub '%/%b**%/', ''
for op in l\gmatch '[%w_]+'
if not ops[op]
error 'missing ' .. op
allops[i + 0x00 + 1] = { n: { i + 0x00 }, body: dump ops[op], 'u->wst', 'u->rst' }
allops[i + 0x20 + 1] = { n: { i + 0x20 }, body: dump ops[op], 'u->rst', 'u->wst' }
allops[i + 0x80 + 1] = { n: { i + 0x80 }, body: dump ops['keep_' .. op], 'u->wst', 'u->rst' }
allops[i + 0xa0 + 1] = { n: { i + 0xa0 }, body: dump ops['keep_' .. op], 'u->rst', 'u->wst' }
i += 1
if i == 0x20
i += 0x20
i = 0
wanted = false
for l in assert io.lines 'src/uxnasm.c'
if l == 'static char ops[][4] = {'
wanted = true
elseif l == '};'
wanted = false
elseif wanted
for op in l\gmatch '"(...)"'
i += 1
allops[i + 0x00].name = op
allops[i + 0x20].name = op .. 'r'
allops[i + 0x40].name = op .. '2'
allops[i + 0x60].name = op .. '2r'
allops[i + 0x80].name = op .. 'k'
allops[i + 0xa0].name = op .. 'kr'
allops[i + 0xc0].name = op .. '2k'
allops[i + 0xe0].name = op .. '2kr'
for i = 1, 256
if not allops[i]
continue
for j = i + 1, 256
if allops[i].body == allops[j].body
table.insert allops[i].n, (table.remove allops[j].n)
allops[j].body = nil
with assert io.open 'src/uxn-fast.c', 'w'
f = assert io.open 'src/uxn.c'
while true
l = f\read '*l'
\write '%s\n'\format l
if l == '*/'
break
\write '\n'
\write [[
/*
^
/!\ THIS FILE IS AUTOMATICALLY GENERATED
---
Its contents can get overwritten with the processed contents of src/uxn.c.
See etc/mkuxn-fast.moon for instructions.
*/
]]
wanted = true
while true
l = f\read '*l'
if l\match' push' or l\match'[ *]pop' or l\match'devr16'
continue
if l == '/* Stack */'
wanted = false
if wanted
\write '%s\n'\format l
if l == '}'
\write '\n'
break
\write [[
/* clang-format on */
#pragma mark - Core
int
uxn_eval(Uxn *u, Uint16 vec)
{
Uint8 instr;
if(!vec || u->dev[0].dat[0xf])
return 0;
u->ram.ptr = vec;
if(u->wst.ptr > 0xf8) u->wst.ptr = 0xf8;
while((instr = u->ram.dat[u->ram.ptr++])) {
switch(instr) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-value"
#pragma GCC diagnostic ignored "-Wunused-variable"
]]
for i = 1, 256
if not allops[i].body
continue
for n in *allops[i].n
\write '\t\tcase 0x%02x: /* %s */\n'\format n, allops[n + 1].name
if generate_labels
\write '\t\t\t__asm__("evaluxn_%02x_%s:");\n'\format allops[i].n[1], allops[i].name
\write allops[i].body
\write [[
#pragma GCC diagnostic pop
}
}
return 1;
#ifndef NO_STACK_CHECKS
error:
if(u->wst.error)
return uxn_halt(u, u->wst.error, "Working-stack", instr);
else
return uxn_halt(u, u->rst.error, "Return-stack", instr);
#endif
}
int
]]
wanted = false
while true
l = f\read '*l'
if not l
break
if l\match '^uxn_boot'
wanted = true
if wanted
\write '%s\n'\format l
f\close!
\close!

File diff suppressed because it is too large Load Diff