sokol-luajit/engine/dynasm/dynasm_demo_x86.dasl

246 lines
7.8 KiB
Plaintext
Raw Permalink Normal View History

--go@ luajit dynasm.lua *
local ffi = require'ffi' -- required
local dasm = require'dasm' -- required
|.arch ARCH
|
|.if not (X86 or X64)
| .error invalid arch ARCH
|.endif
|
|.actionlist actions // gen. `actions` to pass to dasm.new()
|.externnames externnames // gen `externnames` to pass to dasm.new() to solve extern names
|.section sec1, sec2 // gen. `DASM_SECTION_SEC1`, ..., `DASM_MAXSECTION`
|.globals global_ // gen. `global_<labelname>`, ..., `DASM_MAXGLOBAL`
|.globalnames globalnames // gen. `globalnames` to get the names of global labels
local demo = {} --demo table: {name = codegen_func}
local demos = {} --demo list in code order: {name1, ...}
setmetatable(demo, {__newindex = function(t,k,v) rawset(demo, k, v); demos[#demos+1] = k end})
--each code generator function generates some code into the Dst argument (which is a dasm state),
--and optionally returns a function that knows how to test the resulting code (if nothing is returned,
--the code will be just dumped for inspection).
function demo.literals(Dst)
| mov al, 0xff // number literals are translation-time and are range-checked.
| mov eax, 0xffffffff // this is correct and will result in 0xffffffff.
| mov eax, dword*0x22222222 // size overrides with number literals are also translation-time.
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G literal displacements.
|.if X86
| mov eax, [0xffffffff] // this mov form allows 0..4G literals.
|.else
| mov eax, [0x7fffffff] // this mov form allows only 0..2G literals and you won't get an error!
|.endif
end
function demo.immediate_subst(Dst) --immediate value substitution
| mov al, 0+0xff // expressions are encoding-time and coerced to int32.
| mov eax, 0+0xffffffff // but expressions are also normalized first, so we can pass a full uint32.
| mov eax, [ebx + ecx * 8 + 0x7fffffff] // this mov form allows +/- 2G expression displacements.
|.if X86
| mov eax, [0+0xffffffff] // this mov form allows 0..4G expressions.
|.else
| mov eax, [0+0x7fffffff] // this mov form allows only 0..2G expressions and you won't get an error!
| mov64 eax, [0+0xdeadbeefdeadbeefULL] // mov64 allows full 64bit expressions (they're never literals).
| mov64 rax, 0+0xdeadbeefdeadbeefULL // mov64 allows full 64bit expressions (they're never literals).
|.endif
end
function demo.addr(Dst) --address substution for jump targets
local addr = ffi.arch == 'x86' and 0xdeadbeef or 0xdeadbeefdeadbeefULL
local ptr = ffi.cast("void*", addr)
|.if X86
| jmp &addr // works with numbers
| jmp &ptr // works with pointers too
|.else
| mov64 rax, addr // no `&` on x64 but you can load an address with `mov64`
| mov64 rax, ptr // works with pointers too
|.endif
end
function demo.var_reg(Dst) --variable register numbers
| mov Rd(2), 0 // Rd(2) = edx. this is always encoding-time.
| fld Rf(2) // Rf(2) = st2. this is always encoding-time.
end
function demo.types(Dst) --type macros
if not rawget(_G, '__TYPEDEFS') then
rawset(_G, '__TYPEDEFS', true)
ffi.cdef'typedef struct Type1 {int f1; int f2;} Type1'
ffi.cdef'typedef Type1 Type2'
end
local n,m = 1,1
|.type TP1, Type1, eax
|.type TP2, Type2, ebx
|
| mov eax, TP1:ebx // +0
| mov ebx, TP1 // +0
| mov eax, TP1->f1 // +0
| mov ebx, TP1.f2 // +4
| mov ecx, TP2[n+m]->f1 // +16
| mov edx, TP2[n-2*m].f2 // -4
| mov edx, TP2:ecx[n-2*m].f2 // -4
end
function demo.code_sections(Dst)
|.sec1; mov ax, 1
|.sec2; mov ax, 2
|.sec1; mov bx, 1 // append code to .sec1
|.sec2; mov bx, 2 // append code to .sec2
end
function demo.global_labels(Dst)
|->l1:
| jmp ->l2
|->l2:
| jmp ->l1
end
function demo.local_labels(Dst)
|2:
|1:
| jmp >1
|1:
| jmp <2
end
function demo.label_subst(Dst)
|5:
|->label_subst:
| mov eax, [<5] // encoded as abs. addr. on x86 and as rip-relative on x64
| mov ebx, [->label_subst]
end
function demo.align(Dst)
local n = 0x90
| mov eax, 0x22222222
|.byte n
|.align word // puts nop
|.word bit.bor(bit.lshift(n,8), n) // expressions work too
|.align dword // puts nop
|.space 1+1, 0x90 // size can be variable, filler must be constant
| mov eax, 0x22222222
end
function demo.cond(Dst) --conditional compilation
|.define def1, 1
|.define def2, 1
|.if def1 + def2 == 2 // any lua expression works. note: a 0 result evaluates to false!
| nop
|.elif def2
| .fatal this won't be parsed
|.else
| .fatal this won't be parsed
|.endif
end
function demo.macros(Dst)
|.macro saveregs
|.if X86
| push ebp; push edi; push esi; push ebx
|.else
| push rbp; push rdi; push rsi; push rbx
|.endif
|.endmacro
|
|.macro restoreregs
|.if X86
| pop ebx; pop esi; pop edi; pop ebp
|.else
| pop rbx; pop rsi; pop rdi; pop rbp
|.endif
|.endmacro
|
|.macro cat, s1, s2, s3
| s1 .. s2 .. s3 // concatenation like `##` in C
|.endmacro
|
| saveregs
| restoreregs
| mov eax, 0xffffffff
| cat n, o, p
end
function demo.captures(Dst)
|.capture c1; mov eax, 0x1a; .endcapture
|.capture c2; mov ebx, 0x2a; .endcapture
|.capture c1; mov eax, 0x1b; .endcapture
|.capture c2; mov ebx, 0x2b; .endcapture
|.dumpcapture c1
|.dumpcapture c2
|.dumpcapture c2 // we can dump it again
end
function demo.extern(Dst)
local s1, s2 = 'Hello from %s!\n', 'DynASM'
local p1 = ffi.cast('const char*', s1) -- with `const` we can take addr. of a Lua string without making a copy!
local p2 = ffi.cast('const char*', s2)
if ffi.arch == 'x64' then
ffi.cdef'void printf(...)' --we need this to get printf's address
end
|.if X86 // ptr args in stack in reverse order
| sub esp, 4 // align the stack for OSX: 0 - call:4 - p1:4 - p2:4 = -12
| push &p2
| push &p1
| call extern printf
| add esp, 8 // pop args
| add esp, 4 // dealign
| ret
|.elif WINDOWS // ptr args in rcx, rdx, ...
| sub rsp, 32 // allocate shadow space for printf
| mov64 rcx, p1
| mov64 rdx, p2
| mov64 rax, ffi.C.printf // extern doesn't work on x64
| call rax
| add rsp, 32 // put it back
| ret
|.else // ptr args in rdi, rsi, ...
| sub rsp, 8 // align the stack for OSX: 0 - call:8 = -8
| mov rdi, &p1
| mov rsi, &p2
| mov64 rdx, ffi.C.printf // extern doesn't work on x64
| call rdx
| add rsp, 8
| ret
|.endif
return function(buf)
local _ = s1, s2 --pin the strings
local code = ffi.cast('void __cdecl (*) ()', buf)
code()
end
end
function demo.multiply(Dst)
|.if X86 // int args in stack in reverse order; int ret in eax
| mov eax, [esp+4]
| imul dword [esp+8]
| ret
|.elif WINDOWS // int args in rcx, rdx, ...; int ret in rax
| mov rax, rcx
| imul rdx
| ret
|.else // int args in rdi, rsi, ...; int ret in rax
| mov rax, rdi
| imul rsi
| ret
|.endif
return function(buf)
local mul = ffi.cast('int32_t __cdecl (*) (int32_t x, int32_t y)', buf)
assert(mul(7, -5) == -35)
assert(mul(-4, 0x08000000) == -0x20000000)
print'ok'
end
end
return {demo, demos, actions, externnames, globalnames, DASM_MAXSECTION, DASM_MAXGLOBAL}