tools: luaffi
parent
05f23f2ca6
commit
14a234f3ed
|
@ -0,0 +1,118 @@
|
||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
if "%1"=="tidy" (
|
||||||
|
del *.exe 1>nul 2>nul
|
||||||
|
del *.dll 1>nul 2>nul
|
||||||
|
del *.lib 1>nul 2>nul
|
||||||
|
del *.obj 1>nul 2>nul
|
||||||
|
del *.exp 1>nul 2>nul
|
||||||
|
del *.ilk 1>nul 2>nul
|
||||||
|
del *.pdb 1>nul 2>nul
|
||||||
|
del *.def 1>nul 2>nul
|
||||||
|
del call_*.h 1>nul 2>nul
|
||||||
|
rd /q /s lua 1>nul 2>nul
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1"=="dll" (
|
||||||
|
|
||||||
|
if not exist lua git clone https://github.com/lua/lua
|
||||||
|
if not exist lua.dll (
|
||||||
|
cl lua\onelua.c -Ilua /Felua.exe
|
||||||
|
|
||||||
|
cl lua\onelua.c -Ilua /Felua.dll /DLUA_BUILD_AS_DLL /LD
|
||||||
|
echo LIBRARY LUA > lua.def
|
||||||
|
echo EXPORTS >> lua.def
|
||||||
|
for /f "skip=19 tokens=4" %%A in ('dumpbin /exports lua.dll') do echo %%A >> lua.def
|
||||||
|
lib /def:lua.def /out:lua.lib /machine:x64
|
||||||
|
)
|
||||||
|
|
||||||
|
lua dynasm\dynasm.lua -LNE -D X32WIN -o call_x86.h call_x86.dasc
|
||||||
|
lua dynasm\dynasm.lua -LNE -D X64 -o call_x64.h call_x86.dasc
|
||||||
|
lua dynasm\dynasm.lua -LNE -D X64 -D X64WIN -o call_x64win.h call_x86.dasc
|
||||||
|
lua dynasm\dynasm.lua -LNE -o call_arm.h call_arm.dasc
|
||||||
|
|
||||||
|
set COMMON=/nologo /Zi /D_CRT_SECURE_NO_DEPRECATE /DLUA_FFI_BUILD_AS_DLL
|
||||||
|
|
||||||
|
if "%1"=="rel" (
|
||||||
|
set OPTIONS=/MD /Ox %COMMON%
|
||||||
|
) else (
|
||||||
|
set OPTIONS=/MDd /Od %COMMON%
|
||||||
|
)
|
||||||
|
|
||||||
|
CL %OPTIONS% /I. /Ilua /DLUA_DLL_NAME="lua.dll" call.c ctype.c ffi.c parser.c lua.lib /Feffi.dll /LD
|
||||||
|
|
||||||
|
CL %OPTIONS% /Gd test.c /Fe"test_cdecl.dll" /LD
|
||||||
|
CL %OPTIONS% /Gz test.c /Fe"test_stdcall.dll" /LD
|
||||||
|
CL %OPTIONS% /Gr test.c /Fe"test_fastcall.dll" /LD
|
||||||
|
|
||||||
|
lua test.lua
|
||||||
|
|
||||||
|
del *.exp *.ilk *.pdb *.obj *.manifest 2> nul 1> nul
|
||||||
|
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1"=="3rd" (
|
||||||
|
|
||||||
|
echo #ifndef LUAFFI_H > 3rd_luaffi.h
|
||||||
|
echo #define LUAFFI_H >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
type ffi.h >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
type "dynasm\dasm_proto.h" >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
echo #endif >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
|
||||||
|
echo #ifdef LUAFFI_C >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
echo static cfunction compile(Dst_DECL, lua_State* L, cfunction func, int ref^); >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
echo #if defined __arm__ ^|^| defined __arm ^|^| defined __ARM__ ^|^| defined __ARM ^|^| defined ARM ^|^| defined _ARM_ ^|^| defined ARMV4I ^|^| defined _M_ARM >> 3rd_luaffi.h
|
||||||
|
type "dynasm\dasm_arm.h" >> 3rd_luaffi.h
|
||||||
|
echo #else >> 3rd_luaffi.h
|
||||||
|
type "dynasm\dasm_x86.h" >> 3rd_luaffi.h
|
||||||
|
echo #endif >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
echo #if defined __arm__ ^|^| defined __arm ^|^| defined __ARM__ ^|^| defined __ARM ^|^| defined ARM ^|^| defined _ARM_ ^|^| defined ARMV4I ^|^| defined _M_ARM >> 3rd_luaffi.h
|
||||||
|
type "call_arm.h" >> 3rd_luaffi.h
|
||||||
|
echo #elif defined _WIN64 >> 3rd_luaffi.h
|
||||||
|
type "call_x64win.h" >> 3rd_luaffi.h
|
||||||
|
echo #elif defined __amd64__ >> 3rd_luaffi.h
|
||||||
|
type "call_x64.h" >> 3rd_luaffi.h
|
||||||
|
echo #else >> 3rd_luaffi.h
|
||||||
|
type "call_x86.h" >> 3rd_luaffi.h
|
||||||
|
echo #endif >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
type call.c >> 3rd_luaffi.h
|
||||||
|
type ctype.c >> 3rd_luaffi.h
|
||||||
|
type parser.c >> 3rd_luaffi.h
|
||||||
|
type ffi.c >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
echo #endif >> 3rd_luaffi.h
|
||||||
|
echo. >> 3rd_luaffi.h
|
||||||
|
|
||||||
|
..\fart -- 3rd_luaffi.h "#pragma once" "//#pragma once"
|
||||||
|
..\fart -- 3rd_luaffi.h "#include \"" "//#include \""
|
||||||
|
..\fart -- 3rd_luaffi.h "# include" "//# include"
|
||||||
|
..\fart -- 3rd_luaffi.h "dasm_State*" "struct dasm_State*"
|
||||||
|
..\fart -- 3rd_luaffi.h "EXPORT" "LUAFFI_EXPORT"
|
||||||
|
..\fart -- 3rd_luaffi.h "ALIGN_UP" "LUAFFI_ALIGN_UP"
|
||||||
|
..\fart -- 3rd_luaffi.h "get_int" "LUAFFI_get_int"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "..\fart.exe" echo ..\fart.exe not found && exit /b
|
||||||
|
if not exist "call_x86.h" call make dll
|
||||||
|
if not exist "3rd_luaffi.h" call make 3rd
|
||||||
|
if exist "3rd_luaffi.h" move /y 3rd_luaffi.h ..\..\engine\split && call make tidy
|
|
@ -0,0 +1,54 @@
|
||||||
|
.PHONY: all clean test
|
||||||
|
|
||||||
|
PKG_CONFIG=pkg-config
|
||||||
|
LUA=lua
|
||||||
|
|
||||||
|
#LUA_CFLAGS=`$(PKG_CONFIG) --cflags lua5.2 2>/dev/null || $(PKG_CONFIG) --cflags lua`
|
||||||
|
LUA_CFLAGS=-I$(PWD)/../lua-5.2.4/src
|
||||||
|
SOCFLAGS=-fPIC
|
||||||
|
SOCC=$(CC) -shared $(SOCFLAGS)
|
||||||
|
CFLAGS=-fPIC -g -Wall -Werror $(LUA_CFLAGS) -fvisibility=hidden -Wno-unused-function --std=gnu99
|
||||||
|
|
||||||
|
MODNAME=ffi
|
||||||
|
MODSO=$(MODNAME).so
|
||||||
|
|
||||||
|
all:
|
||||||
|
if [ `uname` = "Darwin" ]; then $(MAKE) macosx; else $(MAKE) posix; fi
|
||||||
|
|
||||||
|
test:
|
||||||
|
if [ `uname` = "Darwin" ]; then $(MAKE) test_macosx; else $(MAKE) test_posix; fi
|
||||||
|
|
||||||
|
macosx:
|
||||||
|
$(MAKE) posix "SOCC=MACOSX_DEPLOYMENT_TARGET=10.3 $(CC) -dynamiclib -single_module -undefined dynamic_lookup $(SOCFLAGS)"
|
||||||
|
|
||||||
|
test_macosx:
|
||||||
|
$(MAKE) test_posix "SOCC=MACOSX_DEPLOYMENT_TARGET=10.3 $(CC) -dynamiclib -single_module -undefined dynamic_lookup $(SOCFLAGS)"
|
||||||
|
|
||||||
|
posix: $(MODSO) test_cdecl.so
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.so call_*.h
|
||||||
|
|
||||||
|
call_x86.h: call_x86.dasc dynasm/*.lua
|
||||||
|
$(LUA) dynasm/dynasm.lua -LN -o $@ $<
|
||||||
|
|
||||||
|
call_x64.h: call_x86.dasc dynasm/*.lua
|
||||||
|
$(LUA) dynasm/dynasm.lua -D X64 -LN -o $@ $<
|
||||||
|
|
||||||
|
call_x64win.h: call_x86.dasc dynasm/*.lua
|
||||||
|
$(LUA) dynasm/dynasm.lua -D X64 -D X64WIN -LN -o $@ $<
|
||||||
|
|
||||||
|
%.o: %.c *.h dynasm/*.h call_x86.h call_x64.h call_x64win.h
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
$(MODSO): ffi.o ctype.o parser.o call.o
|
||||||
|
$(SOCC) $^ -o $@
|
||||||
|
|
||||||
|
test_cdecl.so: test.o
|
||||||
|
$(SOCC) $^ -o $@
|
||||||
|
|
||||||
|
test_posix: test_cdecl.so $(MODSO)
|
||||||
|
LD_LIBRARY_PATH=./ $(LUA) test.lua
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
About
|
||||||
|
-----
|
||||||
|
This is a library for calling C function and manipulating C types from lua. It
|
||||||
|
is designed to be interface compatible with the FFI library in luajit (see
|
||||||
|
http://luajit.org/ext_ffi.html). It can parse C function declarations and
|
||||||
|
struct definitions that have been directly copied out of C header files and
|
||||||
|
into lua source as a string.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Copyright (c) 2011 James R. McKaskill.
|
||||||
|
MIT same as Lua 5.1. See full license text in ffi.h.
|
||||||
|
|
||||||
|
Source
|
||||||
|
------
|
||||||
|
https://github.com/jmckaskill/luaffi
|
||||||
|
|
||||||
|
Platforms
|
||||||
|
---------
|
||||||
|
Currently supported:
|
||||||
|
- windows x86/x64
|
||||||
|
- linux x86/x64
|
||||||
|
- windows CE ARM little endian (ARMv4+)
|
||||||
|
- OSX x86/x64
|
||||||
|
|
||||||
|
Currently only dll builds are supported (ie no static).
|
||||||
|
|
||||||
|
Runs with both Lua 5.1 and Lua 5.2 beta.
|
||||||
|
|
||||||
|
Build
|
||||||
|
-----
|
||||||
|
|
||||||
|
On windows use msvcbuild.bat in a visual studio cmd prompt. Available targets are:
|
||||||
|
- nothing or release: default release build
|
||||||
|
- debug: debug build
|
||||||
|
- test: build and run the test debug build
|
||||||
|
- test-release: build and run the test release build
|
||||||
|
- clean: cleanup object files
|
||||||
|
|
||||||
|
Edit msvcbuild.bat if your lua exe, lib, lua include path, or lua dll name
|
||||||
|
differ from c:\Lua5.1 and lua5.1.dll.
|
||||||
|
|
||||||
|
The build script does not build for CE as this is non-trivial and very
|
||||||
|
dependent on which CE profile (or even a custom one). Instead to build on CE,
|
||||||
|
add generate_call_h.bat as a pre-build event and then build *.c with UNDER_CE
|
||||||
|
defined plus whatever defines windows.h requires.
|
||||||
|
|
||||||
|
On posix use make. Available targets are:
|
||||||
|
- nothing or all: default release build
|
||||||
|
- debug: debug build
|
||||||
|
- test: build and run the test build
|
||||||
|
- clean: cleanup object files
|
||||||
|
- macosx: release build for Mac OSX
|
||||||
|
|
||||||
|
Edit the Makefile if your lua exe differs from `lua5.1` or if you can't get
|
||||||
|
the include and lib arguments from pkg-config.
|
||||||
|
|
||||||
|
Known Issues
|
||||||
|
------------
|
||||||
|
- Has not been bullet proof tested
|
||||||
|
- Casting is different from luajit. For the moment this follows C++
|
||||||
|
- ffi.cast is equivalent to a C cast in C++ (T t = (T) f)
|
||||||
|
- ffi.new and ctype() is equivalent to an implicit cast in C++ (T t = f)
|
||||||
|
- since this follows C++ semantics void* does not cast to T* (an explicit
|
||||||
|
cast using ffi.cast is required)
|
||||||
|
- Comparing a ctype pointer to nil doesn't work the same as luajit. This is
|
||||||
|
unfixable with the current metamethod semantics. Instead use ffi.C.NULL
|
||||||
|
- Constant expressions can't handle non integer intermediate values (eg
|
||||||
|
offsetof won't work because it manipulates pointers)
|
||||||
|
- Not all metamethods work with lua 5.1 (eg char* + number). This is due to
|
||||||
|
the way metamethods are looked up with mixed types in Lua 5.1. If you need
|
||||||
|
this upgrade to Lua 5.2 or use boxed numbers (uint64_t and uintptr_t).
|
||||||
|
- All bitfields are treated as unsigned (does anyone even use signed
|
||||||
|
bitfields?). Note that "int s:8" is unsigned on unix x86/x64, but signed on
|
||||||
|
windows.
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----
|
||||||
|
See Github issues for the most up to date list.
|
||||||
|
- Fix arm support - broken since the callback refactor
|
||||||
|
- Vectors
|
||||||
|
- C++ reference types
|
||||||
|
- Subtracting one pointer from another
|
||||||
|
- Variable sized members in unions (is this needed?)
|
||||||
|
|
||||||
|
How it works
|
||||||
|
------------
|
||||||
|
Types are represented by a struct ctype structure and an associated user value
|
||||||
|
table. The table is shared between all related types for structs, unions, and
|
||||||
|
functions. It's members have the types of struct members, function argument
|
||||||
|
types, etc. The struct ctype structure then contains the modifications from
|
||||||
|
the base type (eg number of pointers, array size, etc).
|
||||||
|
|
||||||
|
Types are pushed into lua as a userdata containing the struct ctype with a
|
||||||
|
user value (or fenv in 5.1) set to the shared type table.
|
||||||
|
|
||||||
|
Boxed cdata types are pushed into lua as a userdata containing the struct
|
||||||
|
cdata structure (which contains the struct ctype of the data as its header)
|
||||||
|
followed by the boxed data.
|
||||||
|
|
||||||
|
The functions in ffi.c provide the cdata and ctype metatables and ffi.*
|
||||||
|
functions which manipulate these two types.
|
||||||
|
|
||||||
|
C functions (and function pointers) are pushed into lua as a lua c function
|
||||||
|
with the function pointer cdata as the first upvalue. The actual code is JITed
|
||||||
|
using dynasm (see call_x86.dasc). The JITed code does the following in order:
|
||||||
|
1. Calls the needed unpack functions in ffi.c placing each argument on the HW stack
|
||||||
|
2. Updates errno
|
||||||
|
3. Performs the c call
|
||||||
|
4. Retrieves errno
|
||||||
|
5. Pushes the result back into lua from the HW register or stack
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
||||||
|
*/
|
||||||
|
#include "ffi.h"
|
||||||
|
|
||||||
|
static cfunction compile(Dst_DECL, lua_State* L, cfunction func, int ref);
|
||||||
|
|
||||||
|
static void* reserve_code(struct jit* jit, lua_State* L, size_t sz);
|
||||||
|
static void commit_code(struct jit* jit, void* p, size_t sz);
|
||||||
|
|
||||||
|
static void push_int(lua_State* L, int val)
|
||||||
|
{ lua_pushnumber(L, val); }
|
||||||
|
|
||||||
|
static void push_uint(lua_State* L, unsigned int val)
|
||||||
|
{ lua_pushnumber(L, val); }
|
||||||
|
|
||||||
|
static void push_float(lua_State* L, float val)
|
||||||
|
{ lua_pushnumber(L, val); }
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static int GetLastError(void)
|
||||||
|
{ return errno; }
|
||||||
|
static void SetLastError(int err)
|
||||||
|
{ errno = err; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define shred(a,b,c)
|
||||||
|
#else
|
||||||
|
#define shred(p,s,e) memset((uint8_t*)(p)+(s),0xCC,(e)-(s))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
#include "dynasm/dasm_x86.h"
|
||||||
|
#include "call_x64win.h"
|
||||||
|
#elif defined __amd64__
|
||||||
|
#include "dynasm/dasm_x86.h"
|
||||||
|
#include "call_x64.h"
|
||||||
|
#elif defined __arm__ || defined __arm || defined __ARM__ || defined __ARM || defined ARM || defined _ARM_ || defined ARMV4I || defined _M_ARM
|
||||||
|
#include "dynasm/dasm_arm.h"
|
||||||
|
#include "call_arm.h"
|
||||||
|
#else
|
||||||
|
#include "dynasm/dasm_x86.h"
|
||||||
|
#include "call_x86.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct jit_head {
|
||||||
|
size_t size;
|
||||||
|
int ref;
|
||||||
|
uint8_t jump[JUMP_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LINKTABLE_MAX_SIZE (sizeof(extnames) / sizeof(extnames[0]) * (JUMP_SIZE))
|
||||||
|
|
||||||
|
static cfunction compile(struct jit* jit, lua_State* L, cfunction func, int ref)
|
||||||
|
{
|
||||||
|
struct jit_head* code;
|
||||||
|
size_t codesz;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dasm_checkstep(jit, -1);
|
||||||
|
if ((err = dasm_link(jit, &codesz)) != 0) {
|
||||||
|
char buf[32];
|
||||||
|
sprintf(buf, "%x", err);
|
||||||
|
luaL_error(L, "dasm_link error %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
codesz += sizeof(struct jit_head);
|
||||||
|
code = (struct jit_head*) reserve_code(jit, L, codesz);
|
||||||
|
code->ref = ref;
|
||||||
|
code->size = codesz;
|
||||||
|
compile_extern_jump(jit, L, func, code->jump);
|
||||||
|
|
||||||
|
if ((err = dasm_encode(jit, code+1)) != 0) {
|
||||||
|
char buf[32];
|
||||||
|
sprintf(buf, "%x", err);
|
||||||
|
commit_code(jit, code, 0);
|
||||||
|
luaL_error(L, "dasm_encode error %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
commit_code(jit, code, codesz);
|
||||||
|
return (cfunction) (code+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef uint8_t jump_t[JUMP_SIZE];
|
||||||
|
|
||||||
|
int get_extern(struct jit* jit, uint8_t* addr, int idx, int type)
|
||||||
|
{
|
||||||
|
struct page* page = jit->pages[jit->pagenum-1];
|
||||||
|
jump_t* jumps = (jump_t*) (page+1);
|
||||||
|
struct jit_head* h = (struct jit_head*) ((uint8_t*) page + page->off);
|
||||||
|
uint8_t* jmp;
|
||||||
|
ptrdiff_t off;
|
||||||
|
|
||||||
|
if (idx == jit->function_extern) {
|
||||||
|
jmp = h->jump;
|
||||||
|
} else {
|
||||||
|
jmp = jumps[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compensate for room taken up for the offset so that we can work rip
|
||||||
|
* relative */
|
||||||
|
addr += BRANCH_OFF;
|
||||||
|
|
||||||
|
/* see if we can fit the offset in the branch displacement, if not use the
|
||||||
|
* jump instruction */
|
||||||
|
off = *(uint8_t**) jmp - addr;
|
||||||
|
|
||||||
|
if (MIN_BRANCH <= off && off <= MAX_BRANCH) {
|
||||||
|
return (int32_t) off;
|
||||||
|
} else {
|
||||||
|
return (int32_t)(jmp + sizeof(uint8_t*) - addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM >= 503
|
||||||
|
LUA_API void lua_remove_compat (lua_State *L, int idx) {
|
||||||
|
lua_remove(L, idx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void* reserve_code(struct jit* jit, lua_State* L, size_t sz)
|
||||||
|
{
|
||||||
|
struct page* page;
|
||||||
|
size_t off = (jit->pagenum > 0) ? jit->pages[jit->pagenum-1]->off : 0;
|
||||||
|
size_t size = (jit->pagenum > 0) ? jit->pages[jit->pagenum-1]->size : 0;
|
||||||
|
|
||||||
|
if (off + sz >= size) {
|
||||||
|
int i;
|
||||||
|
uint8_t* pdata;
|
||||||
|
cfunction func;
|
||||||
|
|
||||||
|
/* need to create a new page */
|
||||||
|
jit->pages = (struct page**) realloc(jit->pages, (++jit->pagenum) * sizeof(jit->pages[0]));
|
||||||
|
|
||||||
|
size = ALIGN_UP(sz + LINKTABLE_MAX_SIZE + sizeof(struct page), jit->align_page_size);
|
||||||
|
|
||||||
|
page = (struct page*) AllocPage(size);
|
||||||
|
jit->pages[jit->pagenum-1] = page;
|
||||||
|
pdata = (uint8_t*) page;
|
||||||
|
page->size = size;
|
||||||
|
page->off = sizeof(struct page);
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
#define ADDFUNC(DLL, NAME) \
|
||||||
|
lua_pushliteral(L, #NAME); \
|
||||||
|
func = DLL ? (cfunction) GetProcAddressA(DLL, #NAME) : NULL; \
|
||||||
|
func = func ? func : (cfunction) &NAME; \
|
||||||
|
lua_pushcfunction(L, (lua_CFunction) func); \
|
||||||
|
lua_rawset(L, -3)
|
||||||
|
|
||||||
|
ADDFUNC(NULL, check_double);
|
||||||
|
ADDFUNC(NULL, check_float);
|
||||||
|
ADDFUNC(NULL, check_uint64);
|
||||||
|
ADDFUNC(NULL, check_int64);
|
||||||
|
ADDFUNC(NULL, check_int32);
|
||||||
|
ADDFUNC(NULL, check_uint32);
|
||||||
|
ADDFUNC(NULL, check_uintptr);
|
||||||
|
ADDFUNC(NULL, check_enum);
|
||||||
|
ADDFUNC(NULL, check_typed_pointer);
|
||||||
|
ADDFUNC(NULL, check_typed_cfunction);
|
||||||
|
ADDFUNC(NULL, check_complex_double);
|
||||||
|
ADDFUNC(NULL, check_complex_float);
|
||||||
|
ADDFUNC(NULL, unpack_varargs_stack);
|
||||||
|
ADDFUNC(NULL, unpack_varargs_stack_skip);
|
||||||
|
ADDFUNC(NULL, unpack_varargs_reg);
|
||||||
|
ADDFUNC(NULL, unpack_varargs_float);
|
||||||
|
ADDFUNC(NULL, unpack_varargs_int);
|
||||||
|
ADDFUNC(NULL, push_cdata);
|
||||||
|
ADDFUNC(NULL, push_int);
|
||||||
|
ADDFUNC(NULL, push_uint);
|
||||||
|
ADDFUNC(NULL, push_float);
|
||||||
|
ADDFUNC(jit->kernel32_dll, SetLastError);
|
||||||
|
ADDFUNC(jit->kernel32_dll, GetLastError);
|
||||||
|
ADDFUNC(jit->lua_dll, luaL_error);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_pushnumber);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_pushboolean);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_gettop);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_rawgeti);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_pushnil);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_callk);
|
||||||
|
ADDFUNC(jit->lua_dll, lua_settop);
|
||||||
|
#if LUA_VERSION_NUM >= 503
|
||||||
|
lua_pushliteral(L, "lua_remove");
|
||||||
|
lua_pushcfunction(L, (lua_CFunction) lua_remove_compat);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
#else
|
||||||
|
ADDFUNC(jit->lua_dll, lua_remove);
|
||||||
|
#endif
|
||||||
|
#undef ADDFUNC
|
||||||
|
|
||||||
|
for (i = 0; extnames[i] != NULL; i++) {
|
||||||
|
|
||||||
|
if (strcmp(extnames[i], "FUNCTION") == 0) {
|
||||||
|
shred(pdata + page->off, 0, JUMP_SIZE);
|
||||||
|
jit->function_extern = i;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lua_getfield(L, -1, extnames[i]);
|
||||||
|
func = (cfunction) lua_tocfunction(L, -1);
|
||||||
|
|
||||||
|
if (func == NULL) {
|
||||||
|
luaL_error(L, "internal error: missing link for %s", extnames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_extern_jump(jit, L, func, pdata + page->off);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
page->off += JUMP_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
page->freed = page->off;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
page = jit->pages[jit->pagenum-1];
|
||||||
|
EnableWrite(page, page->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint8_t*) page + page->off;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commit_code(struct jit* jit, void* code, size_t sz)
|
||||||
|
{
|
||||||
|
struct page* page = jit->pages[jit->pagenum-1];
|
||||||
|
page->off += sz;
|
||||||
|
EnableExecute(page, page->size);
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
FILE* out = fopen("\\Hard Disk\\out.bin", "wb");
|
||||||
|
fwrite(page, page->off, 1, out);
|
||||||
|
fclose(out);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* push_func_ref pushes a copy of the upval table embedded in the compiled
|
||||||
|
* function func.
|
||||||
|
*/
|
||||||
|
void push_func_ref(lua_State* L, cfunction func)
|
||||||
|
{
|
||||||
|
struct jit_head* h = ((struct jit_head*) func) - 1;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, h->ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_code(struct jit* jit, lua_State* L, cfunction func)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
struct jit_head* h = ((struct jit_head*) func) - 1;
|
||||||
|
for (i = 0; i < jit->pagenum; i++) {
|
||||||
|
struct page* p = jit->pages[i];
|
||||||
|
|
||||||
|
if ((uint8_t*) h < (uint8_t*) p || (uint8_t*) p + p->size <= (uint8_t*) h) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, h->ref);
|
||||||
|
|
||||||
|
EnableWrite(p, p->size);
|
||||||
|
p->freed += h->size;
|
||||||
|
|
||||||
|
shred(h, 0, h->size);
|
||||||
|
|
||||||
|
if (p->freed < p->off) {
|
||||||
|
EnableExecute(p, p->size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FreePage(p, p->size);
|
||||||
|
memmove(&jit->pages[i], &jit->pages[i+1], (jit->pagenum - (i+1)) * sizeof(jit->pages[0]));
|
||||||
|
jit->pagenum--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!"couldn't find func in the jit pages");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,639 @@
|
||||||
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
||||||
|
*/
|
||||||
|
|.arch arm
|
||||||
|
|.actionlist build_actionlist
|
||||||
|
|.globalnames globnames
|
||||||
|
|.externnames extnames
|
||||||
|
|
||||||
|
#define JUMP_SIZE 8
|
||||||
|
#define MIN_BRANCH ((INT32_MIN) >> 8)
|
||||||
|
#define MAX_BRANCH ((INT32_MAX) >> 8)
|
||||||
|
#define BRANCH_OFF 4
|
||||||
|
|
||||||
|
static void compile_extern_jump(struct jit* jit, lua_State* L, function_t func, uint8_t* code)
|
||||||
|
{
|
||||||
|
/* The jump code is the function pointer followed by a stub to call the
|
||||||
|
* function pointer. The stub exists so we can jump to functions with an
|
||||||
|
* offset greater than 32MB.
|
||||||
|
*
|
||||||
|
* Note we have to manually set this up since there are commands buffered
|
||||||
|
* in the jit state.
|
||||||
|
*/
|
||||||
|
*(function_t*) code = func;
|
||||||
|
/* ldr pc, [pc - 12] */
|
||||||
|
*(uint32_t*) &code[4] = 0xE51FF00CU;
|
||||||
|
}
|
||||||
|
|
||||||
|
|.define TOP, r4
|
||||||
|
|.define L_ARG, r5
|
||||||
|
|.define DATA, r6
|
||||||
|
|.define DATA2, r7
|
||||||
|
|
||||||
|
|.macro load32, reg, val
|
||||||
|
| ldr reg, [pc]
|
||||||
|
| b >5
|
||||||
|
|.long val
|
||||||
|
|5:
|
||||||
|
|.endmacro
|
||||||
|
|
||||||
|
|.macro lcall, func
|
||||||
|
| mov r0, L_ARG
|
||||||
|
| bl func
|
||||||
|
|.endmacro
|
||||||
|
|
||||||
|
void compile_globals(struct jit* jit, lua_State* L)
|
||||||
|
{
|
||||||
|
(void) jit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function_t push_callback(struct jit* jit, lua_State* L, int fidx, int ct_usr, const struct ctype* ct)
|
||||||
|
{
|
||||||
|
struct jit* Dst = jit;
|
||||||
|
int i, nargs, num_upvals, ref;
|
||||||
|
const struct ctype* mt;
|
||||||
|
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
fidx = lua_absindex(L, fidx);
|
||||||
|
nargs = (int) lua_rawlen(L, ct_usr);
|
||||||
|
|
||||||
|
dasm_setup(Dst, build_actionlist);
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
num_upvals = 0;
|
||||||
|
|
||||||
|
if (ct->has_var_arg) {
|
||||||
|
luaL_error(L, "can't create callbacks with varargs");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prolog and get the upval table */
|
||||||
|
| mov r12, sp
|
||||||
|
| push {r0, r1, r2, r3} // do this first so that r0-r3 is right before stack bound arguments
|
||||||
|
| push {TOP, L_ARG, DATA, DATA2, r12, lr}
|
||||||
|
| sub DATA, r12, #16 // points to r0 on stack
|
||||||
|
| ldr L_ARG, [pc, #8]
|
||||||
|
| ldr r2, [pc, #8]
|
||||||
|
| ldr r1, [pc, #8]
|
||||||
|
| b >1
|
||||||
|
|.long L, ref, LUA_REGISTRYINDEX
|
||||||
|
|1:
|
||||||
|
| lcall extern lua_rawgeti
|
||||||
|
|
||||||
|
/* get the lua function */
|
||||||
|
lua_pushvalue(L, fidx);
|
||||||
|
lua_rawseti(L, -2, ++num_upvals);
|
||||||
|
| mov r2, #num_upvals
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern lua_rawgeti
|
||||||
|
|
||||||
|
for (i = 1; i <= nargs; i++) {
|
||||||
|
lua_rawgeti(L, ct_usr, i);
|
||||||
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
||||||
|
|
||||||
|
if (mt->pointers) {
|
||||||
|
lua_getuservalue(L, -1);
|
||||||
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
||||||
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
||||||
|
|
||||||
|
| mov r2, #num_upvals-1 // usr value
|
||||||
|
| mvn r1, #i // -i-1, stack is upval table, func, i-1 args
|
||||||
|
| lcall extern lua_rawgeti
|
||||||
|
| load32 r2, mt
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| ldr r2, [DATA], #4
|
||||||
|
| str r2, [r0]
|
||||||
|
| mvn r1, #1 // -2
|
||||||
|
| lcall extern lua_remove // remove the usr value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
switch (mt->type) {
|
||||||
|
case INT64_TYPE:
|
||||||
|
case UINT64_TYPE:
|
||||||
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
||||||
|
| lcall extern lua_pushnil
|
||||||
|
| load32 r2, mt
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| ldr r2, [DATA], #4
|
||||||
|
| ldr r3, [DATA], #4
|
||||||
|
| str r2, [r0]
|
||||||
|
| str r3, [r0, #4]
|
||||||
|
| mvn r1, #1 // -2
|
||||||
|
| lcall extern lua_remove // remove the nil usr
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINTPTR_TYPE:
|
||||||
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
||||||
|
| lcall extern lua_pushnil
|
||||||
|
| load32 r2, mt
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| ldr r2, [DATA], #4
|
||||||
|
| str r2, [r0]
|
||||||
|
| mvn r1, #1 // -2
|
||||||
|
| lcall extern lua_remove // remove the nil usr
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOL_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| lcall extern lua_pushboolean
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT8_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| mov r1, r1, lsl #24
|
||||||
|
| mov r1, r1, asr #24
|
||||||
|
| lcall extern push_int
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT8_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| and r1, r1, #0xFF
|
||||||
|
| lcall extern push_uint
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT16_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| mov r1, r1, lsl #16
|
||||||
|
| mov r1, r1, asr #16
|
||||||
|
| lcall extern push_int
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT16_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| mov r1, r1, lsl #16
|
||||||
|
| mov r1, r1, lsr #16
|
||||||
|
| lcall extern push_uint
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENUM_TYPE:
|
||||||
|
case INT32_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| lcall extern push_int
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT32_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| lcall extern push_uint
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLOAT_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldr r1, [DATA], #4
|
||||||
|
| lcall extern push_float
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOUBLE_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| ldmia DATA!, {r1, r2}
|
||||||
|
| lcall extern lua_pushnumber
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
luaL_error(L, "NYI: callback arg type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_rawgeti(L, ct_usr, 0);
|
||||||
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
||||||
|
|
||||||
|
| mov r3, #0
|
||||||
|
| mov r2, #((mt->pointers || mt->type != VOID_TYPE) ? 1 : 0)
|
||||||
|
| mov r1, #nargs
|
||||||
|
| lcall extern lua_callk
|
||||||
|
|
||||||
|
if (mt->pointers) {
|
||||||
|
lua_getuservalue(L, -1);
|
||||||
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
||||||
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
||||||
|
|
||||||
|
| mov r2, #num_upvals-1 // usr value
|
||||||
|
| mvn r1, #1 // -2 stack is (upval table, ret val)
|
||||||
|
| lcall extern lua_rawgeti
|
||||||
|
| load32 r3, mt
|
||||||
|
| mov r2, #0 // -1 - ct_usr
|
||||||
|
| mvn r1, #1 // -2 - val
|
||||||
|
| lcall extern to_typed_pointer
|
||||||
|
| mov DATA, r0
|
||||||
|
| mvn r1, #3 // -4 - remove 3 (upval table, ret val, usr value)
|
||||||
|
| lcall extern lua_settop
|
||||||
|
| mov r0, DATA
|
||||||
|
} else {
|
||||||
|
switch (mt->type) {
|
||||||
|
case ENUM_TYPE:
|
||||||
|
lua_getuservalue(L, -1);
|
||||||
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
||||||
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
||||||
|
|
||||||
|
| mov r2, #num_upvals-1 // usr value
|
||||||
|
| mvn r1, #1 // -2 stack is (upval table, ret val)
|
||||||
|
| lcall extern lua_rawgeti
|
||||||
|
| load32 r3, mt
|
||||||
|
| mvn r2, #0 // -1 - ct_usr
|
||||||
|
| mvn r1, #1 // -2 - val
|
||||||
|
| lcall extern to_enum
|
||||||
|
| mov DATA, r0
|
||||||
|
| mvn r1, #3 // -4 - remove 3 (upval table, ret val, usr value)
|
||||||
|
| lcall extern lua_settop
|
||||||
|
| mov r0, DATA
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VOID_TYPE:
|
||||||
|
| mvn r1, #1 // -2
|
||||||
|
| lcall extern lua_settop
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOL_TYPE:
|
||||||
|
case INT8_TYPE:
|
||||||
|
case INT16_TYPE:
|
||||||
|
case INT32_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_int32
|
||||||
|
goto single;
|
||||||
|
|
||||||
|
case UINT8_TYPE:
|
||||||
|
case UINT16_TYPE:
|
||||||
|
case UINT32_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_uint32
|
||||||
|
goto single;
|
||||||
|
|
||||||
|
case INT64_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_int64
|
||||||
|
goto dual;
|
||||||
|
|
||||||
|
case UINT64_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_uint64
|
||||||
|
goto dual;
|
||||||
|
|
||||||
|
case UINTPTR_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_uintptr
|
||||||
|
goto single;
|
||||||
|
|
||||||
|
case FLOAT_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_float
|
||||||
|
goto single;
|
||||||
|
|
||||||
|
case DOUBLE_TYPE:
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern to_double
|
||||||
|
goto dual;
|
||||||
|
|
||||||
|
single:
|
||||||
|
| mov DATA, r0
|
||||||
|
| mvn r1, #2 // -3
|
||||||
|
| lcall extern lua_settop
|
||||||
|
| mov r0, DATA
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
dual:
|
||||||
|
| mov DATA, r0
|
||||||
|
| mov DATA2, r1
|
||||||
|
| mvn r1, #2 // -3
|
||||||
|
| lcall extern lua_settop
|
||||||
|
| mov r0, DATA
|
||||||
|
| mov r1, DATA2
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
luaL_error(L, "NYI: callback return type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
| ldmia sp, {TOP, L_ARG, DATA, DATA2, sp, pc}
|
||||||
|
|
||||||
|
lua_pop(L, 1); /* upval table - already in registry */
|
||||||
|
assert(lua_gettop(L) == top);
|
||||||
|
|
||||||
|
{
|
||||||
|
void* p;
|
||||||
|
struct ctype ft;
|
||||||
|
function_t func;
|
||||||
|
|
||||||
|
func = compile(jit, L, NULL, ref);
|
||||||
|
|
||||||
|
ft = *ct;
|
||||||
|
ft.is_jitted = 1;
|
||||||
|
p = push_cdata(L, ct_usr, &ft);
|
||||||
|
*(function_t*) p = func;
|
||||||
|
|
||||||
|
assert(lua_gettop(L) == top + 1);
|
||||||
|
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_function(struct jit* jit, lua_State* L, function_t func, int ct_usr, const struct ctype* ct)
|
||||||
|
{
|
||||||
|
struct jit* Dst = jit;
|
||||||
|
int i, nargs, num_upvals;
|
||||||
|
const struct ctype* mt;
|
||||||
|
void* p;
|
||||||
|
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
nargs = (int) lua_rawlen(L, ct_usr);
|
||||||
|
|
||||||
|
p = push_cdata(L, ct_usr, ct);
|
||||||
|
*(function_t*) p = func;
|
||||||
|
num_upvals = 1;
|
||||||
|
|
||||||
|
dasm_setup(Dst, build_actionlist);
|
||||||
|
|
||||||
|
| mov r12, sp
|
||||||
|
| push {r0}
|
||||||
|
| push {TOP, L_ARG, DATA, DATA2, r11, r12, lr}
|
||||||
|
| sub r11, r12, #4
|
||||||
|
| mov L_ARG, r0
|
||||||
|
| lcall extern lua_gettop
|
||||||
|
| mov TOP, r0
|
||||||
|
| cmp TOP, #nargs
|
||||||
|
| // these should really be in globals - but for some reason dynasm breaks when you do that
|
||||||
|
if (ct->has_var_arg) {
|
||||||
|
| bge >1
|
||||||
|
| load32 r1, "too few arguments"
|
||||||
|
| lcall extern luaL_error
|
||||||
|
|1:
|
||||||
|
} else {
|
||||||
|
| beq >1
|
||||||
|
| load32 r1, "incorrect number of arguments"
|
||||||
|
| lcall extern luaL_error
|
||||||
|
|1:
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reserve enough stack space for all of the arguments (8 bytes per
|
||||||
|
* argument for double and maintains alignment). Add an extra 16 bytes so
|
||||||
|
* that the pop {r0, r1, r2, r3} doesn't clean out our stack frame */
|
||||||
|
| sub sp, sp, TOP, lsl #3
|
||||||
|
| sub sp, sp, #16
|
||||||
|
| mov DATA, sp
|
||||||
|
|
||||||
|
for (i = 1; i <= nargs; i++) {
|
||||||
|
lua_rawgeti(L, ct_usr, i);
|
||||||
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
||||||
|
|
||||||
|
if (mt->pointers || mt->type == FUNCTION_PTR_TYPE || mt->type == ENUM_TYPE) {
|
||||||
|
lua_getuservalue(L, -1);
|
||||||
|
num_upvals += 2;
|
||||||
|
|
||||||
|
| ldr r3, [pc, #4]
|
||||||
|
| ldr r2, [pc, #4]
|
||||||
|
| b >1
|
||||||
|
|.long mt, lua_upvalueindex(num_upvals)
|
||||||
|
|1:
|
||||||
|
| mov r1, #i
|
||||||
|
| mov r0, L_ARG
|
||||||
|
|
||||||
|
if (mt->pointers) {
|
||||||
|
| bl extern to_typed_pointer
|
||||||
|
} else if (mt->type == FUNCTION_PTR_TYPE) {
|
||||||
|
| bl extern to_typed_function
|
||||||
|
} else if (mt->type == ENUM_TYPE) {
|
||||||
|
| bl extern to_enum
|
||||||
|
}
|
||||||
|
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov r1, #i
|
||||||
|
|
||||||
|
switch (mt->type) {
|
||||||
|
case INT8_TYPE:
|
||||||
|
| lcall extern to_int32
|
||||||
|
| mov r0, r0, lsl #24
|
||||||
|
| mov r0, r0, asr #24
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT16_TYPE:
|
||||||
|
| lcall extern to_int32
|
||||||
|
| mov r0, r0, lsl #16
|
||||||
|
| mov r0, r0, asr #16
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT32_TYPE:
|
||||||
|
| lcall extern to_int32
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT8_TYPE:
|
||||||
|
| lcall extern to_uint32
|
||||||
|
| and r0, r0, #0xFF
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT16_TYPE:
|
||||||
|
| lcall extern to_uint32
|
||||||
|
| mov r0, r0, lsl #16
|
||||||
|
| mov r0, r0, lsr #16
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT32_TYPE:
|
||||||
|
| lcall extern to_uint32
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT64_TYPE:
|
||||||
|
| lcall extern to_int64
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
| str r1, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT64_TYPE:
|
||||||
|
| lcall extern to_uint64
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
| str r1, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOUBLE_TYPE:
|
||||||
|
| lcall extern to_double
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
| str r1, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINTPTR_TYPE:
|
||||||
|
| lcall extern to_uintptr
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLOAT_TYPE:
|
||||||
|
| lcall extern to_float
|
||||||
|
| str r0, [DATA], #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
luaL_error(L, "NYI: call arg type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct->has_var_arg) {
|
||||||
|
| mov r3, DATA
|
||||||
|
| mov r2, TOP
|
||||||
|
| mov r1, #nargs+1
|
||||||
|
| lcall extern unpack_varargs_stack
|
||||||
|
}
|
||||||
|
|
||||||
|
| load32 r0, &jit->last_errno
|
||||||
|
| ldr r0, [r0]
|
||||||
|
| bl extern SetLastError
|
||||||
|
|
||||||
|
| pop {r0, r1, r2, r3} // this pop is balanced with the sub sp, #16
|
||||||
|
| bl extern FUNCTION
|
||||||
|
|
||||||
|
|.macro get_errno
|
||||||
|
| bl extern GetLastError
|
||||||
|
| load32 r1, &jit->last_errno
|
||||||
|
| str r0, [r1]
|
||||||
|
|.endmacro
|
||||||
|
|
||||||
|
|.macro return
|
||||||
|
| ldmdb r11, {TOP, L_ARG, DATA, r11, sp, pc}
|
||||||
|
|.endmacro
|
||||||
|
|
||||||
|
lua_rawgeti(L, ct_usr, 0);
|
||||||
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
||||||
|
|
||||||
|
if (mt->pointers) {
|
||||||
|
lua_getuservalue(L, -1);
|
||||||
|
num_upvals += 2;
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| ldr r2, [pc, #4]
|
||||||
|
| ldr r1, [pc, #4]
|
||||||
|
| b >1
|
||||||
|
|.long mt, lua_upvalueindex(num_upvals)
|
||||||
|
|1:
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| str DATA, [r0]
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
|
||||||
|
} else {
|
||||||
|
switch (mt->type) {
|
||||||
|
case INT64_TYPE:
|
||||||
|
case UINT64_TYPE:
|
||||||
|
num_upvals++;
|
||||||
|
| mov DATA, r0
|
||||||
|
| mov DATA2, r1
|
||||||
|
| get_errno
|
||||||
|
| lcall extern lua_pushnil
|
||||||
|
| load32 r2, mt
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| str DATA, [r0]
|
||||||
|
| str DATA2, [r0, #4]
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINTPTR_TYPE:
|
||||||
|
num_upvals++;
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| lcall extern lua_pushnil
|
||||||
|
| load32 r2, mt
|
||||||
|
| mvn r1, #0 // -1
|
||||||
|
| lcall extern push_cdata
|
||||||
|
| str DATA, [r0]
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VOID_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| get_errno
|
||||||
|
| mov r0, #0
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOL_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| mov r1, DATA
|
||||||
|
| lcall extern lua_pushboolean
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT8_TYPE:
|
||||||
|
case INT16_TYPE:
|
||||||
|
case INT32_TYPE:
|
||||||
|
case ENUM_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| mov r1, DATA
|
||||||
|
| lcall extern push_int
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UINT8_TYPE:
|
||||||
|
case UINT16_TYPE:
|
||||||
|
case UINT32_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| mov r1, DATA
|
||||||
|
| lcall extern push_uint
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLOAT_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov DATA, r0
|
||||||
|
| get_errno
|
||||||
|
| mov r1, DATA
|
||||||
|
| lcall extern push_float
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOUBLE_TYPE:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
| mov DATA, r0
|
||||||
|
| mov DATA2, r1
|
||||||
|
| get_errno
|
||||||
|
| mov r2, DATA2
|
||||||
|
| mov r1, DATA
|
||||||
|
| lcall extern lua_pushnumber
|
||||||
|
| mov r0, #1
|
||||||
|
| return
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
luaL_error(L, "NYI: call return type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(lua_gettop(L) == top + num_upvals);
|
||||||
|
lua_pushcclosure(L, (lua_CFunction) compile(jit, L, func, LUA_NOREF), num_upvals);
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,268 @@
|
||||||
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
||||||
|
*/
|
||||||
|
#include "ffi.h"
|
||||||
|
|
||||||
|
static int to_define_key;
|
||||||
|
|
||||||
|
static void update_on_definition(lua_State* L, int ct_usr, int ct_idx)
|
||||||
|
{
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
ct_idx = lua_absindex(L, ct_idx);
|
||||||
|
|
||||||
|
lua_pushlightuserdata(L, &to_define_key);
|
||||||
|
lua_rawget(L, ct_usr);
|
||||||
|
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1); /* pop the nil */
|
||||||
|
|
||||||
|
/* {} */
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
/* {__mode='k'} */
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_pushliteral(L, "k");
|
||||||
|
lua_setfield(L, -2, "__mode");
|
||||||
|
|
||||||
|
/* setmetatable({}, {__mode='k'}) */
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
/* usr[TO_UPDATE_KEY] = setmetatable({}, {__mode='k'}) */
|
||||||
|
lua_pushlightuserdata(L, &to_define_key);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_rawset(L, ct_usr);
|
||||||
|
|
||||||
|
/* leave the table on the stack */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* to_update[ctype or cdata] = true */
|
||||||
|
lua_pushvalue(L, ct_idx);
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
|
||||||
|
/* pop the to_update table */
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_defined(lua_State* L, int ct_usr, struct ctype* ct)
|
||||||
|
{
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
|
||||||
|
ct->is_defined = 1;
|
||||||
|
|
||||||
|
/* update ctypes and cdatas that were created before the definition came in */
|
||||||
|
lua_pushlightuserdata(L, &to_define_key);
|
||||||
|
lua_rawget(L, ct_usr);
|
||||||
|
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
while (lua_next(L, -2)) {
|
||||||
|
struct ctype* upd = (struct ctype*) lua_touserdata(L, -2);
|
||||||
|
upd->base_size = ct->base_size;
|
||||||
|
upd->align_mask = ct->align_mask;
|
||||||
|
upd->is_defined = 1;
|
||||||
|
upd->is_variable_struct = ct->is_variable_struct;
|
||||||
|
upd->variable_increment = ct->variable_increment;
|
||||||
|
assert(!upd->variable_size_known);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
/* usr[TO_UPDATE_KEY] = nil */
|
||||||
|
lua_pushlightuserdata(L, &to_define_key);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_rawset(L, ct_usr);
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct)
|
||||||
|
{
|
||||||
|
struct ctype* ret;
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
|
||||||
|
ret = (struct ctype*) lua_newuserdata(L, sizeof(struct ctype));
|
||||||
|
*ret = *ct;
|
||||||
|
|
||||||
|
push_upval(L, &ctype_mt_key);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM == 501
|
||||||
|
if (!ct_usr || lua_isnil(L, ct_usr)) {
|
||||||
|
push_upval(L, &niluv_key);
|
||||||
|
lua_setfenv(L, -2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ct_usr && !lua_isnil(L, ct_usr)) {
|
||||||
|
lua_pushvalue(L, ct_usr);
|
||||||
|
lua_setuservalue(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) {
|
||||||
|
update_on_definition(L, ct_usr, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ctype_size(lua_State* L, const struct ctype* ct)
|
||||||
|
{
|
||||||
|
if (ct->pointers - ct->is_array) {
|
||||||
|
return sizeof(void*) * (ct->is_array ? ct->array_size : 1);
|
||||||
|
|
||||||
|
} else if (!ct->is_defined || ct->type == VOID_TYPE) {
|
||||||
|
return luaL_error(L, "can't calculate size of an undefined type");
|
||||||
|
|
||||||
|
} else if (ct->variable_size_known) {
|
||||||
|
assert(ct->is_variable_struct && !ct->is_array);
|
||||||
|
return ct->base_size + ct->variable_increment;
|
||||||
|
|
||||||
|
} else if (ct->is_variable_array || ct->is_variable_struct) {
|
||||||
|
return luaL_error(L, "internal error: calc size of variable type with unknown size");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return ct->base_size * (ct->is_array ? ct->array_size : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct)
|
||||||
|
{
|
||||||
|
struct cdata* cd;
|
||||||
|
size_t sz = ct->is_reference ? sizeof(void*) : ctype_size(L, ct);
|
||||||
|
ct_usr = lua_absindex(L, ct_usr);
|
||||||
|
|
||||||
|
/* This is to stop valgrind from complaining. Bitfields are accessed in 8
|
||||||
|
* byte chunks so that the code doesn't have to deal with different access
|
||||||
|
* patterns, but this means that occasionally it will read past the end of
|
||||||
|
* the struct. As its not setting the bits past the end (only reading and
|
||||||
|
* then writing the bits back) and the read is aligned its a non-issue,
|
||||||
|
* but valgrind complains nonetheless.
|
||||||
|
*/
|
||||||
|
if (ct->has_bitfield) {
|
||||||
|
sz = ALIGN_UP(sz, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
cd = (struct cdata*) lua_newuserdata(L, sizeof(struct cdata) + sz);
|
||||||
|
*(struct ctype*) &cd->type = *ct;
|
||||||
|
memset(cd+1, 0, sz);
|
||||||
|
|
||||||
|
/* TODO: handle cases where lua_newuserdata returns a pointer that is not
|
||||||
|
* aligned */
|
||||||
|
#if 0
|
||||||
|
assert((uintptr_t) (cd + 1) % 8 == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM == 501
|
||||||
|
if (!ct_usr || lua_isnil(L, ct_usr)) {
|
||||||
|
push_upval(L, &niluv_key);
|
||||||
|
lua_setfenv(L, -2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ct_usr && !lua_isnil(L, ct_usr)) {
|
||||||
|
lua_pushvalue(L, ct_usr);
|
||||||
|
lua_setuservalue(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
push_upval(L, &cdata_mt_key);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) {
|
||||||
|
update_on_definition(L, ct_usr, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cd+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_callback(lua_State* L, cfunction f)
|
||||||
|
{
|
||||||
|
cfunction* pf = (cfunction*) lua_newuserdata(L, sizeof(cfunction));
|
||||||
|
*pf = f;
|
||||||
|
|
||||||
|
push_upval(L, &callback_mt_key);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns the value as a ctype, pushes the user value onto the stack */
|
||||||
|
void check_ctype(lua_State* L, int idx, struct ctype* ct)
|
||||||
|
{
|
||||||
|
if (lua_isstring(L, idx)) {
|
||||||
|
struct parser P;
|
||||||
|
P.line = 1;
|
||||||
|
P.prev = P.next = lua_tostring(L, idx);
|
||||||
|
P.align_mask = DEFAULT_ALIGN_MASK;
|
||||||
|
parse_type(L, &P, ct);
|
||||||
|
parse_argument(L, &P, -1, ct, NULL, NULL);
|
||||||
|
lua_remove(L, -2); /* remove the user value from parse_type */
|
||||||
|
|
||||||
|
} else if (lua_getmetatable(L, idx)) {
|
||||||
|
if (!equals_upval(L, -1, &ctype_mt_key)
|
||||||
|
&& !equals_upval(L, -1, &cdata_mt_key)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1); /* pop the metatable */
|
||||||
|
*ct = *(struct ctype*) lua_touserdata(L, idx);
|
||||||
|
lua_getuservalue(L, idx);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
luaL_error(L, "expected cdata, ctype or string for arg #%d", idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* to_cdata returns the struct cdata* and pushes the user value onto the
|
||||||
|
* stack. If the index is not a ctype then ct is not touched, a nil is pushed,
|
||||||
|
* NULL is returned, and ct->type is set to INVALID_TYPE. Also dereferences
|
||||||
|
* references */
|
||||||
|
void* to_cdata(lua_State* L, int idx, struct ctype* ct)
|
||||||
|
{
|
||||||
|
struct cdata* cd;
|
||||||
|
|
||||||
|
ct->type = INVALID_TYPE;
|
||||||
|
if (!lua_isuserdata(L, idx) || !lua_getmetatable(L, idx)) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!equals_upval(L, -1, &cdata_mt_key)) {
|
||||||
|
lua_pop(L, 1); /* mt */
|
||||||
|
lua_pushnil(L);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1); /* mt */
|
||||||
|
cd = (struct cdata*) lua_touserdata(L, idx);
|
||||||
|
*ct = cd->type;
|
||||||
|
lua_getuservalue(L, idx);
|
||||||
|
|
||||||
|
if (ct->is_reference) {
|
||||||
|
ct->is_reference = 0;
|
||||||
|
return *(void**) (cd+1);
|
||||||
|
|
||||||
|
} else if (ct->pointers && !ct->is_array) {
|
||||||
|
return *(void**) (cd+1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return cd + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check_cdata returns the struct cdata* and pushes the user value onto the
|
||||||
|
* stack. Also dereferences references. */
|
||||||
|
void* check_cdata(lua_State* L, int idx, struct ctype* ct)
|
||||||
|
{
|
||||||
|
void* p = to_cdata(L, idx, ct);
|
||||||
|
if (ct->type == INVALID_TYPE) {
|
||||||
|
luaL_error(L, "expected cdata for arg #%d", idx);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,455 @@
|
||||||
|
/*
|
||||||
|
** DynASM ARM encoding engine.
|
||||||
|
** Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define DASM_ARCH "arm"
|
||||||
|
|
||||||
|
#ifndef DASM_EXTERN
|
||||||
|
#define DASM_EXTERN(a,b,c,d) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Action definitions. */
|
||||||
|
enum {
|
||||||
|
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||||
|
/* The following actions need a buffer position. */
|
||||||
|
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||||
|
/* The following actions also have an argument. */
|
||||||
|
DASM_REL_PC, DASM_LABEL_PC,
|
||||||
|
DASM_LONG, DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12,
|
||||||
|
DASM__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||||
|
#define DASM_MAXSECPOS 25
|
||||||
|
|
||||||
|
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||||
|
#define DASM_S_OK 0x00000000
|
||||||
|
#define DASM_S_NOMEM 0x01000000
|
||||||
|
#define DASM_S_PHASE 0x02000000
|
||||||
|
#define DASM_S_MATCH_SEC 0x03000000
|
||||||
|
#define DASM_S_RANGE_I 0x11000000
|
||||||
|
#define DASM_S_RANGE_SEC 0x12000000
|
||||||
|
#define DASM_S_RANGE_LG 0x13000000
|
||||||
|
#define DASM_S_RANGE_PC 0x14000000
|
||||||
|
#define DASM_S_RANGE_REL 0x15000000
|
||||||
|
#define DASM_S_UNDEF_LG 0x21000000
|
||||||
|
#define DASM_S_UNDEF_PC 0x22000000
|
||||||
|
|
||||||
|
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||||
|
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||||
|
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||||
|
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||||
|
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||||
|
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||||
|
|
||||||
|
/* Action list type. */
|
||||||
|
typedef const unsigned int *dasm_ActList;
|
||||||
|
|
||||||
|
/* Per-section structure. */
|
||||||
|
typedef struct dasm_Section {
|
||||||
|
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||||
|
int *buf; /* True buffer pointer. */
|
||||||
|
size_t bsize; /* Buffer size in bytes. */
|
||||||
|
int pos; /* Biased buffer position. */
|
||||||
|
int epos; /* End of biased buffer position - max single put. */
|
||||||
|
int ofs; /* Byte offset into section. */
|
||||||
|
} dasm_Section;
|
||||||
|
|
||||||
|
/* Core structure holding the DynASM encoding state. */
|
||||||
|
struct dasm_State {
|
||||||
|
size_t psize; /* Allocated size of this structure. */
|
||||||
|
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||||
|
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||||
|
size_t lgsize;
|
||||||
|
int *pclabels; /* PC label chains/pos ptrs. */
|
||||||
|
size_t pcsize;
|
||||||
|
void **globals; /* Array of globals (bias -10). */
|
||||||
|
dasm_Section *section; /* Pointer to active section. */
|
||||||
|
size_t codesize; /* Total size of all code sections. */
|
||||||
|
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||||
|
int status; /* Status code. */
|
||||||
|
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The size of the core structure depends on the max. number of sections. */
|
||||||
|
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize DynASM state. */
|
||||||
|
void dasm_init(Dst_DECL, int maxsection)
|
||||||
|
{
|
||||||
|
dasm_State *D;
|
||||||
|
size_t psz = 0;
|
||||||
|
int i;
|
||||||
|
Dst_REF = NULL;
|
||||||
|
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||||
|
D = Dst_REF;
|
||||||
|
D->psize = psz;
|
||||||
|
D->lglabels = NULL;
|
||||||
|
D->lgsize = 0;
|
||||||
|
D->pclabels = NULL;
|
||||||
|
D->pcsize = 0;
|
||||||
|
D->globals = NULL;
|
||||||
|
D->maxsection = maxsection;
|
||||||
|
for (i = 0; i < maxsection; i++) {
|
||||||
|
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||||
|
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||||
|
D->sections[i].bsize = 0;
|
||||||
|
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free DynASM state. */
|
||||||
|
void dasm_free(Dst_DECL)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < D->maxsection; i++)
|
||||||
|
if (D->sections[i].buf)
|
||||||
|
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||||
|
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||||
|
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||||
|
DASM_M_FREE(Dst, D, D->psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup global label array. Must be called before dasm_setup(). */
|
||||||
|
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||||
|
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||||
|
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
size_t osz = D->pcsize;
|
||||||
|
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||||
|
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup encoder. */
|
||||||
|
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
D->actionlist = (dasm_ActList)actionlist;
|
||||||
|
D->status = DASM_S_OK;
|
||||||
|
D->section = &D->sections[0];
|
||||||
|
memset((void *)D->lglabels, 0, D->lgsize);
|
||||||
|
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||||
|
for (i = 0; i < D->maxsection; i++) {
|
||||||
|
D->sections[i].pos = DASM_SEC2POS(i);
|
||||||
|
D->sections[i].ofs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
#define CK(x, st) \
|
||||||
|
do { if (!(x)) { \
|
||||||
|
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#define CKPL(kind, st) \
|
||||||
|
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||||
|
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#else
|
||||||
|
#define CK(x, st) ((void)0)
|
||||||
|
#define CKPL(kind, st) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int dasm_imm12(unsigned int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
|
||||||
|
if (n <= 255) return (int)(n + (i << 8));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||||
|
void dasm_put(Dst_DECL, int start, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
dasm_ActList p = D->actionlist + start;
|
||||||
|
dasm_Section *sec = D->section;
|
||||||
|
int pos = sec->pos, ofs = sec->ofs;
|
||||||
|
int *b;
|
||||||
|
|
||||||
|
if (pos >= sec->epos) {
|
||||||
|
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||||
|
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||||
|
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||||
|
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
b = sec->rbuf;
|
||||||
|
b[pos++] = start;
|
||||||
|
|
||||||
|
va_start(ap, start);
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
if (action >= DASM__MAX) {
|
||||||
|
ofs += 4;
|
||||||
|
} else {
|
||||||
|
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: goto stop;
|
||||||
|
case DASM_SECTION:
|
||||||
|
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||||
|
D->section = &D->sections[n]; goto stop;
|
||||||
|
case DASM_ESC: p++; ofs += 4; break;
|
||||||
|
case DASM_REL_EXT: break;
|
||||||
|
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||||
|
case DASM_REL_LG:
|
||||||
|
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||||
|
if (n >= 0) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
|
||||||
|
pl += 10; n = *pl;
|
||||||
|
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||||
|
goto linkrel;
|
||||||
|
case DASM_REL_PC:
|
||||||
|
pl = D->pclabels + n; CKPL(pc, PC);
|
||||||
|
putrel:
|
||||||
|
n = *pl;
|
||||||
|
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||||
|
b[pos] = -n;
|
||||||
|
} else {
|
||||||
|
linkrel:
|
||||||
|
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||||
|
*pl = pos;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_LG:
|
||||||
|
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||||
|
case DASM_LABEL_PC:
|
||||||
|
pl = D->pclabels + n; CKPL(pc, PC);
|
||||||
|
putlabel:
|
||||||
|
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||||
|
}
|
||||||
|
*pl = -pos; /* Label exists now. */
|
||||||
|
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||||
|
break;
|
||||||
|
case DASM_LONG:
|
||||||
|
ofs += 4;
|
||||||
|
b[pos++] = n;
|
||||||
|
break;
|
||||||
|
case DASM_IMM:
|
||||||
|
case DASM_IMM16:
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||||
|
if ((ins & 0x8000))
|
||||||
|
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||||
|
else
|
||||||
|
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||||
|
#endif
|
||||||
|
b[pos++] = n;
|
||||||
|
break;
|
||||||
|
case DASM_IMML8:
|
||||||
|
case DASM_IMML12:
|
||||||
|
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
|
||||||
|
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
|
||||||
|
b[pos++] = n;
|
||||||
|
break;
|
||||||
|
case DASM_IMM12:
|
||||||
|
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||||
|
b[pos++] = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
va_end(ap);
|
||||||
|
sec->pos = pos;
|
||||||
|
sec->ofs = ofs;
|
||||||
|
}
|
||||||
|
#undef CK
|
||||||
|
|
||||||
|
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||||
|
int dasm_link(Dst_DECL, size_t *szp)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int secnum;
|
||||||
|
int ofs = 0;
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
*szp = 0;
|
||||||
|
if (D->status != DASM_S_OK) return D->status;
|
||||||
|
{
|
||||||
|
int pc;
|
||||||
|
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||||
|
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{ /* Handle globals not defined in this translation unit. */
|
||||||
|
int idx;
|
||||||
|
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||||
|
int n = D->lglabels[idx];
|
||||||
|
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combine all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->rbuf;
|
||||||
|
int pos = DASM_SEC2POS(secnum);
|
||||||
|
int lastpos = sec->pos;
|
||||||
|
|
||||||
|
while (pos != lastpos) {
|
||||||
|
dasm_ActList p = D->actionlist + b[pos++];
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||||
|
case DASM_ESC: p++; break;
|
||||||
|
case DASM_REL_EXT: break;
|
||||||
|
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||||
|
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||||
|
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||||
|
case DASM_LONG: case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
|
||||||
|
case DASM_IMML8: case DASM_IMML12: pos++; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||||
|
}
|
||||||
|
|
||||||
|
D->codesize = ofs; /* Total size of all code sections */
|
||||||
|
*szp = ofs;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
#define CK(x, st) \
|
||||||
|
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||||
|
#else
|
||||||
|
#define CK(x, st) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pass 3: Encode sections. */
|
||||||
|
int dasm_encode(Dst_DECL, void *buffer)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
char *base = (char *)buffer;
|
||||||
|
unsigned int *cp = (unsigned int *)buffer;
|
||||||
|
int secnum;
|
||||||
|
|
||||||
|
/* Encode all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->buf;
|
||||||
|
int *endb = sec->rbuf + sec->pos;
|
||||||
|
|
||||||
|
while (b != endb) {
|
||||||
|
dasm_ActList p = D->actionlist + *b++;
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||||
|
case DASM_ESC: *cp++ = *p++; break;
|
||||||
|
case DASM_REL_EXT:
|
||||||
|
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||||
|
goto patchrel;
|
||||||
|
case DASM_ALIGN:
|
||||||
|
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
|
||||||
|
break;
|
||||||
|
case DASM_REL_LG:
|
||||||
|
CK(n >= 0, UNDEF_LG);
|
||||||
|
case DASM_REL_PC:
|
||||||
|
CK(n >= 0, UNDEF_PC);
|
||||||
|
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4;
|
||||||
|
patchrel:
|
||||||
|
if ((ins & 0x800) == 0) {
|
||||||
|
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
|
||||||
|
cp[-1] |= ((n >> 2) & 0x00ffffff);
|
||||||
|
} else if ((ins & 0x1000)) {
|
||||||
|
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
|
||||||
|
goto patchimml8;
|
||||||
|
} else {
|
||||||
|
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
|
||||||
|
goto patchimml12;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_LG:
|
||||||
|
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_PC: break;
|
||||||
|
case DASM_LONG:
|
||||||
|
*cp++ = n;
|
||||||
|
break;
|
||||||
|
case DASM_IMM:
|
||||||
|
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||||
|
break;
|
||||||
|
case DASM_IMM12:
|
||||||
|
cp[-1] |= dasm_imm12((unsigned int)n);
|
||||||
|
break;
|
||||||
|
case DASM_IMM16:
|
||||||
|
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
|
||||||
|
break;
|
||||||
|
case DASM_IMML8: patchimml8:
|
||||||
|
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
|
||||||
|
((-n & 0x0f) | ((-n & 0xf0) << 4));
|
||||||
|
break;
|
||||||
|
case DASM_IMML12: patchimml12:
|
||||||
|
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
|
||||||
|
break;
|
||||||
|
default: *cp++ = ins; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||||
|
return DASM_S_PHASE;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
#undef CK
|
||||||
|
|
||||||
|
/* Get PC label offset. */
|
||||||
|
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (pc*sizeof(int) < D->pcsize) {
|
||||||
|
int pos = D->pclabels[pc];
|
||||||
|
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||||
|
if (pos > 0) return -1; /* Undefined. */
|
||||||
|
}
|
||||||
|
return -2; /* Unused or out of range. */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
/* Optional sanity checker to call between isolated encoding steps. */
|
||||||
|
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (D->status == DASM_S_OK) {
|
||||||
|
int i;
|
||||||
|
for (i = 1; i <= 9; i++) {
|
||||||
|
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||||
|
D->lglabels[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||||
|
D->section != &D->sections[secmatch])
|
||||||
|
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||||
|
return D->status;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,952 @@
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- DynASM ARM module.
|
||||||
|
--
|
||||||
|
-- Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
-- See dynasm.lua for full copyright notice.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Module information:
|
||||||
|
local _info = {
|
||||||
|
arch = "arm",
|
||||||
|
description = "DynASM ARM module",
|
||||||
|
version = "1.3.0",
|
||||||
|
vernum = 10300,
|
||||||
|
release = "2011-05-05",
|
||||||
|
author = "Mike Pall",
|
||||||
|
license = "MIT",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Exported glue functions for the arch-specific module.
|
||||||
|
local _M = { _info = _info }
|
||||||
|
|
||||||
|
-- Cache library functions.
|
||||||
|
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
|
||||||
|
local assert, setmetatable, rawget = assert, setmetatable, rawget
|
||||||
|
local _s = string
|
||||||
|
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
|
||||||
|
local match, gmatch, gsub = _s.match, _s.gmatch, _s.gsub
|
||||||
|
local concat, sort, insert = table.concat, table.sort, table.insert
|
||||||
|
|
||||||
|
-- Inherited tables and callbacks.
|
||||||
|
local g_opt, g_arch
|
||||||
|
local wline, werror, wfatal, wwarn
|
||||||
|
|
||||||
|
-- Action name list.
|
||||||
|
-- CHECK: Keep this in sync with the C code!
|
||||||
|
local action_names = {
|
||||||
|
"STOP", "SECTION", "ESC", "REL_EXT",
|
||||||
|
"ALIGN", "REL_LG", "LABEL_LG",
|
||||||
|
"REL_PC", "LABEL_PC", "LONG", "IMM", "IMM12", "IMM16", "IMML8", "IMML12",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Maximum number of section buffer positions for dasm_put().
|
||||||
|
-- CHECK: Keep this in sync with the C code!
|
||||||
|
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.
|
||||||
|
|
||||||
|
-- Action name -> action number.
|
||||||
|
local map_action = {}
|
||||||
|
for n,name in ipairs(action_names) do
|
||||||
|
map_action[name] = n-1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Action list buffer.
|
||||||
|
local actlist = {}
|
||||||
|
|
||||||
|
-- Argument list for next dasm_put(). Start with offset 0 into action list.
|
||||||
|
local actargs = { 0 }
|
||||||
|
|
||||||
|
-- Current number of section buffer positions for dasm_put().
|
||||||
|
local secpos = 1
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Return 8 digit hex number.
|
||||||
|
local function tohex(x)
|
||||||
|
return sub(format("%08x", x), -8) -- Avoid 64 bit portability problem in Lua.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dump action names and numbers.
|
||||||
|
local function dumpactions(out)
|
||||||
|
out:write("DynASM encoding engine action codes:\n")
|
||||||
|
for n,name in ipairs(action_names) do
|
||||||
|
local num = map_action[name]
|
||||||
|
out:write(format(" %-10s %02X %d\n", name, num, num))
|
||||||
|
end
|
||||||
|
out:write("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write action list buffer as a huge static C array.
|
||||||
|
local function writeactions(out, name)
|
||||||
|
local nn = #actlist
|
||||||
|
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
|
||||||
|
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
|
||||||
|
for i = 1,nn-1 do
|
||||||
|
assert(out:write("0x", tohex(actlist[i]), ",\n"))
|
||||||
|
end
|
||||||
|
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Add word to action list.
|
||||||
|
local function wputxw(n)
|
||||||
|
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||||
|
actlist[#actlist+1] = n
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add action to list with optional arg. Advance buffer pos, too.
|
||||||
|
local function waction(action, val, a, num)
|
||||||
|
local w = assert(map_action[action], "bad action name `"..action.."'")
|
||||||
|
wputxw(w * 0x10000 + (val or 0))
|
||||||
|
if a then actargs[#actargs+1] = a end
|
||||||
|
if a or num then secpos = secpos + (num or 1) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Flush action list (intervening C code or buffer pos overflow).
|
||||||
|
local function wflush(term)
|
||||||
|
if #actlist == actargs[1] then return end -- Nothing to flush.
|
||||||
|
if not term then waction("STOP") end -- Terminate action list.
|
||||||
|
wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true)
|
||||||
|
actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
|
||||||
|
secpos = 1 -- The actionlist offset occupies a buffer position, too.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Put escaped word.
|
||||||
|
local function wputw(n)
|
||||||
|
if n <= 0x000fffff then waction("ESC") end
|
||||||
|
wputxw(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reserve position for word.
|
||||||
|
local function wpos()
|
||||||
|
local pos = #actlist+1
|
||||||
|
actlist[pos] = ""
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Store word to reserved position.
|
||||||
|
local function wputpos(pos, n)
|
||||||
|
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||||
|
if n <= 0x000fffff then
|
||||||
|
insert(actlist, pos+1, n)
|
||||||
|
n = map_action.ESC * 0x10000
|
||||||
|
end
|
||||||
|
actlist[pos] = n
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Global label name -> global label number. With auto assignment on 1st use.
|
||||||
|
local next_global = 20
|
||||||
|
local map_global = setmetatable({}, { __index = function(t, name)
|
||||||
|
if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end
|
||||||
|
local n = next_global
|
||||||
|
if n > 2047 then werror("too many global labels") end
|
||||||
|
next_global = n + 1
|
||||||
|
t[name] = n
|
||||||
|
return n
|
||||||
|
end})
|
||||||
|
|
||||||
|
-- Dump global labels.
|
||||||
|
local function dumpglobals(out, lvl)
|
||||||
|
local t = {}
|
||||||
|
for name, n in pairs(map_global) do t[n] = name end
|
||||||
|
out:write("Global labels:\n")
|
||||||
|
for i=20,next_global-1 do
|
||||||
|
out:write(format(" %s\n", t[i]))
|
||||||
|
end
|
||||||
|
out:write("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write global label enum.
|
||||||
|
local function writeglobals(out, prefix)
|
||||||
|
local t = {}
|
||||||
|
for name, n in pairs(map_global) do t[n] = name end
|
||||||
|
out:write("enum {\n")
|
||||||
|
for i=20,next_global-1 do
|
||||||
|
out:write(" ", prefix, t[i], ",\n")
|
||||||
|
end
|
||||||
|
out:write(" ", prefix, "_MAX\n};\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write global label names.
|
||||||
|
local function writeglobalnames(out, name)
|
||||||
|
local t = {}
|
||||||
|
for name, n in pairs(map_global) do t[n] = name end
|
||||||
|
out:write("static const char *const ", name, "[] = {\n")
|
||||||
|
for i=20,next_global-1 do
|
||||||
|
out:write(" \"", t[i], "\",\n")
|
||||||
|
end
|
||||||
|
out:write(" (const char *)0\n};\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Extern label name -> extern label number. With auto assignment on 1st use.
|
||||||
|
local next_extern = 0
|
||||||
|
local map_extern_ = {}
|
||||||
|
local map_extern = setmetatable({}, { __index = function(t, name)
|
||||||
|
-- No restrictions on the name for now.
|
||||||
|
local n = next_extern
|
||||||
|
if n > 2047 then werror("too many extern labels") end
|
||||||
|
next_extern = n + 1
|
||||||
|
t[name] = n
|
||||||
|
map_extern_[n] = name
|
||||||
|
return n
|
||||||
|
end})
|
||||||
|
|
||||||
|
-- Dump extern labels.
|
||||||
|
local function dumpexterns(out, lvl)
|
||||||
|
out:write("Extern labels:\n")
|
||||||
|
for i=0,next_extern-1 do
|
||||||
|
out:write(format(" %s\n", map_extern_[i]))
|
||||||
|
end
|
||||||
|
out:write("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write extern label names.
|
||||||
|
local function writeexternnames(out, name)
|
||||||
|
out:write("static const char *const ", name, "[] = {\n")
|
||||||
|
for i=0,next_extern-1 do
|
||||||
|
out:write(" \"", map_extern_[i], "\",\n")
|
||||||
|
end
|
||||||
|
out:write(" (const char *)0\n};\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Arch-specific maps.
|
||||||
|
|
||||||
|
-- Ext. register name -> int. name.
|
||||||
|
local map_archdef = { sp = "r13", lr = "r14", pc = "r15", }
|
||||||
|
|
||||||
|
-- Int. register name -> ext. name.
|
||||||
|
local map_reg_rev = { r13 = "sp", r14 = "lr", r15 = "pc", }
|
||||||
|
|
||||||
|
local map_type = {} -- Type name -> { ctype, reg }
|
||||||
|
local ctypenum = 0 -- Type number (for Dt... macros).
|
||||||
|
|
||||||
|
-- Reverse defines for registers.
|
||||||
|
function _M.revdef(s)
|
||||||
|
return map_reg_rev[s] or s
|
||||||
|
end
|
||||||
|
|
||||||
|
local map_shift = { lsl = 0, lsr = 1, asr = 2, ror = 3, }
|
||||||
|
|
||||||
|
local map_cond = {
|
||||||
|
eq = 0, ne = 1, cs = 2, cc = 3, mi = 4, pl = 5, vs = 6, vc = 7,
|
||||||
|
hi = 8, ls = 9, ge = 10, lt = 11, gt = 12, le = 13, al = 14,
|
||||||
|
hs = 2, lo = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Template strings for ARM instructions.
|
||||||
|
local map_op = {
|
||||||
|
-- Basic data processing instructions.
|
||||||
|
and_3 = "e0000000DNPs",
|
||||||
|
eor_3 = "e0200000DNPs",
|
||||||
|
sub_3 = "e0400000DNPs",
|
||||||
|
rsb_3 = "e0600000DNPs",
|
||||||
|
add_3 = "e0800000DNPs",
|
||||||
|
adc_3 = "e0a00000DNPs",
|
||||||
|
sbc_3 = "e0c00000DNPs",
|
||||||
|
rsc_3 = "e0e00000DNPs",
|
||||||
|
tst_2 = "e1100000NP",
|
||||||
|
teq_2 = "e1300000NP",
|
||||||
|
cmp_2 = "e1500000NP",
|
||||||
|
cmn_2 = "e1700000NP",
|
||||||
|
orr_3 = "e1800000DNPs",
|
||||||
|
mov_2 = "e1a00000DPs",
|
||||||
|
bic_3 = "e1c00000DNPs",
|
||||||
|
mvn_2 = "e1e00000DPs",
|
||||||
|
|
||||||
|
and_4 = "e0000000DNMps",
|
||||||
|
eor_4 = "e0200000DNMps",
|
||||||
|
sub_4 = "e0400000DNMps",
|
||||||
|
rsb_4 = "e0600000DNMps",
|
||||||
|
add_4 = "e0800000DNMps",
|
||||||
|
adc_4 = "e0a00000DNMps",
|
||||||
|
sbc_4 = "e0c00000DNMps",
|
||||||
|
rsc_4 = "e0e00000DNMps",
|
||||||
|
tst_3 = "e1100000NMp",
|
||||||
|
teq_3 = "e1300000NMp",
|
||||||
|
cmp_3 = "e1500000NMp",
|
||||||
|
cmn_3 = "e1700000NMp",
|
||||||
|
orr_4 = "e1800000DNMps",
|
||||||
|
mov_3 = "e1a00000DMps",
|
||||||
|
bic_4 = "e1c00000DNMps",
|
||||||
|
mvn_3 = "e1e00000DMps",
|
||||||
|
|
||||||
|
lsl_3 = "e1a00000DMws",
|
||||||
|
lsr_3 = "e1a00020DMws",
|
||||||
|
asr_3 = "e1a00040DMws",
|
||||||
|
ror_3 = "e1a00060DMws",
|
||||||
|
rrx_2 = "e1a00060DMs",
|
||||||
|
|
||||||
|
-- Multiply and multiply-accumulate.
|
||||||
|
mul_3 = "e0000090NMSs",
|
||||||
|
mla_4 = "e0200090NMSDs",
|
||||||
|
umaal_4 = "e0400090DNMSs", -- v6
|
||||||
|
mls_4 = "e0600090DNMSs", -- v6T2
|
||||||
|
umull_4 = "e0800090DNMSs",
|
||||||
|
umlal_4 = "e0a00090DNMSs",
|
||||||
|
smull_4 = "e0c00090DNMSs",
|
||||||
|
smlal_4 = "e0e00090DNMSs",
|
||||||
|
|
||||||
|
-- Halfword multiply and multiply-accumulate.
|
||||||
|
smlabb_4 = "e1000080NMSD", -- v5TE
|
||||||
|
smlatb_4 = "e10000a0NMSD", -- v5TE
|
||||||
|
smlabt_4 = "e10000c0NMSD", -- v5TE
|
||||||
|
smlatt_4 = "e10000e0NMSD", -- v5TE
|
||||||
|
smlawb_4 = "e1200080NMSD", -- v5TE
|
||||||
|
smulwb_3 = "e12000a0NMS", -- v5TE
|
||||||
|
smlawt_4 = "e12000c0NMSD", -- v5TE
|
||||||
|
smulwt_3 = "e12000e0NMS", -- v5TE
|
||||||
|
smlalbb_4 = "e1400080NMSD", -- v5TE
|
||||||
|
smlaltb_4 = "e14000a0NMSD", -- v5TE
|
||||||
|
smlalbt_4 = "e14000c0NMSD", -- v5TE
|
||||||
|
smlaltt_4 = "e14000e0NMSD", -- v5TE
|
||||||
|
smulbb_3 = "e1600080NMS", -- v5TE
|
||||||
|
smultb_3 = "e16000a0NMS", -- v5TE
|
||||||
|
smulbt_3 = "e16000c0NMS", -- v5TE
|
||||||
|
smultt_3 = "e16000e0NMS", -- v5TE
|
||||||
|
|
||||||
|
-- Miscellaneous data processing instructions.
|
||||||
|
clz_2 = "e16f0f10DM", -- v5T
|
||||||
|
rev_2 = "e6bf0f30DM", -- v6
|
||||||
|
rev16_2 = "e6bf0fb0DM", -- v6
|
||||||
|
revsh_2 = "e6ff0fb0DM", -- v6
|
||||||
|
sel_3 = "e6800fb0DNM", -- v6
|
||||||
|
usad8_3 = "e780f010NMS", -- v6
|
||||||
|
usada8_4 = "e7800010NMSD", -- v6
|
||||||
|
rbit_2 = "e6ff0f30DM", -- v6T2
|
||||||
|
movw_2 = "e3000000DW", -- v6T2
|
||||||
|
movt_2 = "e3400000DW", -- v6T2
|
||||||
|
-- Note: the X encodes width-1, not width.
|
||||||
|
sbfx_4 = "e7a00050DMvX", -- v6T2
|
||||||
|
ubfx_4 = "e7e00050DMvX", -- v6T2
|
||||||
|
-- Note: the X encodes the msb field, not the width.
|
||||||
|
bfc_3 = "e7c0001fDvX", -- v6T2
|
||||||
|
bfi_4 = "e7c00010DMvX", -- v6T2
|
||||||
|
|
||||||
|
-- Packing and unpacking instructions.
|
||||||
|
pkhbt_3 = "e6800010DNM", pkhbt_4 = "e6800010DNMv", -- v6
|
||||||
|
pkhtb_3 = "e6800050DNM", pkhtb_4 = "e6800050DNMv", -- v6
|
||||||
|
sxtab_3 = "e6a00070DNM", sxtab_4 = "e6a00070DNMv", -- v6
|
||||||
|
sxtab16_3 = "e6800070DNM", sxtab16_4 = "e6800070DNMv", -- v6
|
||||||
|
sxtah_3 = "e6b00070DNM", sxtah_4 = "e6b00070DNMv", -- v6
|
||||||
|
sxtb_2 = "e6af0070DM", sxtb_3 = "e6af0070DMv", -- v6
|
||||||
|
sxtb16_2 = "e68f0070DM", sxtb16_3 = "e68f0070DMv", -- v6
|
||||||
|
sxth_2 = "e6bf0070DM", sxth_3 = "e6bf0070DMv", -- v6
|
||||||
|
uxtab_3 = "e6e00070DNM", uxtab_4 = "e6e00070DNMv", -- v6
|
||||||
|
uxtab16_3 = "e6c00070DNM", uxtab16_4 = "e6c00070DNMv", -- v6
|
||||||
|
uxtah_3 = "e6f00070DNM", uxtah_4 = "e6f00070DNMv", -- v6
|
||||||
|
uxtb_2 = "e6ef0070DM", uxtb_3 = "e6ef0070DMv", -- v6
|
||||||
|
uxtb16_2 = "e6cf0070DM", uxtb16_3 = "e6cf0070DMv", -- v6
|
||||||
|
uxth_2 = "e6ff0070DM", uxth_3 = "e6ff0070DMv", -- v6
|
||||||
|
|
||||||
|
-- Saturating instructions.
|
||||||
|
qadd_3 = "e1000050DMN", -- v5TE
|
||||||
|
qsub_3 = "e1200050DMN", -- v5TE
|
||||||
|
qdadd_3 = "e1400050DMN", -- v5TE
|
||||||
|
qdsub_3 = "e1600050DMN", -- v5TE
|
||||||
|
-- Note: the X for ssat* encodes sat_imm-1, not sat_imm.
|
||||||
|
ssat_3 = "e6a00010DXM", ssat_4 = "e6a00010DXMp", -- v6
|
||||||
|
usat_3 = "e6e00010DXM", usat_4 = "e6e00010DXMp", -- v6
|
||||||
|
ssat16_3 = "e6a00f30DXM", -- v6
|
||||||
|
usat16_3 = "e6e00f30DXM", -- v6
|
||||||
|
|
||||||
|
-- Parallel addition and subtraction.
|
||||||
|
sadd16_3 = "e6100f10DNM", -- v6
|
||||||
|
sasx_3 = "e6100f30DNM", -- v6
|
||||||
|
ssax_3 = "e6100f50DNM", -- v6
|
||||||
|
ssub16_3 = "e6100f70DNM", -- v6
|
||||||
|
sadd8_3 = "e6100f90DNM", -- v6
|
||||||
|
ssub8_3 = "e6100ff0DNM", -- v6
|
||||||
|
qadd16_3 = "e6200f10DNM", -- v6
|
||||||
|
qasx_3 = "e6200f30DNM", -- v6
|
||||||
|
qsax_3 = "e6200f50DNM", -- v6
|
||||||
|
qsub16_3 = "e6200f70DNM", -- v6
|
||||||
|
qadd8_3 = "e6200f90DNM", -- v6
|
||||||
|
qsub8_3 = "e6200ff0DNM", -- v6
|
||||||
|
shadd16_3 = "e6300f10DNM", -- v6
|
||||||
|
shasx_3 = "e6300f30DNM", -- v6
|
||||||
|
shsax_3 = "e6300f50DNM", -- v6
|
||||||
|
shsub16_3 = "e6300f70DNM", -- v6
|
||||||
|
shadd8_3 = "e6300f90DNM", -- v6
|
||||||
|
shsub8_3 = "e6300ff0DNM", -- v6
|
||||||
|
uadd16_3 = "e6500f10DNM", -- v6
|
||||||
|
uasx_3 = "e6500f30DNM", -- v6
|
||||||
|
usax_3 = "e6500f50DNM", -- v6
|
||||||
|
usub16_3 = "e6500f70DNM", -- v6
|
||||||
|
uadd8_3 = "e6500f90DNM", -- v6
|
||||||
|
usub8_3 = "e6500ff0DNM", -- v6
|
||||||
|
uqadd16_3 = "e6600f10DNM", -- v6
|
||||||
|
uqasx_3 = "e6600f30DNM", -- v6
|
||||||
|
uqsax_3 = "e6600f50DNM", -- v6
|
||||||
|
uqsub16_3 = "e6600f70DNM", -- v6
|
||||||
|
uqadd8_3 = "e6600f90DNM", -- v6
|
||||||
|
uqsub8_3 = "e6600ff0DNM", -- v6
|
||||||
|
uhadd16_3 = "e6700f10DNM", -- v6
|
||||||
|
uhasx_3 = "e6700f30DNM", -- v6
|
||||||
|
uhsax_3 = "e6700f50DNM", -- v6
|
||||||
|
uhsub16_3 = "e6700f70DNM", -- v6
|
||||||
|
uhadd8_3 = "e6700f90DNM", -- v6
|
||||||
|
uhsub8_3 = "e6700ff0DNM", -- v6
|
||||||
|
|
||||||
|
-- Load/store instructions.
|
||||||
|
str_2 = "e4000000DL", str_3 = "e4000000DL", str_4 = "e4000000DL",
|
||||||
|
strb_2 = "e4400000DL", strb_3 = "e4400000DL", strb_4 = "e4400000DL",
|
||||||
|
ldr_2 = "e4100000DL", ldr_3 = "e4100000DL", ldr_4 = "e4100000DL",
|
||||||
|
ldrb_2 = "e4500000DL", ldrb_3 = "e4500000DL", ldrb_4 = "e4500000DL",
|
||||||
|
strh_2 = "e00000b0DL", strh_3 = "e00000b0DL",
|
||||||
|
ldrh_2 = "e01000b0DL", ldrh_3 = "e01000b0DL",
|
||||||
|
ldrd_2 = "e00000d0DL", ldrd_3 = "e00000d0DL", -- v5TE
|
||||||
|
ldrsb_2 = "e01000d0DL", ldrsb_3 = "e01000d0DL",
|
||||||
|
strd_2 = "e00000f0DL", strd_3 = "e00000f0DL", -- v5TE
|
||||||
|
ldrsh_2 = "e01000f0DL", ldrsh_3 = "e01000f0DL",
|
||||||
|
|
||||||
|
ldm_2 = "e8900000nR", ldmia_2 = "e8900000nR", ldmfd_2 = "e8900000nR",
|
||||||
|
ldmda_2 = "e8100000nR", ldmfa_2 = "e8100000nR",
|
||||||
|
ldmdb_2 = "e9100000nR", ldmea_2 = "e9100000nR",
|
||||||
|
ldmib_2 = "e9900000nR", ldmed_2 = "e9900000nR",
|
||||||
|
stm_2 = "e8800000nR", stmia_2 = "e8800000nR", stmfd_2 = "e8800000nR",
|
||||||
|
stmda_2 = "e8000000nR", stmfa_2 = "e8000000nR",
|
||||||
|
stmdb_2 = "e9000000nR", stmea_2 = "e9000000nR",
|
||||||
|
stmib_2 = "e9800000nR", stmed_2 = "e9800000nR",
|
||||||
|
pop_1 = "e8bd0000R", push_1 = "e92d0000R",
|
||||||
|
|
||||||
|
-- Branch instructions.
|
||||||
|
b_1 = "ea000000B",
|
||||||
|
bl_1 = "eb000000B",
|
||||||
|
blx_1 = "e12fff30C",
|
||||||
|
bx_1 = "e12fff10M",
|
||||||
|
|
||||||
|
-- Miscellaneous instructions.
|
||||||
|
nop_0 = "e1a00000",
|
||||||
|
mrs_1 = "e10f0000D",
|
||||||
|
bkpt_1 = "e1200070K", -- v5T
|
||||||
|
svc_1 = "ef000000T", swi_1 = "ef000000T",
|
||||||
|
ud_0 = "e7f001f0",
|
||||||
|
|
||||||
|
-- NYI: Advanced SIMD and VFP instructions.
|
||||||
|
|
||||||
|
-- NYI instructions, since I have no need for them right now:
|
||||||
|
-- swp, swpb, strex, ldrex, strexd, ldrexd, strexb, ldrexb, strexh, ldrexh
|
||||||
|
-- msr, nopv6, yield, wfe, wfi, sev, dbg, bxj, smc, srs, rfe
|
||||||
|
-- cps, setend, pli, pld, pldw, clrex, dsb, dmb, isb
|
||||||
|
-- stc, ldc, mcr, mcr2, mrc, mrc2, mcrr, mcrr2, mrrc, mrrc2, cdp, cdp2
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Add mnemonics for "s" variants.
|
||||||
|
do
|
||||||
|
local t = {}
|
||||||
|
for k,v in pairs(map_op) do
|
||||||
|
if sub(v, -1) == "s" then
|
||||||
|
local v2 = sub(v, 1, 2)..char(byte(v, 3)+1)..sub(v, 4, -2)
|
||||||
|
t[sub(k, 1, -3).."s"..sub(k, -2)] = v2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
map_op[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function parse_gpr(expr)
|
||||||
|
local tname, ovreg = match(expr, "^([%w_]+):(r1?[0-9])$")
|
||||||
|
local tp = map_type[tname or expr]
|
||||||
|
if tp then
|
||||||
|
local reg = ovreg or tp.reg
|
||||||
|
if not reg then
|
||||||
|
werror("type `"..(tname or expr).."' needs a register override")
|
||||||
|
end
|
||||||
|
expr = reg
|
||||||
|
end
|
||||||
|
local r = match(expr, "^r(1?[0-9])$")
|
||||||
|
if r then
|
||||||
|
r = tonumber(r)
|
||||||
|
if r <= 15 then return r, tp end
|
||||||
|
end
|
||||||
|
werror("bad register name `"..expr.."'")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_gpr_pm(expr)
|
||||||
|
local pm, expr2 = match(expr, "^([+-]?)(.*)$")
|
||||||
|
return parse_gpr(expr2), (pm == "-")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_reglist(reglist)
|
||||||
|
reglist = match(reglist, "^{%s*([^}]*)}$")
|
||||||
|
if not reglist then werror("register list expected") end
|
||||||
|
local rr = 0
|
||||||
|
for p in gmatch(reglist..",", "%s*([^,]*),") do
|
||||||
|
local rbit = 2^parse_gpr(gsub(p, "%s+$", ""))
|
||||||
|
if ((rr - (rr % rbit)) / rbit) % 2 ~= 0 then
|
||||||
|
werror("duplicate register `"..p.."'")
|
||||||
|
end
|
||||||
|
rr = rr + rbit
|
||||||
|
end
|
||||||
|
return rr
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_imm(imm, bits, shift, scale, signed)
|
||||||
|
imm = match(imm, "^#(.*)$")
|
||||||
|
if not imm then werror("expected immediate operand") end
|
||||||
|
local n = tonumber(imm)
|
||||||
|
if n then
|
||||||
|
if n % 2^scale == 0 then
|
||||||
|
n = n / 2^scale
|
||||||
|
if signed then
|
||||||
|
if n >= 0 then
|
||||||
|
if n < 2^(bits-1) then return n*2^shift end
|
||||||
|
else
|
||||||
|
if n >= -(2^(bits-1))-1 then return (n+2^bits)*2^shift end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if n >= 0 and n <= 2^bits-1 then return n*2^shift end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
werror("out of range immediate `"..imm.."'")
|
||||||
|
else
|
||||||
|
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_imm12(imm)
|
||||||
|
local n = tonumber(imm)
|
||||||
|
if n then
|
||||||
|
local m = n
|
||||||
|
for i=0,-15,-1 do
|
||||||
|
if m >= 0 and m <= 255 and n % 1 == 0 then return m + (i%16) * 256 end
|
||||||
|
local t = m % 4
|
||||||
|
m = (m - t) / 4 + t * 2^30
|
||||||
|
end
|
||||||
|
werror("out of range immediate `"..imm.."'")
|
||||||
|
else
|
||||||
|
waction("IMM12", 0, imm)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_imm16(imm)
|
||||||
|
imm = match(imm, "^#(.*)$")
|
||||||
|
if not imm then werror("expected immediate operand") end
|
||||||
|
local n = tonumber(imm)
|
||||||
|
if n then
|
||||||
|
if n >= 0 and n <= 65535 and n % 1 == 0 then
|
||||||
|
local t = n % 4096
|
||||||
|
return (n - t) * 16 + t
|
||||||
|
end
|
||||||
|
werror("out of range immediate `"..imm.."'")
|
||||||
|
else
|
||||||
|
waction("IMM16", 32*16, imm)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_imm_load(imm, ext)
|
||||||
|
local n = tonumber(imm)
|
||||||
|
if n then
|
||||||
|
if ext then
|
||||||
|
if n >= -255 and n <= 255 then
|
||||||
|
local up = 0x00800000
|
||||||
|
if n < 0 then n = -n; up = 0 end
|
||||||
|
return (n-(n%16))*16+(n%16) + up
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if n >= -4095 and n <= 4095 then
|
||||||
|
if n >= 0 then return n+0x00800000 end
|
||||||
|
return -n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
werror("out of range immediate `"..imm.."'")
|
||||||
|
else
|
||||||
|
waction(ext and "IMML8" or "IMML12", 32768 + 32*(ext and 8 or 12), imm)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_shift(shift, gprok)
|
||||||
|
if shift == "rrx" then
|
||||||
|
return 3 * 32
|
||||||
|
else
|
||||||
|
local s, s2 = match(shift, "^(%S+)%s*(.*)$")
|
||||||
|
s = map_shift[s]
|
||||||
|
if not s then werror("expected shift operand") end
|
||||||
|
if sub(s2, 1, 1) == "#" then
|
||||||
|
return parse_imm(s2, 5, 7, 0, false) + s * 32
|
||||||
|
else
|
||||||
|
if not gprok then werror("expected immediate shift operand") end
|
||||||
|
return parse_gpr(s2) * 256 + s * 32 + 16
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_label(label, def)
|
||||||
|
local prefix = sub(label, 1, 2)
|
||||||
|
-- =>label (pc label reference)
|
||||||
|
if prefix == "=>" then
|
||||||
|
return "PC", 0, sub(label, 3)
|
||||||
|
end
|
||||||
|
-- ->name (global label reference)
|
||||||
|
if prefix == "->" then
|
||||||
|
return "LG", map_global[sub(label, 3)]
|
||||||
|
end
|
||||||
|
if def then
|
||||||
|
-- [1-9] (local label definition)
|
||||||
|
if match(label, "^[1-9]$") then
|
||||||
|
return "LG", 10+tonumber(label)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- [<>][1-9] (local label reference)
|
||||||
|
local dir, lnum = match(label, "^([<>])([1-9])$")
|
||||||
|
if dir then -- Fwd: 1-9, Bkwd: 11-19.
|
||||||
|
return "LG", lnum + (dir == ">" and 0 or 10)
|
||||||
|
end
|
||||||
|
-- extern label (extern label reference)
|
||||||
|
local extname = match(label, "^extern%s+(%S+)$")
|
||||||
|
if extname then
|
||||||
|
return "EXT", map_extern[extname]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
werror("bad label `"..label.."'")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_load(params, nparams, n, op)
|
||||||
|
local oplo = op % 256
|
||||||
|
local ext, ldrd = (oplo ~= 0), (oplo == 208)
|
||||||
|
local d
|
||||||
|
if (ldrd or oplo == 240) then
|
||||||
|
d = ((op - (op % 4096)) / 4096) % 16
|
||||||
|
if d % 2 ~= 0 then werror("odd destination register") end
|
||||||
|
end
|
||||||
|
local pn = params[n]
|
||||||
|
local p1, wb = match(pn, "^%[%s*(.-)%s*%](!?)$")
|
||||||
|
local p2 = params[n+1]
|
||||||
|
if not p1 then
|
||||||
|
if not p2 then
|
||||||
|
if match(pn, "^[<>=%-]") or match(pn, "^extern%s+") then
|
||||||
|
local mode, n, s = parse_label(pn, false)
|
||||||
|
waction("REL_"..mode, n + (ext and 0x1800 or 0x0800), s, 1)
|
||||||
|
return op + 15 * 65536 + 0x01000000 + (ext and 0x00400000 or 0)
|
||||||
|
end
|
||||||
|
local reg, tailr = match(pn, "^([%w_:]+)%s*(.*)$")
|
||||||
|
if reg and tailr ~= "" then
|
||||||
|
local d, tp = parse_gpr(reg)
|
||||||
|
if tp then
|
||||||
|
waction(ext and "IMML8" or "IMML12", 32768 + 32*(ext and 8 or 12),
|
||||||
|
format(tp.ctypefmt, tailr))
|
||||||
|
return op + d * 65536 + 0x01000000 + (ext and 0x00400000 or 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
werror("expected address operand")
|
||||||
|
end
|
||||||
|
if wb == "!" then op = op + 0x00200000 end
|
||||||
|
if p2 then
|
||||||
|
if wb == "!" then werror("bad use of '!'") end
|
||||||
|
local p3 = params[n+2]
|
||||||
|
op = op + parse_gpr(p1) * 65536
|
||||||
|
local imm = match(p2, "^#(.*)$")
|
||||||
|
if imm then
|
||||||
|
local m = parse_imm_load(imm, ext)
|
||||||
|
if p3 then werror("too many parameters") end
|
||||||
|
op = op + m + (ext and 0x00400000 or 0)
|
||||||
|
else
|
||||||
|
local m, neg = parse_gpr_pm(p2)
|
||||||
|
if ldrd and (m == d or m-1 == d) then werror("register conflict") end
|
||||||
|
op = op + m + (neg and 0 or 0x00800000) + (ext and 0 or 0x02000000)
|
||||||
|
if p3 then op = op + parse_shift(p3) end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local p1a, p2 = match(p1, "^([^,%s]*)%s*(.*)$")
|
||||||
|
op = op + parse_gpr(p1a) * 65536 + 0x01000000
|
||||||
|
if p2 ~= "" then
|
||||||
|
local imm = match(p2, "^,%s*#(.*)$")
|
||||||
|
if imm then
|
||||||
|
local m = parse_imm_load(imm, ext)
|
||||||
|
op = op + m + (ext and 0x00400000 or 0)
|
||||||
|
else
|
||||||
|
local p2a, p3 = match(p2, "^,%s*([^,%s]*)%s*,?%s*(.*)$")
|
||||||
|
local m, neg = parse_gpr_pm(p2a)
|
||||||
|
if ldrd and (m == d or m-1 == d) then werror("register conflict") end
|
||||||
|
op = op + m + (neg and 0 or 0x00800000) + (ext and 0 or 0x02000000)
|
||||||
|
if p3 ~= "" then
|
||||||
|
if ext then werror("too many parameters") end
|
||||||
|
op = op + parse_shift(p3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if wb == "!" then werror("bad use of '!'") end
|
||||||
|
op = op + (ext and 0x00c00000 or 0x00800000)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return op
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Handle opcodes defined with template strings.
|
||||||
|
map_op[".template__"] = function(params, template, nparams)
|
||||||
|
if not params then return sub(template, 9) end
|
||||||
|
local op = tonumber(sub(template, 1, 8), 16)
|
||||||
|
local n = 1
|
||||||
|
|
||||||
|
-- Limit number of section buffer positions used by a single dasm_put().
|
||||||
|
-- A single opcode needs a maximum of 3 positions.
|
||||||
|
if secpos+3 > maxsecpos then wflush() end
|
||||||
|
local pos = wpos()
|
||||||
|
|
||||||
|
-- Process each character.
|
||||||
|
for p in gmatch(sub(template, 9), ".") do
|
||||||
|
if p == "D" then
|
||||||
|
op = op + parse_gpr(params[n]) * 4096; n = n + 1
|
||||||
|
elseif p == "N" then
|
||||||
|
op = op + parse_gpr(params[n]) * 65536; n = n + 1
|
||||||
|
elseif p == "S" then
|
||||||
|
op = op + parse_gpr(params[n]) * 256; n = n + 1
|
||||||
|
elseif p == "M" then
|
||||||
|
op = op + parse_gpr(params[n]); n = n + 1
|
||||||
|
elseif p == "P" then
|
||||||
|
local imm = match(params[n], "^#(.*)$")
|
||||||
|
if imm then
|
||||||
|
op = op + parse_imm12(imm) + 0x02000000
|
||||||
|
else
|
||||||
|
op = op + parse_gpr(params[n])
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
elseif p == "p" then
|
||||||
|
op = op + parse_shift(params[n], true); n = n + 1
|
||||||
|
elseif p == "L" then
|
||||||
|
op = parse_load(params, nparams, n, op)
|
||||||
|
elseif p == "B" then
|
||||||
|
local mode, n, s = parse_label(params[n], false)
|
||||||
|
waction("REL_"..mode, n, s, 1)
|
||||||
|
elseif p == "C" then -- blx gpr vs. blx label.
|
||||||
|
local p = params[n]
|
||||||
|
if match(p, "^([%w_]+):(r1?[0-9])$") or match(p, "^r(1?[0-9])$") then
|
||||||
|
op = op + parse_gpr(p)
|
||||||
|
else
|
||||||
|
if op < 0xe0000000 then werror("unconditional instruction") end
|
||||||
|
local mode, n, s = parse_label(p, false)
|
||||||
|
waction("REL_"..mode, n, s, 1)
|
||||||
|
op = 0xfa000000
|
||||||
|
end
|
||||||
|
elseif p == "n" then
|
||||||
|
local r, wb = match(params[n], "^([^!]*)(!?)$")
|
||||||
|
op = op + parse_gpr(r) * 65536 + (wb == "!" and 0x00200000 or 0)
|
||||||
|
n = n + 1
|
||||||
|
elseif p == "R" then
|
||||||
|
op = op + parse_reglist(params[n]); n = n + 1
|
||||||
|
elseif p == "W" then
|
||||||
|
op = op + parse_imm16(params[n]); n = n + 1
|
||||||
|
elseif p == "v" then
|
||||||
|
op = op + parse_imm(params[n], 5, 7, 0, false); n = n + 1
|
||||||
|
elseif p == "w" then
|
||||||
|
local imm = match(params[n], "^#(.*)$")
|
||||||
|
if imm then
|
||||||
|
op = op + parse_imm(params[n], 5, 7, 0, false); n = n + 1
|
||||||
|
else
|
||||||
|
op = op + parse_gpr(params[n]) * 256 + 16
|
||||||
|
end
|
||||||
|
elseif p == "X" then
|
||||||
|
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
|
||||||
|
elseif p == "K" then
|
||||||
|
local imm = tonumber(match(params[n], "^#(.*)$")); n = n + 1
|
||||||
|
if not imm or imm % 1 ~= 0 or imm < 0 or imm > 0xffff then
|
||||||
|
werror("bad immediate operand")
|
||||||
|
end
|
||||||
|
local t = imm % 16
|
||||||
|
op = op + (imm - t) * 16 + t
|
||||||
|
elseif p == "T" then
|
||||||
|
op = op + parse_imm(params[n], 24, 0, 0, false); n = n + 1
|
||||||
|
elseif p == "s" then
|
||||||
|
-- Ignored.
|
||||||
|
else
|
||||||
|
assert(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
wputpos(pos, op)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Pseudo-opcode to mark the position where the action list is to be emitted.
|
||||||
|
map_op[".actionlist_1"] = function(params)
|
||||||
|
if not params then return "cvar" end
|
||||||
|
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||||
|
wline(function(out) writeactions(out, name) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pseudo-opcode to mark the position where the global enum is to be emitted.
|
||||||
|
map_op[".globals_1"] = function(params)
|
||||||
|
if not params then return "prefix" end
|
||||||
|
local prefix = params[1] -- No syntax check. You get to keep the pieces.
|
||||||
|
wline(function(out) writeglobals(out, prefix) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pseudo-opcode to mark the position where the global names are to be emitted.
|
||||||
|
map_op[".globalnames_1"] = function(params)
|
||||||
|
if not params then return "cvar" end
|
||||||
|
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||||
|
wline(function(out) writeglobalnames(out, name) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pseudo-opcode to mark the position where the extern names are to be emitted.
|
||||||
|
map_op[".externnames_1"] = function(params)
|
||||||
|
if not params then return "cvar" end
|
||||||
|
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||||
|
wline(function(out) writeexternnames(out, name) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Label pseudo-opcode (converted from trailing colon form).
|
||||||
|
map_op[".label_1"] = function(params)
|
||||||
|
if not params then return "[1-9] | ->global | =>pcexpr" end
|
||||||
|
if secpos+1 > maxsecpos then wflush() end
|
||||||
|
local mode, n, s = parse_label(params[1], true)
|
||||||
|
if mode == "EXT" then werror("bad label definition") end
|
||||||
|
waction("LABEL_"..mode, n, s, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Pseudo-opcodes for data storage.
|
||||||
|
map_op[".long_*"] = function(params)
|
||||||
|
if not params then return "imm..." end
|
||||||
|
for _,p in ipairs(params) do
|
||||||
|
local n = tonumber(p)
|
||||||
|
if n then
|
||||||
|
if n < 0 then n = n + 2^32 end
|
||||||
|
wputw(n)
|
||||||
|
if secpos+2 > maxsecpos then wflush() end
|
||||||
|
else
|
||||||
|
waction("LONG", 0, format("(uintptr_t)(%s)", p))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Alignment pseudo-opcode.
|
||||||
|
map_op[".align_1"] = function(params)
|
||||||
|
if not params then return "numpow2" end
|
||||||
|
if secpos+1 > maxsecpos then wflush() end
|
||||||
|
local align = tonumber(params[1])
|
||||||
|
if align then
|
||||||
|
local x = align
|
||||||
|
-- Must be a power of 2 in the range (2 ... 256).
|
||||||
|
for i=1,8 do
|
||||||
|
x = x / 2
|
||||||
|
if x == 1 then
|
||||||
|
waction("ALIGN", align-1, nil, 1) -- Action byte is 2**n-1.
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
werror("bad alignment")
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Pseudo-opcode for (primitive) type definitions (map to C types).
|
||||||
|
map_op[".type_3"] = function(params, nparams)
|
||||||
|
if not params then
|
||||||
|
return nparams == 2 and "name, ctype" or "name, ctype, reg"
|
||||||
|
end
|
||||||
|
local name, ctype, reg = params[1], params[2], params[3]
|
||||||
|
if not match(name, "^[%a_][%w_]*$") then
|
||||||
|
werror("bad type name `"..name.."'")
|
||||||
|
end
|
||||||
|
local tp = map_type[name]
|
||||||
|
if tp then
|
||||||
|
werror("duplicate type `"..name.."'")
|
||||||
|
end
|
||||||
|
-- Add #type to defines. A bit unclean to put it in map_archdef.
|
||||||
|
map_archdef["#"..name] = "sizeof("..ctype..")"
|
||||||
|
-- Add new type and emit shortcut define.
|
||||||
|
local num = ctypenum + 1
|
||||||
|
map_type[name] = {
|
||||||
|
ctype = ctype,
|
||||||
|
ctypefmt = format("Dt%X(%%s)", num),
|
||||||
|
reg = reg,
|
||||||
|
}
|
||||||
|
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||||
|
ctypenum = num
|
||||||
|
end
|
||||||
|
map_op[".type_2"] = map_op[".type_3"]
|
||||||
|
|
||||||
|
-- Dump type definitions.
|
||||||
|
local function dumptypes(out, lvl)
|
||||||
|
local t = {}
|
||||||
|
for name in pairs(map_type) do t[#t+1] = name end
|
||||||
|
sort(t)
|
||||||
|
out:write("Type definitions:\n")
|
||||||
|
for _,name in ipairs(t) do
|
||||||
|
local tp = map_type[name]
|
||||||
|
local reg = tp.reg or ""
|
||||||
|
out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg))
|
||||||
|
end
|
||||||
|
out:write("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Set the current section.
|
||||||
|
function _M.section(num)
|
||||||
|
waction("SECTION", num)
|
||||||
|
wflush(true) -- SECTION is a terminal action.
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Dump architecture description.
|
||||||
|
function _M.dumparch(out)
|
||||||
|
out:write(format("DynASM %s version %s, released %s\n\n",
|
||||||
|
_info.arch, _info.version, _info.release))
|
||||||
|
dumpactions(out)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dump all user defined elements.
|
||||||
|
function _M.dumpdef(out, lvl)
|
||||||
|
dumptypes(out, lvl)
|
||||||
|
dumpglobals(out, lvl)
|
||||||
|
dumpexterns(out, lvl)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Pass callbacks from/to the DynASM core.
|
||||||
|
function _M.passcb(wl, we, wf, ww)
|
||||||
|
wline, werror, wfatal, wwarn = wl, we, wf, ww
|
||||||
|
return wflush
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Setup the arch-specific module.
|
||||||
|
function _M.setup(arch, opt)
|
||||||
|
g_arch, g_opt = arch, opt
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Merge the core maps and the arch-specific maps.
|
||||||
|
function _M.mergemaps(map_coreop, map_def)
|
||||||
|
setmetatable(map_op, { __index = function(t, k)
|
||||||
|
local v = map_coreop[k]
|
||||||
|
if v then return v end
|
||||||
|
local cc = sub(k, -4, -3)
|
||||||
|
local cv = map_cond[cc]
|
||||||
|
if cv then
|
||||||
|
local v = rawget(t, sub(k, 1, -5)..sub(k, -2))
|
||||||
|
if type(v) == "string" then return format("%x%s", cv, sub(v, 2)) end
|
||||||
|
end
|
||||||
|
end })
|
||||||
|
setmetatable(map_def, { __index = map_archdef })
|
||||||
|
return map_op, map_def
|
||||||
|
end
|
||||||
|
|
||||||
|
return _M
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,408 @@
|
||||||
|
/*
|
||||||
|
** DynASM PPC encoding engine.
|
||||||
|
** Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define DASM_ARCH "ppc"
|
||||||
|
|
||||||
|
#ifndef DASM_EXTERN
|
||||||
|
#define DASM_EXTERN(a,b,c,d) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Action definitions. */
|
||||||
|
enum {
|
||||||
|
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||||
|
/* The following actions need a buffer position. */
|
||||||
|
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||||
|
/* The following actions also have an argument. */
|
||||||
|
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM,
|
||||||
|
DASM__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||||
|
#define DASM_MAXSECPOS 25
|
||||||
|
|
||||||
|
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||||
|
#define DASM_S_OK 0x00000000
|
||||||
|
#define DASM_S_NOMEM 0x01000000
|
||||||
|
#define DASM_S_PHASE 0x02000000
|
||||||
|
#define DASM_S_MATCH_SEC 0x03000000
|
||||||
|
#define DASM_S_RANGE_I 0x11000000
|
||||||
|
#define DASM_S_RANGE_SEC 0x12000000
|
||||||
|
#define DASM_S_RANGE_LG 0x13000000
|
||||||
|
#define DASM_S_RANGE_PC 0x14000000
|
||||||
|
#define DASM_S_RANGE_REL 0x15000000
|
||||||
|
#define DASM_S_UNDEF_LG 0x21000000
|
||||||
|
#define DASM_S_UNDEF_PC 0x22000000
|
||||||
|
|
||||||
|
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||||
|
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||||
|
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||||
|
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||||
|
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||||
|
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||||
|
|
||||||
|
/* Action list type. */
|
||||||
|
typedef const unsigned int *dasm_ActList;
|
||||||
|
|
||||||
|
/* Per-section structure. */
|
||||||
|
typedef struct dasm_Section {
|
||||||
|
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||||
|
int *buf; /* True buffer pointer. */
|
||||||
|
size_t bsize; /* Buffer size in bytes. */
|
||||||
|
int pos; /* Biased buffer position. */
|
||||||
|
int epos; /* End of biased buffer position - max single put. */
|
||||||
|
int ofs; /* Byte offset into section. */
|
||||||
|
} dasm_Section;
|
||||||
|
|
||||||
|
/* Core structure holding the DynASM encoding state. */
|
||||||
|
struct dasm_State {
|
||||||
|
size_t psize; /* Allocated size of this structure. */
|
||||||
|
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||||
|
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||||
|
size_t lgsize;
|
||||||
|
int *pclabels; /* PC label chains/pos ptrs. */
|
||||||
|
size_t pcsize;
|
||||||
|
void **globals; /* Array of globals (bias -10). */
|
||||||
|
dasm_Section *section; /* Pointer to active section. */
|
||||||
|
size_t codesize; /* Total size of all code sections. */
|
||||||
|
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||||
|
int status; /* Status code. */
|
||||||
|
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The size of the core structure depends on the max. number of sections. */
|
||||||
|
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize DynASM state. */
|
||||||
|
void dasm_init(Dst_DECL, int maxsection)
|
||||||
|
{
|
||||||
|
dasm_State *D;
|
||||||
|
size_t psz = 0;
|
||||||
|
int i;
|
||||||
|
Dst_REF = NULL;
|
||||||
|
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||||
|
D = Dst_REF;
|
||||||
|
D->psize = psz;
|
||||||
|
D->lglabels = NULL;
|
||||||
|
D->lgsize = 0;
|
||||||
|
D->pclabels = NULL;
|
||||||
|
D->pcsize = 0;
|
||||||
|
D->globals = NULL;
|
||||||
|
D->maxsection = maxsection;
|
||||||
|
for (i = 0; i < maxsection; i++) {
|
||||||
|
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||||
|
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||||
|
D->sections[i].bsize = 0;
|
||||||
|
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free DynASM state. */
|
||||||
|
void dasm_free(Dst_DECL)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < D->maxsection; i++)
|
||||||
|
if (D->sections[i].buf)
|
||||||
|
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||||
|
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||||
|
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||||
|
DASM_M_FREE(Dst, D, D->psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup global label array. Must be called before dasm_setup(). */
|
||||||
|
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||||
|
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||||
|
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
size_t osz = D->pcsize;
|
||||||
|
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||||
|
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup encoder. */
|
||||||
|
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
D->actionlist = (dasm_ActList)actionlist;
|
||||||
|
D->status = DASM_S_OK;
|
||||||
|
D->section = &D->sections[0];
|
||||||
|
memset((void *)D->lglabels, 0, D->lgsize);
|
||||||
|
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||||
|
for (i = 0; i < D->maxsection; i++) {
|
||||||
|
D->sections[i].pos = DASM_SEC2POS(i);
|
||||||
|
D->sections[i].ofs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
#define CK(x, st) \
|
||||||
|
do { if (!(x)) { \
|
||||||
|
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#define CKPL(kind, st) \
|
||||||
|
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||||
|
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#else
|
||||||
|
#define CK(x, st) ((void)0)
|
||||||
|
#define CKPL(kind, st) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||||
|
void dasm_put(Dst_DECL, int start, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
dasm_ActList p = D->actionlist + start;
|
||||||
|
dasm_Section *sec = D->section;
|
||||||
|
int pos = sec->pos, ofs = sec->ofs;
|
||||||
|
int *b;
|
||||||
|
|
||||||
|
if (pos >= sec->epos) {
|
||||||
|
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||||
|
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||||
|
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||||
|
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
b = sec->rbuf;
|
||||||
|
b[pos++] = start;
|
||||||
|
|
||||||
|
va_start(ap, start);
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
if (action >= DASM__MAX) {
|
||||||
|
ofs += 4;
|
||||||
|
} else {
|
||||||
|
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: goto stop;
|
||||||
|
case DASM_SECTION:
|
||||||
|
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||||
|
D->section = &D->sections[n]; goto stop;
|
||||||
|
case DASM_ESC: p++; ofs += 4; break;
|
||||||
|
case DASM_REL_EXT: break;
|
||||||
|
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||||
|
case DASM_REL_LG:
|
||||||
|
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||||
|
if (n >= 0) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
|
||||||
|
pl += 10; n = *pl;
|
||||||
|
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||||
|
goto linkrel;
|
||||||
|
case DASM_REL_PC:
|
||||||
|
pl = D->pclabels + n; CKPL(pc, PC);
|
||||||
|
putrel:
|
||||||
|
n = *pl;
|
||||||
|
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||||
|
b[pos] = -n;
|
||||||
|
} else {
|
||||||
|
linkrel:
|
||||||
|
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||||
|
*pl = pos;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_LG:
|
||||||
|
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||||
|
case DASM_LABEL_PC:
|
||||||
|
pl = D->pclabels + n; CKPL(pc, PC);
|
||||||
|
putlabel:
|
||||||
|
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||||
|
}
|
||||||
|
*pl = -pos; /* Label exists now. */
|
||||||
|
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||||
|
break;
|
||||||
|
case DASM_IMM:
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||||
|
if (ins & 0x8000)
|
||||||
|
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||||
|
else
|
||||||
|
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||||
|
#endif
|
||||||
|
b[pos++] = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
va_end(ap);
|
||||||
|
sec->pos = pos;
|
||||||
|
sec->ofs = ofs;
|
||||||
|
}
|
||||||
|
#undef CK
|
||||||
|
|
||||||
|
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||||
|
int dasm_link(Dst_DECL, size_t *szp)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int secnum;
|
||||||
|
int ofs = 0;
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
*szp = 0;
|
||||||
|
if (D->status != DASM_S_OK) return D->status;
|
||||||
|
{
|
||||||
|
int pc;
|
||||||
|
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||||
|
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{ /* Handle globals not defined in this translation unit. */
|
||||||
|
int idx;
|
||||||
|
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||||
|
int n = D->lglabels[idx];
|
||||||
|
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combine all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->rbuf;
|
||||||
|
int pos = DASM_SEC2POS(secnum);
|
||||||
|
int lastpos = sec->pos;
|
||||||
|
|
||||||
|
while (pos != lastpos) {
|
||||||
|
dasm_ActList p = D->actionlist + b[pos++];
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||||
|
case DASM_ESC: p++; break;
|
||||||
|
case DASM_REL_EXT: break;
|
||||||
|
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||||
|
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||||
|
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||||
|
case DASM_IMM: pos++; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||||
|
}
|
||||||
|
|
||||||
|
D->codesize = ofs; /* Total size of all code sections */
|
||||||
|
*szp = ofs;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
#define CK(x, st) \
|
||||||
|
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||||
|
#else
|
||||||
|
#define CK(x, st) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pass 3: Encode sections. */
|
||||||
|
int dasm_encode(Dst_DECL, void *buffer)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
char *base = (char *)buffer;
|
||||||
|
unsigned int *cp = (unsigned int *)buffer;
|
||||||
|
int secnum;
|
||||||
|
|
||||||
|
/* Encode all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->buf;
|
||||||
|
int *endb = sec->rbuf + sec->pos;
|
||||||
|
|
||||||
|
while (b != endb) {
|
||||||
|
dasm_ActList p = D->actionlist + *b++;
|
||||||
|
while (1) {
|
||||||
|
unsigned int ins = *p++;
|
||||||
|
unsigned int action = (ins >> 16);
|
||||||
|
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||||
|
case DASM_ESC: *cp++ = *p++; break;
|
||||||
|
case DASM_REL_EXT:
|
||||||
|
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1);
|
||||||
|
goto patchrel;
|
||||||
|
case DASM_ALIGN:
|
||||||
|
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||||
|
break;
|
||||||
|
case DASM_REL_LG:
|
||||||
|
CK(n >= 0, UNDEF_LG);
|
||||||
|
case DASM_REL_PC:
|
||||||
|
CK(n >= 0, UNDEF_PC);
|
||||||
|
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
|
||||||
|
patchrel:
|
||||||
|
CK((n & 3) == 0 &&
|
||||||
|
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
|
||||||
|
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
|
||||||
|
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_LG:
|
||||||
|
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_PC: break;
|
||||||
|
case DASM_IMM:
|
||||||
|
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||||
|
break;
|
||||||
|
default: *cp++ = ins; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||||
|
return DASM_S_PHASE;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
#undef CK
|
||||||
|
|
||||||
|
/* Get PC label offset. */
|
||||||
|
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (pc*sizeof(int) < D->pcsize) {
|
||||||
|
int pos = D->pclabels[pc];
|
||||||
|
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||||
|
if (pos > 0) return -1; /* Undefined. */
|
||||||
|
}
|
||||||
|
return -2; /* Unused or out of range. */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
/* Optional sanity checker to call between isolated encoding steps. */
|
||||||
|
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (D->status == DASM_S_OK) {
|
||||||
|
int i;
|
||||||
|
for (i = 1; i <= 9; i++) {
|
||||||
|
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||||
|
D->lglabels[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||||
|
D->section != &D->sections[secmatch])
|
||||||
|
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||||
|
return D->status;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
** DynASM encoding engine prototypes.
|
||||||
|
** Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DASM_PROTO_H
|
||||||
|
#define _DASM_PROTO_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define DASM_IDENT "DynASM 1.3.0"
|
||||||
|
#define DASM_VERSION 10300 /* 1.3.0 */
|
||||||
|
|
||||||
|
#ifndef Dst_DECL
|
||||||
|
#define Dst_DECL dasm_State **Dst
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Dst_REF
|
||||||
|
#define Dst_REF (*Dst)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DASM_FDEF
|
||||||
|
#define DASM_FDEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DASM_M_GROW
|
||||||
|
#define DASM_M_GROW(ctx, t, p, sz, need) \
|
||||||
|
do { \
|
||||||
|
size_t _sz = (sz), _need = (need); \
|
||||||
|
if (_sz < _need) { \
|
||||||
|
if (_sz < 16) _sz = 16; \
|
||||||
|
while (_sz < _need) _sz += _sz; \
|
||||||
|
(p) = (t *)realloc((p), _sz); \
|
||||||
|
if ((p) == NULL) exit(1); \
|
||||||
|
(sz) = _sz; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DASM_M_FREE
|
||||||
|
#define DASM_M_FREE(ctx, p, sz) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Internal DynASM encoder state. */
|
||||||
|
typedef struct dasm_State dasm_State;
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize and free DynASM state. */
|
||||||
|
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
|
||||||
|
DASM_FDEF void dasm_free(Dst_DECL);
|
||||||
|
|
||||||
|
/* Setup global array. Must be called before dasm_setup(). */
|
||||||
|
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
||||||
|
|
||||||
|
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||||
|
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
||||||
|
|
||||||
|
/* Setup encoder. */
|
||||||
|
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
|
||||||
|
|
||||||
|
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
||||||
|
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
|
||||||
|
|
||||||
|
/* Link sections and return the resulting size. */
|
||||||
|
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
|
||||||
|
|
||||||
|
/* Encode sections into buffer. */
|
||||||
|
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
|
||||||
|
|
||||||
|
/* Get PC label offset. */
|
||||||
|
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
/* Optional sanity checker to call between isolated encoding steps. */
|
||||||
|
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
|
||||||
|
#else
|
||||||
|
#define dasm_checkstep(a, b) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _DASM_PROTO_H */
|
|
@ -0,0 +1,12 @@
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- DynASM x64 module.
|
||||||
|
--
|
||||||
|
-- Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
-- See dynasm.lua for full copyright notice.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- This module just sets 64 bit mode for the combined x86/x64 module.
|
||||||
|
-- All the interesting stuff is there.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
x64 = true -- Using a global is an ugly, but effective solution.
|
||||||
|
return require("dasm_x86")
|
|
@ -0,0 +1,470 @@
|
||||||
|
/*
|
||||||
|
** DynASM x86 encoding engine.
|
||||||
|
** Copyright (C) 2005-2011 Mike Pall. All rights reserved.
|
||||||
|
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define DASM_ARCH "x86"
|
||||||
|
|
||||||
|
#ifndef DASM_EXTERN
|
||||||
|
#define DASM_EXTERN(a,b,c,d) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Action definitions. DASM_STOP must be 255. */
|
||||||
|
enum {
|
||||||
|
DASM_DISP = 233,
|
||||||
|
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
|
||||||
|
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
|
||||||
|
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
|
||||||
|
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||||
|
#define DASM_MAXSECPOS 25
|
||||||
|
|
||||||
|
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||||
|
#define DASM_S_OK 0x00000000
|
||||||
|
#define DASM_S_NOMEM 0x01000000
|
||||||
|
#define DASM_S_PHASE 0x02000000
|
||||||
|
#define DASM_S_MATCH_SEC 0x03000000
|
||||||
|
#define DASM_S_RANGE_I 0x11000000
|
||||||
|
#define DASM_S_RANGE_SEC 0x12000000
|
||||||
|
#define DASM_S_RANGE_LG 0x13000000
|
||||||
|
#define DASM_S_RANGE_PC 0x14000000
|
||||||
|
#define DASM_S_RANGE_VREG 0x15000000
|
||||||
|
#define DASM_S_UNDEF_L 0x21000000
|
||||||
|
#define DASM_S_UNDEF_PC 0x22000000
|
||||||
|
|
||||||
|
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||||
|
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||||
|
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||||
|
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||||
|
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||||
|
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||||
|
|
||||||
|
/* Action list type. */
|
||||||
|
typedef const unsigned char *dasm_ActList;
|
||||||
|
|
||||||
|
/* Per-section structure. */
|
||||||
|
typedef struct dasm_Section {
|
||||||
|
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||||
|
int *buf; /* True buffer pointer. */
|
||||||
|
size_t bsize; /* Buffer size in bytes. */
|
||||||
|
int pos; /* Biased buffer position. */
|
||||||
|
int epos; /* End of biased buffer position - max single put. */
|
||||||
|
int ofs; /* Byte offset into section. */
|
||||||
|
} dasm_Section;
|
||||||
|
|
||||||
|
/* Core structure holding the DynASM encoding state. */
|
||||||
|
struct dasm_State {
|
||||||
|
size_t psize; /* Allocated size of this structure. */
|
||||||
|
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||||
|
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||||
|
size_t lgsize;
|
||||||
|
int *pclabels; /* PC label chains/pos ptrs. */
|
||||||
|
size_t pcsize;
|
||||||
|
void **globals; /* Array of globals (bias -10). */
|
||||||
|
dasm_Section *section; /* Pointer to active section. */
|
||||||
|
size_t codesize; /* Total size of all code sections. */
|
||||||
|
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||||
|
int status; /* Status code. */
|
||||||
|
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The size of the core structure depends on the max. number of sections. */
|
||||||
|
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize DynASM state. */
|
||||||
|
void dasm_init(Dst_DECL, int maxsection)
|
||||||
|
{
|
||||||
|
dasm_State *D;
|
||||||
|
size_t psz = 0;
|
||||||
|
int i;
|
||||||
|
Dst_REF = NULL;
|
||||||
|
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||||
|
D = Dst_REF;
|
||||||
|
D->psize = psz;
|
||||||
|
D->lglabels = NULL;
|
||||||
|
D->lgsize = 0;
|
||||||
|
D->pclabels = NULL;
|
||||||
|
D->pcsize = 0;
|
||||||
|
D->globals = NULL;
|
||||||
|
D->maxsection = maxsection;
|
||||||
|
for (i = 0; i < maxsection; i++) {
|
||||||
|
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||||
|
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||||
|
D->sections[i].bsize = 0;
|
||||||
|
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free DynASM state. */
|
||||||
|
void dasm_free(Dst_DECL)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < D->maxsection; i++)
|
||||||
|
if (D->sections[i].buf)
|
||||||
|
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||||
|
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||||
|
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||||
|
DASM_M_FREE(Dst, D, D->psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup global label array. Must be called before dasm_setup(). */
|
||||||
|
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||||
|
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||||
|
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
size_t osz = D->pcsize;
|
||||||
|
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||||
|
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup encoder. */
|
||||||
|
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int i;
|
||||||
|
D->actionlist = (dasm_ActList)actionlist;
|
||||||
|
D->status = DASM_S_OK;
|
||||||
|
D->section = &D->sections[0];
|
||||||
|
memset((void *)D->lglabels, 0, D->lgsize);
|
||||||
|
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||||
|
for (i = 0; i < D->maxsection; i++) {
|
||||||
|
D->sections[i].pos = DASM_SEC2POS(i);
|
||||||
|
D->sections[i].ofs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
#define CK(x, st) \
|
||||||
|
do { if (!(x)) { \
|
||||||
|
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#define CKPL(kind, st) \
|
||||||
|
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||||
|
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||||
|
#else
|
||||||
|
#define CK(x, st) ((void)0)
|
||||||
|
#define CKPL(kind, st) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||||
|
void dasm_put(Dst_DECL, int start, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
dasm_ActList p = D->actionlist + start;
|
||||||
|
dasm_Section *sec = D->section;
|
||||||
|
int pos = sec->pos, ofs = sec->ofs, mrm = 4;
|
||||||
|
int *b;
|
||||||
|
|
||||||
|
if (pos >= sec->epos) {
|
||||||
|
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||||
|
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||||
|
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||||
|
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
b = sec->rbuf;
|
||||||
|
b[pos++] = start;
|
||||||
|
|
||||||
|
va_start(ap, start);
|
||||||
|
while (1) {
|
||||||
|
int action = *p++;
|
||||||
|
if (action < DASM_DISP) {
|
||||||
|
ofs++;
|
||||||
|
} else if (action <= DASM_REL_A) {
|
||||||
|
int n = va_arg(ap, int);
|
||||||
|
b[pos++] = n;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_DISP:
|
||||||
|
if (n == 0) { if ((mrm&7) == 4) mrm = p[-2]; if ((mrm&7) != 5) break; }
|
||||||
|
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob;
|
||||||
|
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
|
||||||
|
case DASM_IMM_D: ofs += 4; break;
|
||||||
|
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
|
||||||
|
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
|
||||||
|
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob;
|
||||||
|
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
|
||||||
|
case DASM_SPACE: p++; ofs += n; break;
|
||||||
|
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
|
||||||
|
case DASM_VREG: CK((n&-8) == 0 && (n != 4 || (*p&1) == 0), RANGE_VREG);
|
||||||
|
if (*p++ == 1 && *p == DASM_DISP) mrm = n; continue;
|
||||||
|
}
|
||||||
|
mrm = 4;
|
||||||
|
} else {
|
||||||
|
int *pl, n;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_REL_LG:
|
||||||
|
case DASM_IMM_LG:
|
||||||
|
n = *p++; pl = D->lglabels + n;
|
||||||
|
if (n <= 246) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
|
||||||
|
pl -= 246; n = *pl;
|
||||||
|
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||||
|
goto linkrel;
|
||||||
|
case DASM_REL_PC:
|
||||||
|
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||||
|
putrel:
|
||||||
|
n = *pl;
|
||||||
|
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||||
|
b[pos] = -n;
|
||||||
|
} else {
|
||||||
|
linkrel:
|
||||||
|
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||||
|
*pl = pos;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
ofs += 4; /* Maximum offset needed. */
|
||||||
|
if (action == DASM_REL_LG || action == DASM_REL_PC)
|
||||||
|
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||||
|
break;
|
||||||
|
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
|
||||||
|
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||||
|
putlabel:
|
||||||
|
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; }
|
||||||
|
*pl = -pos; /* Label exists now. */
|
||||||
|
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||||
|
break;
|
||||||
|
case DASM_ALIGN:
|
||||||
|
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
|
||||||
|
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||||
|
break;
|
||||||
|
case DASM_EXTERN: p += 2; ofs += 4; break;
|
||||||
|
case DASM_ESC: p++; ofs++; break;
|
||||||
|
case DASM_MARK: mrm = p[-2]; break;
|
||||||
|
case DASM_SECTION:
|
||||||
|
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
|
||||||
|
case DASM_STOP: goto stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
va_end(ap);
|
||||||
|
sec->pos = pos;
|
||||||
|
sec->ofs = ofs;
|
||||||
|
}
|
||||||
|
#undef CK
|
||||||
|
|
||||||
|
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
|
||||||
|
int dasm_link(Dst_DECL, size_t *szp)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
int secnum;
|
||||||
|
int ofs = 0;
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
*szp = 0;
|
||||||
|
if (D->status != DASM_S_OK) return D->status;
|
||||||
|
{
|
||||||
|
int pc;
|
||||||
|
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||||
|
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{ /* Handle globals not defined in this translation unit. */
|
||||||
|
int idx;
|
||||||
|
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||||
|
int n = D->lglabels[idx];
|
||||||
|
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||||
|
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combine all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->rbuf;
|
||||||
|
int pos = DASM_SEC2POS(secnum);
|
||||||
|
int lastpos = sec->pos;
|
||||||
|
|
||||||
|
while (pos != lastpos) {
|
||||||
|
dasm_ActList p = D->actionlist + b[pos++];
|
||||||
|
while (1) {
|
||||||
|
int op, action = *p++;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_REL_LG: p++; op = p[-3]; goto rel_pc;
|
||||||
|
case DASM_REL_PC: op = p[-2]; rel_pc: {
|
||||||
|
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
|
||||||
|
if (shrink) { /* Shrinkable branch opcode? */
|
||||||
|
int lofs, lpos = b[pos];
|
||||||
|
if (lpos < 0) goto noshrink; /* Ext global? */
|
||||||
|
lofs = *DASM_POS2PTR(D, lpos);
|
||||||
|
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
|
||||||
|
int i;
|
||||||
|
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
|
||||||
|
lofs += D->sections[i].ofs;
|
||||||
|
} else {
|
||||||
|
lofs -= ofs; /* Bkwd label: unfix offset. */
|
||||||
|
}
|
||||||
|
lofs -= b[pos+1]; /* Short branch ok? */
|
||||||
|
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
|
||||||
|
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
|
||||||
|
}
|
||||||
|
b[pos+1] = shrink;
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
|
||||||
|
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
|
||||||
|
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
|
||||||
|
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
|
||||||
|
case DASM_LABEL_LG: p++;
|
||||||
|
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
|
||||||
|
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
|
||||||
|
case DASM_EXTERN: p += 2; break;
|
||||||
|
case DASM_ESC: p++; break;
|
||||||
|
case DASM_MARK: break;
|
||||||
|
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||||
|
}
|
||||||
|
|
||||||
|
D->codesize = ofs; /* Total size of all code sections */
|
||||||
|
*szp = ofs;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define dasmb(x) *cp++ = (unsigned char)(x)
|
||||||
|
#ifndef DASM_ALIGNED_WRITES
|
||||||
|
#define dasmw(x) \
|
||||||
|
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
|
||||||
|
#define dasmd(x) \
|
||||||
|
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
|
||||||
|
#else
|
||||||
|
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
|
||||||
|
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Pass 3: Encode sections. */
|
||||||
|
int dasm_encode(Dst_DECL, void *buffer)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
unsigned char *base = (unsigned char *)buffer;
|
||||||
|
unsigned char *cp = base;
|
||||||
|
int secnum;
|
||||||
|
|
||||||
|
/* Encode all code sections. No support for data sections (yet). */
|
||||||
|
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||||
|
dasm_Section *sec = D->sections + secnum;
|
||||||
|
int *b = sec->buf;
|
||||||
|
int *endb = sec->rbuf + sec->pos;
|
||||||
|
|
||||||
|
while (b != endb) {
|
||||||
|
dasm_ActList p = D->actionlist + *b++;
|
||||||
|
unsigned char *mark = NULL;
|
||||||
|
while (1) {
|
||||||
|
int action = *p++;
|
||||||
|
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
|
||||||
|
switch (action) {
|
||||||
|
case DASM_DISP: if (!mark) mark = cp; {
|
||||||
|
unsigned char *mm = mark;
|
||||||
|
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
|
||||||
|
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
|
||||||
|
if (mrm != 5) { mm[-1] -= 0x80; break; } }
|
||||||
|
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
|
||||||
|
}
|
||||||
|
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
|
||||||
|
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
|
||||||
|
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
|
||||||
|
} else mark = NULL;
|
||||||
|
case DASM_IMM_D: wd: dasmd(n); break;
|
||||||
|
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
|
||||||
|
case DASM_IMM_W: dasmw(n); break;
|
||||||
|
case DASM_VREG: { int t = *p++; if (t >= 2) n<<=3; cp[-1] |= n; break; }
|
||||||
|
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
|
||||||
|
b++; n = (int)(ptrdiff_t)D->globals[-n];
|
||||||
|
case DASM_REL_A: rel_a: n -= (int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
|
||||||
|
case DASM_REL_PC: rel_pc: {
|
||||||
|
int shrink = *b++;
|
||||||
|
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
|
||||||
|
n = *pb - ((int)(cp-base) + 4-shrink);
|
||||||
|
if (shrink == 0) goto wd;
|
||||||
|
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
|
||||||
|
goto wb;
|
||||||
|
}
|
||||||
|
case DASM_IMM_LG:
|
||||||
|
p++; if (n < 0) { n = (int)(ptrdiff_t)D->globals[-n]; goto wd; }
|
||||||
|
case DASM_IMM_PC: {
|
||||||
|
int *pb = DASM_POS2PTR(D, n);
|
||||||
|
n = *pb < 0 ? pb[1] : (*pb + (int)(ptrdiff_t)base);
|
||||||
|
goto wd;
|
||||||
|
}
|
||||||
|
case DASM_LABEL_LG: {
|
||||||
|
int idx = *p++;
|
||||||
|
if (idx >= 10)
|
||||||
|
D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DASM_LABEL_PC: case DASM_SETLABEL: break;
|
||||||
|
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
|
||||||
|
case DASM_ALIGN:
|
||||||
|
n = *p++;
|
||||||
|
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
|
||||||
|
break;
|
||||||
|
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
|
||||||
|
case DASM_MARK: mark = cp; break;
|
||||||
|
case DASM_ESC: action = *p++;
|
||||||
|
default: *cp++ = action; break;
|
||||||
|
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop: (void)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base + D->codesize != cp) /* Check for phase errors. */
|
||||||
|
return DASM_S_PHASE;
|
||||||
|
return DASM_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PC label offset. */
|
||||||
|
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (pc*sizeof(int) < D->pcsize) {
|
||||||
|
int pos = D->pclabels[pc];
|
||||||
|
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||||
|
if (pos > 0) return -1; /* Undefined. */
|
||||||
|
}
|
||||||
|
return -2; /* Unused or out of range. */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DASM_CHECKS
|
||||||
|
/* Optional sanity checker to call between isolated encoding steps. */
|
||||||
|
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||||
|
{
|
||||||
|
dasm_State *D = Dst_REF;
|
||||||
|
if (D->status == DASM_S_OK) {
|
||||||
|
int i;
|
||||||
|
for (i = 1; i <= 9; i++) {
|
||||||
|
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; }
|
||||||
|
D->lglabels[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||||
|
D->section != &D->sections[secmatch])
|
||||||
|
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||||
|
return D->status;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,450 @@
|
||||||
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 James R. McKaskill
|
||||||
|
*
|
||||||
|
* This software is licensed under the stock MIT license:
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included 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 OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
# include <lua.h>
|
||||||
|
# include <lauxlib.h>
|
||||||
|
}
|
||||||
|
# define EXTERN_C extern "C"
|
||||||
|
#else
|
||||||
|
# include <lua.h>
|
||||||
|
# include <lauxlib.h>
|
||||||
|
# define EXTERN_C extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC_VERSION__+0 >= 199901L && !defined(__TINYC__) //< @r-lyeh: handle tcc case
|
||||||
|
#include <complex.h>
|
||||||
|
#define HAVE_COMPLEX
|
||||||
|
#define HAVE_LONG_DOUBLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define DASM_CHECKS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct jit;
|
||||||
|
#define Dst_DECL struct jit* Dst
|
||||||
|
#define Dst_REF (Dst->ctx)
|
||||||
|
#define DASM_EXTERN(a,b,c,d) get_extern(a,b,c,d)
|
||||||
|
|
||||||
|
#include "dynasm/dasm_proto.h"
|
||||||
|
|
||||||
|
#if defined LUA_FFI_BUILD_AS_DLL
|
||||||
|
# define EXPORT __declspec(dllexport)
|
||||||
|
#elif defined __GNUC__
|
||||||
|
# define EXPORT __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
# define EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXTERN_C EXPORT int luaopen_ffi(lua_State* L);
|
||||||
|
|
||||||
|
static int lua_absindex2(lua_State* L, int idx) {
|
||||||
|
return (LUA_REGISTRYINDEX <= idx && idx < 0)
|
||||||
|
? lua_gettop(L) + idx + 1
|
||||||
|
: idx;
|
||||||
|
}
|
||||||
|
/* use our own version of lua_absindex such that lua_absindex(L, 0) == 0 */
|
||||||
|
#define lua_absindex(L, idx) lua_absindex2(L, idx)
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM == 501
|
||||||
|
static void lua_callk(lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k)
|
||||||
|
{
|
||||||
|
lua_call(L, nargs, nresults);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
** set functions from list 'l' into table at top - 'nup'; each
|
||||||
|
** function gets the 'nup' elements at the top as upvalues.
|
||||||
|
** Returns with only the table at the stack.
|
||||||
|
*/
|
||||||
|
static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||||
|
luaL_checkstack(L, nup, "too many upvalues");
|
||||||
|
for (; l && l->name; l++) { /* fill the table with given functions */
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||||
|
lua_pushvalue(L, -nup);
|
||||||
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||||
|
lua_setfield(L, -(nup + 2), l->name);
|
||||||
|
}
|
||||||
|
lua_pop(L, nup); /* remove upvalues */
|
||||||
|
}
|
||||||
|
#define lua_setuservalue lua_setfenv
|
||||||
|
#define lua_getuservalue lua_getfenv
|
||||||
|
#define lua_rawlen lua_objlen
|
||||||
|
static char* luaL_prepbuffsize(luaL_Buffer* B, size_t sz) {
|
||||||
|
if (sz > LUAL_BUFFERSIZE) {
|
||||||
|
luaL_error(B->L, "string too long");
|
||||||
|
}
|
||||||
|
return luaL_prepbuffer(B);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* architectures */
|
||||||
|
#if defined _WIN32 && defined UNDER_CE
|
||||||
|
# define OS_CE
|
||||||
|
#elif defined _WIN32
|
||||||
|
# define OS_WIN
|
||||||
|
#elif defined __APPLE__ && defined __MACH__
|
||||||
|
# define OS_OSX
|
||||||
|
#elif defined __linux__
|
||||||
|
# define OS_LINUX
|
||||||
|
#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
|
||||||
|
# define OS_BSD
|
||||||
|
#elif defined unix || defined __unix__ || defined __unix || defined _POSIX_VERSION || defined _XOPEN_VERSION
|
||||||
|
# define OS_POSIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* architecture */
|
||||||
|
#if defined __i386__ || defined _M_IX86
|
||||||
|
# define ARCH_X86
|
||||||
|
#elif defined __amd64__ || defined _M_X64
|
||||||
|
# define ARCH_X64
|
||||||
|
#elif defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm
|
||||||
|
# define ARCH_ARM
|
||||||
|
#elif defined OS_LINUX && defined __TINYC__ //< @r-lyeh: tcc+linux
|
||||||
|
# define ARCH_X64 //< @r-lyeh: tcc+linux
|
||||||
|
#else
|
||||||
|
# error
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
# ifdef UNDER_CE
|
||||||
|
static void* DoLoadLibraryA(const char* name) {
|
||||||
|
wchar_t buf[MAX_PATH];
|
||||||
|
int sz = MultiByteToWideChar(CP_UTF8, 0, name, -1, buf, 512);
|
||||||
|
if (sz > 0) {
|
||||||
|
buf[sz] = 0;
|
||||||
|
return LoadLibraryW(buf);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# define LoadLibraryA DoLoadLibraryA
|
||||||
|
# else
|
||||||
|
# define GetProcAddressA GetProcAddress
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# define LIB_FORMAT_1 "%s.dll"
|
||||||
|
# define AllocPage(size) VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)
|
||||||
|
# define FreePage(data, size) VirtualFree(data, 0, MEM_RELEASE)
|
||||||
|
# define EnableExecute(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_EXECUTE, &old); FlushInstructionCache(GetCurrentProcess(), data, size);} while (0)
|
||||||
|
# define EnableWrite(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_READWRITE, &old);} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define LIB_FORMAT_1 "%s.so"
|
||||||
|
# define LIB_FORMAT_2 "lib%s.so"
|
||||||
|
# define LoadLibraryA(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
|
||||||
|
# define GetProcAddressA(lib, name) dlsym(lib, name)
|
||||||
|
# define AllocPage(size) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
|
||||||
|
# define FreePage(data, size) munmap(data, size)
|
||||||
|
# define EnableExecute(data, size) mprotect(data, size, PROT_READ|PROT_EXEC)
|
||||||
|
# define EnableWrite(data, size) mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ARCH_X86 || defined ARCH_X64
|
||||||
|
#define ALLOW_MISALIGNED_ACCESS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct token;
|
||||||
|
|
||||||
|
struct parser {
|
||||||
|
int line;
|
||||||
|
const char* next;
|
||||||
|
const char* prev;
|
||||||
|
unsigned align_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct page {
|
||||||
|
size_t size;
|
||||||
|
size_t off;
|
||||||
|
size_t freed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jit {
|
||||||
|
lua_State* L;
|
||||||
|
int32_t last_errno;
|
||||||
|
dasm_State* ctx;
|
||||||
|
size_t pagenum;
|
||||||
|
struct page** pages;
|
||||||
|
size_t align_page_size;
|
||||||
|
void** globals;
|
||||||
|
int function_extern;
|
||||||
|
void* lua_dll;
|
||||||
|
void* kernel32_dll;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ALIGN_DOWN(PTR, MASK) \
|
||||||
|
(((uintptr_t) (PTR)) & (~ ((uintptr_t) (MASK)) ))
|
||||||
|
#define ALIGN_UP(PTR, MASK) \
|
||||||
|
(( ((uintptr_t) (PTR)) + ((uintptr_t) (MASK)) ) & (~ ((uintptr_t) (MASK)) ))
|
||||||
|
|
||||||
|
/* struct cdata/struct ctype */
|
||||||
|
|
||||||
|
#define PTR_ALIGN_MASK (sizeof(void*) - 1)
|
||||||
|
#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1)
|
||||||
|
#define DEFAULT_ALIGN_MASK 7
|
||||||
|
|
||||||
|
#ifdef OS_OSX
|
||||||
|
/* TODO: figure out why the alignof trick doesn't work on OS X */
|
||||||
|
#define ALIGNED_DEFAULT 7
|
||||||
|
#elif defined __GNUC__
|
||||||
|
#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1)
|
||||||
|
#else
|
||||||
|
#define ALIGNED_DEFAULT PTR_ALIGN_MASK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int jit_key;
|
||||||
|
extern int ctype_mt_key;
|
||||||
|
extern int cdata_mt_key;
|
||||||
|
extern int cmodule_mt_key;
|
||||||
|
extern int callback_mt_key;
|
||||||
|
extern int constants_key;
|
||||||
|
extern int types_key;
|
||||||
|
extern int gc_key;
|
||||||
|
extern int callbacks_key;
|
||||||
|
extern int functions_key;
|
||||||
|
extern int abi_key;
|
||||||
|
extern int next_unnamed_key;
|
||||||
|
extern int niluv_key;
|
||||||
|
extern int asmname_key;
|
||||||
|
|
||||||
|
int equals_upval(lua_State* L, int idx, int* key);
|
||||||
|
void push_upval(lua_State* L, int* key);
|
||||||
|
void set_upval(lua_State* L, int* key);
|
||||||
|
struct jit* get_jit(lua_State* L);
|
||||||
|
|
||||||
|
/* both ctype and cdata are stored as userdatas
|
||||||
|
*
|
||||||
|
* usr value is a table shared between the related subtypes which has:
|
||||||
|
* name -> member ctype (for structs and unions)
|
||||||
|
* +ves -> member ctype - in memory order (for structs)
|
||||||
|
* +ves -> argument ctype (for function prototypes)
|
||||||
|
* 0 -> return ctype (for function prototypes)
|
||||||
|
* light userdata -> misc
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
C_CALL,
|
||||||
|
STD_CALL,
|
||||||
|
FAST_CALL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
INVALID_TYPE,
|
||||||
|
VOID_TYPE,
|
||||||
|
FLOAT_TYPE,
|
||||||
|
DOUBLE_TYPE,
|
||||||
|
LONG_DOUBLE_TYPE,
|
||||||
|
COMPLEX_FLOAT_TYPE,
|
||||||
|
COMPLEX_DOUBLE_TYPE,
|
||||||
|
COMPLEX_LONG_DOUBLE_TYPE,
|
||||||
|
BOOL_TYPE,
|
||||||
|
INT8_TYPE,
|
||||||
|
INT16_TYPE,
|
||||||
|
INT32_TYPE,
|
||||||
|
INT64_TYPE,
|
||||||
|
INTPTR_TYPE,
|
||||||
|
ENUM_TYPE,
|
||||||
|
UNION_TYPE,
|
||||||
|
STRUCT_TYPE,
|
||||||
|
FUNCTION_TYPE,
|
||||||
|
FUNCTION_PTR_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IS_CHAR_UNSIGNED (((char) -1) > 0)
|
||||||
|
#define IS_COMPLEX(type) ((type) == COMPLEX_FLOAT_TYPE || (type) == COMPLEX_DOUBLE_TYPE)
|
||||||
|
|
||||||
|
#define POINTER_BITS 2
|
||||||
|
#define POINTER_MAX ((1 << POINTER_BITS) - 1)
|
||||||
|
|
||||||
|
#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1))
|
||||||
|
|
||||||
|
/* Note: if adding a new member that is associated with a struct/union
|
||||||
|
* definition then it needs to be copied over in ctype.c:set_defined for when
|
||||||
|
* we create types based off of the declaration alone.
|
||||||
|
*
|
||||||
|
* Since this is used as a header for every ctype and cdata, and we create a
|
||||||
|
* ton of them on the stack, we try and minimise its size.
|
||||||
|
*/
|
||||||
|
struct ctype {
|
||||||
|
size_t base_size; /* size of the base type in bytes */
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* valid if is_bitfield */
|
||||||
|
struct {
|
||||||
|
/* size of bitfield in bits */
|
||||||
|
unsigned bit_size : 7;
|
||||||
|
/* offset within the current byte between 0-63 */
|
||||||
|
unsigned bit_offset : 6;
|
||||||
|
};
|
||||||
|
/* Valid if is_array */
|
||||||
|
size_t array_size;
|
||||||
|
/* Valid for is_variable_struct or is_variable_array. If
|
||||||
|
* variable_size_known (only used for is_variable_struct) then this is
|
||||||
|
* the total increment otherwise this is the per element increment.
|
||||||
|
*/
|
||||||
|
size_t variable_increment;
|
||||||
|
};
|
||||||
|
size_t offset;
|
||||||
|
unsigned align_mask : 4; /* as (align bytes - 1) eg 7 gives 8 byte alignment */
|
||||||
|
unsigned pointers : POINTER_BITS; /* number of dereferences to get to the base type including +1 for arrays */
|
||||||
|
unsigned const_mask : POINTER_MAX + 1; /* const pointer mask, LSB is current pointer, +1 for the whether the base type is const */
|
||||||
|
unsigned type : 5; /* value given by type enum above */
|
||||||
|
unsigned is_reference : 1;
|
||||||
|
unsigned is_array : 1;
|
||||||
|
unsigned is_defined : 1;
|
||||||
|
unsigned is_null : 1;
|
||||||
|
unsigned has_member_name : 1;
|
||||||
|
unsigned calling_convention : 2;
|
||||||
|
unsigned has_var_arg : 1;
|
||||||
|
unsigned is_variable_array : 1; /* set for variable array types where we don't know the variable size yet */
|
||||||
|
unsigned is_variable_struct : 1;
|
||||||
|
unsigned variable_size_known : 1; /* used for variable structs after we know the variable size */
|
||||||
|
unsigned is_bitfield : 1;
|
||||||
|
unsigned has_bitfield : 1;
|
||||||
|
unsigned is_jitted : 1;
|
||||||
|
unsigned is_packed : 1;
|
||||||
|
unsigned is_unsigned : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__declspec(align(16))
|
||||||
|
#endif
|
||||||
|
struct cdata {
|
||||||
|
const struct ctype type
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__ ((aligned(16)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*cfunction)(void);
|
||||||
|
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
typedef double complex complex_double;
|
||||||
|
typedef float complex complex_float;
|
||||||
|
#else
|
||||||
|
typedef struct {
|
||||||
|
double real, imag;
|
||||||
|
} complex_double;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float real, imag;
|
||||||
|
} complex_float;
|
||||||
|
|
||||||
|
static double creal(complex_double c) {
|
||||||
|
return c.real;
|
||||||
|
}
|
||||||
|
static float crealf(complex_float c) {
|
||||||
|
return c.real;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double cimag(complex_double c) {
|
||||||
|
return c.imag;
|
||||||
|
}
|
||||||
|
static float cimagf(complex_float c) {
|
||||||
|
return c.imag;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CALLBACK_FUNC_USR_IDX 1
|
||||||
|
|
||||||
|
void set_defined(lua_State* L, int ct_usr, struct ctype* ct);
|
||||||
|
struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct);
|
||||||
|
void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct); /* called from asm */
|
||||||
|
void push_callback(lua_State* L, cfunction f);
|
||||||
|
void check_ctype(lua_State* L, int idx, struct ctype* ct);
|
||||||
|
void* to_cdata(lua_State* L, int idx, struct ctype* ct);
|
||||||
|
void* check_cdata(lua_State* L, int idx, struct ctype* ct);
|
||||||
|
size_t ctype_size(lua_State* L, const struct ctype* ct);
|
||||||
|
|
||||||
|
int parse_type(lua_State* L, struct parser* P, struct ctype* type);
|
||||||
|
void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* type, struct token* name, struct parser* asmname);
|
||||||
|
void push_type_name(lua_State* L, int usr, const struct ctype* ct);
|
||||||
|
|
||||||
|
int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct);
|
||||||
|
|
||||||
|
int ffi_cdef(lua_State* L);
|
||||||
|
|
||||||
|
void push_func_ref(lua_State* L, cfunction func);
|
||||||
|
void free_code(struct jit* jit, lua_State* L, cfunction func);
|
||||||
|
int x86_return_size(lua_State* L, int usr, const struct ctype* ct);
|
||||||
|
void compile_function(lua_State* L, cfunction f, int ct_usr, const struct ctype* ct);
|
||||||
|
cfunction compile_callback(lua_State* L, int fidx, int ct_usr, const struct ctype* ct);
|
||||||
|
void compile_globals(struct jit* jit, lua_State* L);
|
||||||
|
int get_extern(struct jit* jit, uint8_t* addr, int idx, int type);
|
||||||
|
|
||||||
|
/* WARNING: assembly needs to be updated for prototype changes of these functions */
|
||||||
|
int check_bool(lua_State* L, int idx);
|
||||||
|
double check_double(lua_State* L, int idx);
|
||||||
|
double check_complex_imag(lua_State* L, int idx);
|
||||||
|
float check_float(lua_State* L, int idx);
|
||||||
|
uint64_t check_uint64(lua_State* L, int idx);
|
||||||
|
int64_t check_int64(lua_State* L, int idx);
|
||||||
|
int32_t check_int32(lua_State* L, int idx);
|
||||||
|
uint32_t check_uint32(lua_State* L, int idx);
|
||||||
|
uintptr_t check_uintptr(lua_State* L, int idx);
|
||||||
|
int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||||||
|
/* these two will always push a value so that we can create structs/functions on the fly */
|
||||||
|
void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||||||
|
cfunction check_typed_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt);
|
||||||
|
complex_double check_complex_double(lua_State* L, int idx);
|
||||||
|
complex_float check_complex_float(lua_State* L, int idx);
|
||||||
|
|
||||||
|
void unpack_varargs_stack(lua_State* L, int first, int last, char* to);
|
||||||
|
void unpack_varargs_reg(lua_State* L, int first, int last, char* to);
|
||||||
|
|
||||||
|
void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to);
|
||||||
|
void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to);
|
||||||
|
void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to);
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,677 @@
|
||||||
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC_VERSION__+0 >= 199901L
|
||||||
|
#include <complex.h>
|
||||||
|
#define HAVE_COMPLEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define EXTERN_C extern "C"
|
||||||
|
#else
|
||||||
|
# define EXTERN_C extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define EXPORT EXTERN_C __declspec(dllexport)
|
||||||
|
#elif defined __GNUC__
|
||||||
|
#define EXPORT EXTERN_C __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define EXPORT EXTERN_C
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum e8 {
|
||||||
|
FOO8,
|
||||||
|
BAR8,
|
||||||
|
};
|
||||||
|
enum e16 {
|
||||||
|
FOO16 = 1 << 8,
|
||||||
|
BAR16,
|
||||||
|
BIG16 = 1 << 14,
|
||||||
|
};
|
||||||
|
enum e32 {
|
||||||
|
FOO32 = 1 << 16,
|
||||||
|
BAR32,
|
||||||
|
BIG32 = 1 << 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT bool have_complex();
|
||||||
|
|
||||||
|
bool have_complex()
|
||||||
|
{
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT bool is_msvc;
|
||||||
|
|
||||||
|
bool is_msvc =
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
1;
|
||||||
|
#else
|
||||||
|
0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT int test_pow(int v);
|
||||||
|
int test_pow(int v)
|
||||||
|
{ return v * v; }
|
||||||
|
|
||||||
|
#define ADD(TYPE, NAME) \
|
||||||
|
EXPORT TYPE NAME(TYPE a, TYPE b); \
|
||||||
|
TYPE NAME(TYPE a, TYPE b) { return a + b; }
|
||||||
|
|
||||||
|
ADD(int8_t, add_i8)
|
||||||
|
ADD(uint8_t, add_u8)
|
||||||
|
ADD(int16_t, add_i16)
|
||||||
|
ADD(uint16_t, add_u16)
|
||||||
|
ADD(int32_t, add_i32)
|
||||||
|
ADD(uint32_t, add_u32)
|
||||||
|
ADD(int64_t, add_i64)
|
||||||
|
ADD(uint64_t, add_u64)
|
||||||
|
ADD(double, add_d)
|
||||||
|
ADD(float, add_f)
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
ADD(double complex, add_dc)
|
||||||
|
ADD(float complex, add_fc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT enum e8 inc_e8(enum e8 v);
|
||||||
|
EXPORT enum e16 inc_e16(enum e16 v);
|
||||||
|
EXPORT enum e32 inc_e32(enum e32 v);
|
||||||
|
enum e8 inc_e8(enum e8 v) {return v+1;}
|
||||||
|
enum e16 inc_e16(enum e16 v) {return v+1;}
|
||||||
|
enum e32 inc_e32(enum e32 v) {return v+1;}
|
||||||
|
|
||||||
|
EXPORT _Bool not_b(_Bool v);
|
||||||
|
EXPORT _Bool not_b2(_Bool v);
|
||||||
|
|
||||||
|
_Bool not_b(_Bool v) {return !v;}
|
||||||
|
_Bool not_b2(_Bool v) {return !v;}
|
||||||
|
|
||||||
|
#define PRINT(TYPE, NAME, FORMAT) \
|
||||||
|
EXPORT int NAME(char* buf, TYPE val); \
|
||||||
|
int NAME(char* buf, TYPE val) {return sprintf(buf, "%" FORMAT, val);}
|
||||||
|
|
||||||
|
PRINT(int8_t, print_i8, PRId8)
|
||||||
|
PRINT(uint8_t, print_u8, PRIu8)
|
||||||
|
PRINT(int16_t, print_i16, PRId16)
|
||||||
|
PRINT(uint16_t, print_u16, PRIu16)
|
||||||
|
PRINT(int32_t, print_i32, PRId32)
|
||||||
|
PRINT(uint32_t, print_u32, PRIu32)
|
||||||
|
PRINT(int64_t, print_i64, PRId64)
|
||||||
|
PRINT(uint64_t, print_u64, PRIu64)
|
||||||
|
PRINT(double, print_d, "g")
|
||||||
|
PRINT(float, print_f, "g")
|
||||||
|
PRINT(const char*, print_s, "s")
|
||||||
|
PRINT(void*, print_p, "p")
|
||||||
|
PRINT(enum e8, print_e8, "d")
|
||||||
|
PRINT(enum e16, print_e16, "d")
|
||||||
|
PRINT(enum e32, print_e32, "d")
|
||||||
|
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
EXPORT int print_dc(char* buf, double complex val);
|
||||||
|
EXPORT int print_fc(char* buf, float complex val);
|
||||||
|
int print_dc(char* buf, double complex val) {return sprintf(buf, "%g+%gi", creal(val), cimag(val));}
|
||||||
|
int print_fc(char* buf, float complex val) {return sprintf(buf, "%g+%gi", creal(val), cimag(val));}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT int print_b(char* buf, _Bool val);
|
||||||
|
EXPORT int print_b2(char* buf, _Bool val);
|
||||||
|
int print_b(char* buf, _Bool val) {return sprintf(buf, "%s", val ? "true" : "false");}
|
||||||
|
int print_b2(char* buf, _Bool val) {return sprintf(buf, "%s", val ? "true" : "false");}
|
||||||
|
|
||||||
|
EXPORT bool (*ret_fp(bool (*val)(bool)))(bool);
|
||||||
|
bool (*ret_fp(bool (*val)(bool)))(bool)
|
||||||
|
{return val;}
|
||||||
|
|
||||||
|
#define OFFSETOF(STRUCT, MEMBER) ((int) ((char*) &STRUCT.MEMBER - (char*) &S - 1))
|
||||||
|
|
||||||
|
#define ALIGN_UP(VALUE, ALIGNMENT, SUFFIX) \
|
||||||
|
struct align_##ALIGNMENT##_##SUFFIX { \
|
||||||
|
char pad; \
|
||||||
|
VALUE; \
|
||||||
|
}; \
|
||||||
|
EXPORT int print_align_##ALIGNMENT##_##SUFFIX(char* buf, struct align_##ALIGNMENT##_##SUFFIX* p); \
|
||||||
|
int print_align_##ALIGNMENT##_##SUFFIX(char* buf, struct align_##ALIGNMENT##_##SUFFIX* p) { \
|
||||||
|
struct {char ch; struct align_##ALIGNMENT##_##SUFFIX v;} s; \
|
||||||
|
int off = sprintf(buf, "size %d offset %d align %d value ", \
|
||||||
|
(int) sizeof(s.v), \
|
||||||
|
(int) (((char*) &p->v) - (char*) p), \
|
||||||
|
(int) (((char*) &s.v) - (char*) &s)); \
|
||||||
|
return print_##SUFFIX(buf+off, p->v); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
#define COMPLEX_ALIGN(ALIGNMENT, ATTR) \
|
||||||
|
ALIGN_UP(ATTR(double complex), ALIGNMENT, dc) \
|
||||||
|
ALIGN_UP(ATTR(float complex), ALIGNMENT, fc)
|
||||||
|
#else
|
||||||
|
#define COMPLEX_ALIGN(ALIGNMENT, ATTR)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* MSVC doesn't support __declspec(aligned(#)) on enums see C4329 */
|
||||||
|
#define ENUM_ALIGN2(ALIGNMENT, ATTR) \
|
||||||
|
ALIGN_UP(ATTR(enum e8), ALIGNMENT, e8) \
|
||||||
|
ALIGN_UP(ATTR(enum e16), ALIGNMENT, e16) \
|
||||||
|
ALIGN_UP(ATTR(enum e32), ALIGNMENT, e32) \
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define ENUM_ALIGN(ALIGNMENT, ATTR)
|
||||||
|
#else
|
||||||
|
#define ENUM_ALIGN(ALIGNMENT, ATTR) ENUM_ALIGN2(ALIGNMENT, ATTR)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ALIGN2(ALIGNMENT, ATTR) \
|
||||||
|
ALIGN_UP(ATTR(uint16_t), ALIGNMENT, u16) \
|
||||||
|
ALIGN_UP(ATTR(uint32_t), ALIGNMENT, u32) \
|
||||||
|
ALIGN_UP(ATTR(uint64_t), ALIGNMENT, u64) \
|
||||||
|
ALIGN_UP(ATTR(float), ALIGNMENT, f) \
|
||||||
|
ALIGN_UP(ATTR(double), ALIGNMENT, d) \
|
||||||
|
ALIGN_UP(ATTR(const char*), ALIGNMENT, s) \
|
||||||
|
ALIGN_UP(ATTR(void*), ALIGNMENT, p) \
|
||||||
|
ALIGN_UP(ATTR(_Bool), ALIGNMENT, b) \
|
||||||
|
ALIGN_UP(ATTR(_Bool), ALIGNMENT, b2) \
|
||||||
|
ENUM_ALIGN(ALIGNMENT, ATTR) \
|
||||||
|
COMPLEX_ALIGN(ALIGNMENT, ATTR)
|
||||||
|
|
||||||
|
#define NO_ATTR(TYPE) TYPE v
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define ALIGN_NO_ATTR(ALIGNMENT) \
|
||||||
|
ALIGN2(ALIGNMENT, NO_ATTR) \
|
||||||
|
ENUM_ALIGN2(ALIGNMENT, NO_ATTR)
|
||||||
|
#else
|
||||||
|
#define ALIGN_NO_ATTR(ALIGNMENT) \
|
||||||
|
ALIGN2(ALIGNMENT, NO_ATTR)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ALIGN_NO_ATTR(0)
|
||||||
|
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(1)
|
||||||
|
ALIGN_NO_ATTR(1)
|
||||||
|
#pragma pack(2)
|
||||||
|
ALIGN_NO_ATTR(2)
|
||||||
|
#pragma pack(4)
|
||||||
|
ALIGN_NO_ATTR(4)
|
||||||
|
#pragma pack(8)
|
||||||
|
ALIGN_NO_ATTR(8)
|
||||||
|
#pragma pack(16)
|
||||||
|
ALIGN_NO_ATTR(16)
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define ATTR_(TYPE, ALIGN) __declspec(align(ALIGN)) TYPE v
|
||||||
|
#else
|
||||||
|
#define ATTR_(TYPE, ALIGN) TYPE v __attribute__((aligned(ALIGN)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ATTR1(TYPE) ATTR_(TYPE, 1)
|
||||||
|
#define ATTR2(TYPE) ATTR_(TYPE, 2)
|
||||||
|
#define ATTR4(TYPE) ATTR_(TYPE, 4)
|
||||||
|
#define ATTR8(TYPE) ATTR_(TYPE, 8)
|
||||||
|
#define ATTR16(TYPE) ATTR_(TYPE, 16)
|
||||||
|
|
||||||
|
#define ATTR_DEF(TYPE) TYPE v __attribute__((aligned))
|
||||||
|
|
||||||
|
ALIGN2(attr_1, ATTR1)
|
||||||
|
ALIGN2(attr_2, ATTR2)
|
||||||
|
ALIGN2(attr_4, ATTR4)
|
||||||
|
ALIGN2(attr_8, ATTR8)
|
||||||
|
ALIGN2(attr_16, ATTR16)
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
ALIGN2(attr_def, ATTR_DEF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define alignof(type) __alignof(type)
|
||||||
|
#else
|
||||||
|
#define alignof(type) __alignof__(type)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT int max_alignment();
|
||||||
|
|
||||||
|
int max_alignment()
|
||||||
|
{ return alignof(struct align_attr_16_p); }
|
||||||
|
|
||||||
|
/* bit_fields1.cpp */
|
||||||
|
/* compile with: /LD */
|
||||||
|
struct Date {
|
||||||
|
unsigned short nWeekDay : 3; /* 0..7 (3 bits) */
|
||||||
|
unsigned short nMonthDay : 6; /* 0..31 (6 bits) */
|
||||||
|
unsigned short nMonth : 5; /* 0..12 (5 bits) */
|
||||||
|
unsigned short nYear : 8; /* 0..100 (8 bits) */
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_date(size_t* sz, size_t* align, char* buf, struct Date* d);
|
||||||
|
|
||||||
|
int print_date(size_t* sz, size_t* align, char* buf, struct Date* d) {
|
||||||
|
*sz = sizeof(struct Date);
|
||||||
|
*align = alignof(struct Date);
|
||||||
|
return sprintf(buf, "%d %d %d %d", d->nWeekDay, d->nMonthDay, d->nMonth, d->nYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bit_fields2.cpp */
|
||||||
|
/* compile with: /LD */
|
||||||
|
struct Date2 {
|
||||||
|
unsigned nWeekDay : 3; /* 0..7 (3 bits) */
|
||||||
|
unsigned nMonthDay : 6; /* 0..31 (6 bits) */
|
||||||
|
unsigned : 0; /* Force alignment to next boundary. */
|
||||||
|
unsigned nMonth : 5; /* 0..12 (5 bits) */
|
||||||
|
unsigned nYear : 8; /* 0..100 (8 bits) */
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_date2(size_t* sz, size_t* align, char* buf, struct Date2* d);
|
||||||
|
|
||||||
|
int print_date2(size_t* sz, size_t* align, char* buf, struct Date2* d) {
|
||||||
|
*sz = sizeof(struct Date2);
|
||||||
|
*align = alignof(struct Date2);
|
||||||
|
return sprintf(buf, "%d %d %d %d", d->nWeekDay, d->nMonthDay, d->nMonth, d->nYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examples from SysV X86 ABI
|
||||||
|
struct sysv1 {
|
||||||
|
int j:5;
|
||||||
|
int k:6;
|
||||||
|
int m:7;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv1(size_t* sz, size_t* align, char* buf, struct sysv1* s);
|
||||||
|
|
||||||
|
int print_sysv1(size_t* sz, size_t* align, char* buf, struct sysv1* s) {
|
||||||
|
*sz = sizeof(struct sysv1);
|
||||||
|
*align = alignof(struct sysv1);
|
||||||
|
return sprintf(buf, "%d %d %d", s->j, s->k, s->m);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sysv2 {
|
||||||
|
short s:9;
|
||||||
|
int j:9;
|
||||||
|
char c;
|
||||||
|
short t:9;
|
||||||
|
short u:9;
|
||||||
|
char d;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv2(size_t* sz, size_t* align, char* buf, struct sysv2* s);
|
||||||
|
|
||||||
|
int print_sysv2(size_t* sz, size_t* align, char* buf, struct sysv2* s) {
|
||||||
|
*sz = sizeof(struct sysv2);
|
||||||
|
*align = alignof(struct sysv2);
|
||||||
|
return sprintf(buf, "%d %d %d %d %d %d", s->s, s->j, s->c, s->t, s->u, s->d);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sysv3 {
|
||||||
|
char c;
|
||||||
|
short s:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv3(size_t* sz, size_t* align, char* buf, struct sysv3* s);
|
||||||
|
|
||||||
|
int print_sysv3(size_t* sz, size_t* align, char* buf, struct sysv3* s) {
|
||||||
|
*sz = sizeof(struct sysv3);
|
||||||
|
*align = alignof(struct sysv3);
|
||||||
|
return sprintf(buf, "%d %d", s->c, s->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
union sysv4 {
|
||||||
|
char c;
|
||||||
|
short s:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv4(size_t* sz, size_t* align, char* buf, union sysv4* s);
|
||||||
|
|
||||||
|
int print_sysv4(size_t* sz, size_t* align, char* buf, union sysv4* s) {
|
||||||
|
*sz = sizeof(union sysv4);
|
||||||
|
*align = alignof(union sysv4);
|
||||||
|
return sprintf(buf, "%d", s->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sysv5 {
|
||||||
|
char c;
|
||||||
|
int :0;
|
||||||
|
char d;
|
||||||
|
short :9;
|
||||||
|
char e;
|
||||||
|
char :0;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv5(size_t* sz, size_t* align, char* buf, struct sysv5* s);
|
||||||
|
|
||||||
|
int print_sysv5(size_t* sz, size_t* align, char* buf, struct sysv5* s) {
|
||||||
|
*sz = sizeof(struct sysv5);
|
||||||
|
*align = alignof(struct sysv5);
|
||||||
|
return sprintf(buf, "%d %d %d", s->c, s->d, s->e);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sysv6 {
|
||||||
|
char c;
|
||||||
|
int :0;
|
||||||
|
char d;
|
||||||
|
int :9;
|
||||||
|
char e;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv6(size_t* sz, size_t* align, char* buf, struct sysv6* s);
|
||||||
|
|
||||||
|
int print_sysv6(size_t* sz, size_t* align, char* buf, struct sysv6* s) {
|
||||||
|
*sz = sizeof(struct sysv6);
|
||||||
|
*align = alignof(struct sysv6);
|
||||||
|
return sprintf(buf, "%d %d %d", s->c, s->d, s->e);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sysv7 {
|
||||||
|
int j:9;
|
||||||
|
short s:9;
|
||||||
|
char c;
|
||||||
|
short t:9;
|
||||||
|
short u:9;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_sysv7(size_t* sz, size_t* align, char* buf, struct sysv7* s);
|
||||||
|
|
||||||
|
int print_sysv7(size_t* sz, size_t* align, char* buf, struct sysv7* s) {
|
||||||
|
*sz = sizeof(struct sysv7);
|
||||||
|
*align = alignof(struct sysv7);
|
||||||
|
return sprintf(buf, "%d %d %d %d %d", s->j, s->s, s->c, s->t, s->u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now some targeting bitfield tests */
|
||||||
|
|
||||||
|
/* Bitfield alignment */
|
||||||
|
#define BITALIGN(TNUM,BNUM) \
|
||||||
|
struct ba_##TNUM##_##BNUM { \
|
||||||
|
char a; \
|
||||||
|
uint##TNUM##_t b : BNUM; \
|
||||||
|
}; \
|
||||||
|
EXPORT int print_ba_##TNUM##_##BNUM(size_t* sz, size_t* align, char* buf, struct ba_##TNUM##_##BNUM* s); \
|
||||||
|
int print_ba_##TNUM##_##BNUM(size_t* sz, size_t* align, char* buf, struct ba_##TNUM##_##BNUM* s) { \
|
||||||
|
*sz = sizeof(struct ba_##TNUM##_##BNUM); \
|
||||||
|
*align = alignof(struct ba_##TNUM##_##BNUM); \
|
||||||
|
return sprintf(buf, "%d %d", (int) s->a, (int) s->b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BITALIGN(8,7)
|
||||||
|
BITALIGN(16,7)
|
||||||
|
BITALIGN(16,15)
|
||||||
|
BITALIGN(32,7)
|
||||||
|
BITALIGN(32,15)
|
||||||
|
BITALIGN(32,31)
|
||||||
|
BITALIGN(64,7)
|
||||||
|
BITALIGN(64,15)
|
||||||
|
BITALIGN(64,31)
|
||||||
|
BITALIGN(64,63)
|
||||||
|
|
||||||
|
/* Do unsigned and signed coallesce */
|
||||||
|
#define BITCOALESCE(NUM) \
|
||||||
|
struct bc##NUM { \
|
||||||
|
uint##NUM##_t a : 3; \
|
||||||
|
int##NUM##_t b : 3; \
|
||||||
|
}; \
|
||||||
|
EXPORT int print_bc##NUM(size_t* sz, size_t* align, char* buf, struct bc##NUM* s); \
|
||||||
|
int print_bc##NUM(size_t* sz, size_t* align, char* buf, struct bc##NUM* s) { \
|
||||||
|
*sz = sizeof(struct bc##NUM); \
|
||||||
|
*align = alignof(struct bc##NUM); \
|
||||||
|
return sprintf(buf, "%d %d", (int) s->a, (int) s->b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BITCOALESCE(8)
|
||||||
|
BITCOALESCE(16)
|
||||||
|
BITCOALESCE(32)
|
||||||
|
BITCOALESCE(64)
|
||||||
|
|
||||||
|
// Do different sizes coallesce
|
||||||
|
struct bdsz {
|
||||||
|
uint8_t a : 3;
|
||||||
|
uint16_t b : 3;
|
||||||
|
uint32_t c : 3;
|
||||||
|
uint64_t d : 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_bdsz(size_t* sz, size_t* align, char* buf, struct bdsz* s);
|
||||||
|
int print_bdsz(size_t* sz, size_t* align, char* buf, struct bdsz* s) {
|
||||||
|
*sz = sizeof(struct bdsz);
|
||||||
|
*align = alignof(struct bdsz);
|
||||||
|
return sprintf(buf, "%d %d %d %d", (int) s->a, (int) s->b, (int) s->c, (int) s->d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does coallesence upgrade the storage unit
|
||||||
|
struct bcup {
|
||||||
|
uint8_t a : 7;
|
||||||
|
uint16_t b : 9;
|
||||||
|
uint32_t c : 17;
|
||||||
|
uint64_t d : 33;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_bcup(size_t* sz, size_t* align, char* buf, struct bcup* s);
|
||||||
|
int print_bcup(size_t* sz, size_t* align, char* buf, struct bcup* s) {
|
||||||
|
*sz = sizeof(struct bcup);
|
||||||
|
*align = alignof(struct bcup);
|
||||||
|
return sprintf(buf, "%d %d %d %"PRIu64, (int) s->a, (int) s->b, (int) s->c, (uint64_t) s->d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is unaligned access allowed
|
||||||
|
struct buna {
|
||||||
|
uint32_t a : 31;
|
||||||
|
uint32_t b : 31;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int print_buna(size_t* sz, size_t* align, char* buf, struct buna* s);
|
||||||
|
int print_buna(size_t* sz, size_t* align, char* buf, struct buna* s) {
|
||||||
|
*sz = sizeof(struct buna);
|
||||||
|
*align = alignof(struct buna);
|
||||||
|
return sprintf(buf, "%d %d", (int) s->a, (int) s->b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* What does a lone :0 do */
|
||||||
|
#define BITLONEZERO(NUM) \
|
||||||
|
struct blz##NUM { \
|
||||||
|
uint##NUM##_t a; \
|
||||||
|
uint##NUM##_t :0; \
|
||||||
|
uint##NUM##_t b; \
|
||||||
|
}; \
|
||||||
|
EXPORT int print_##blz##NUM(size_t* sz, size_t* align, char* buf, struct blz##NUM* s); \
|
||||||
|
int print_blz##NUM(size_t* sz, size_t* align, char* buf, struct blz##NUM* s) { \
|
||||||
|
*sz = sizeof(struct blz##NUM); \
|
||||||
|
*align = alignof(struct blz##NUM); \
|
||||||
|
return sprintf(buf, "%d %d", (int) s->a, (int) s->b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BITLONEZERO(8)
|
||||||
|
BITLONEZERO(16)
|
||||||
|
BITLONEZERO(32)
|
||||||
|
BITLONEZERO(64)
|
||||||
|
|
||||||
|
/* What does a :0 or unnamed :# of the same or different type do */
|
||||||
|
#define BITZERO(NUM, ZNUM, BNUM) \
|
||||||
|
struct bz_##NUM##_##ZNUM##_##BNUM { \
|
||||||
|
uint8_t a; \
|
||||||
|
uint##NUM##_t b : 3; \
|
||||||
|
uint##ZNUM##_t :BNUM; \
|
||||||
|
uint##NUM##_t c : 3; \
|
||||||
|
}; \
|
||||||
|
EXPORT int print_bz_##NUM##_##ZNUM##_##BNUM(size_t* sz, size_t* align, char* buf, struct bz_##NUM##_##ZNUM##_##BNUM* s); \
|
||||||
|
int print_bz_##NUM##_##ZNUM##_##BNUM(size_t* sz, size_t* align, char* buf, struct bz_##NUM##_##ZNUM##_##BNUM* s) { \
|
||||||
|
*sz = sizeof(struct bz_##NUM##_##ZNUM##_##BNUM); \
|
||||||
|
*align = alignof(struct bz_##NUM##_##ZNUM##_##BNUM); \
|
||||||
|
return sprintf(buf, "%d %d %d", (int) s->a, (int) s->b, (int) s->c); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BITZERO(8,8,0)
|
||||||
|
BITZERO(8,8,7)
|
||||||
|
BITZERO(8,16,0)
|
||||||
|
BITZERO(8,16,7)
|
||||||
|
BITZERO(8,16,15)
|
||||||
|
BITZERO(8,32,0)
|
||||||
|
BITZERO(8,32,7)
|
||||||
|
BITZERO(8,32,15)
|
||||||
|
BITZERO(8,32,31)
|
||||||
|
BITZERO(8,64,0)
|
||||||
|
BITZERO(8,64,7)
|
||||||
|
BITZERO(8,64,15)
|
||||||
|
BITZERO(8,64,31)
|
||||||
|
BITZERO(8,64,63)
|
||||||
|
BITZERO(16,8,0)
|
||||||
|
BITZERO(16,8,7)
|
||||||
|
BITZERO(16,16,0)
|
||||||
|
BITZERO(16,16,7)
|
||||||
|
BITZERO(16,16,15)
|
||||||
|
BITZERO(16,32,0)
|
||||||
|
BITZERO(16,32,7)
|
||||||
|
BITZERO(16,32,15)
|
||||||
|
BITZERO(16,32,31)
|
||||||
|
BITZERO(16,64,0)
|
||||||
|
BITZERO(16,64,7)
|
||||||
|
BITZERO(16,64,15)
|
||||||
|
BITZERO(16,64,31)
|
||||||
|
BITZERO(16,64,63)
|
||||||
|
BITZERO(32,8,0)
|
||||||
|
BITZERO(32,8,7)
|
||||||
|
BITZERO(32,16,0)
|
||||||
|
BITZERO(32,16,7)
|
||||||
|
BITZERO(32,16,15)
|
||||||
|
BITZERO(32,32,0)
|
||||||
|
BITZERO(32,32,7)
|
||||||
|
BITZERO(32,32,15)
|
||||||
|
BITZERO(32,32,31)
|
||||||
|
BITZERO(32,64,0)
|
||||||
|
BITZERO(32,64,7)
|
||||||
|
BITZERO(32,64,15)
|
||||||
|
BITZERO(32,64,31)
|
||||||
|
BITZERO(32,64,63)
|
||||||
|
BITZERO(64,8,0)
|
||||||
|
BITZERO(64,8,7)
|
||||||
|
BITZERO(64,16,0)
|
||||||
|
BITZERO(64,16,7)
|
||||||
|
BITZERO(64,16,15)
|
||||||
|
BITZERO(64,32,0)
|
||||||
|
BITZERO(64,32,7)
|
||||||
|
BITZERO(64,32,15)
|
||||||
|
BITZERO(64,32,31)
|
||||||
|
BITZERO(64,64,0)
|
||||||
|
BITZERO(64,64,7)
|
||||||
|
BITZERO(64,64,15)
|
||||||
|
BITZERO(64,64,31)
|
||||||
|
BITZERO(64,64,63)
|
||||||
|
|
||||||
|
#define CALL(TYPE, SUFFIX) \
|
||||||
|
EXPORT TYPE call_##SUFFIX(TYPE (*func)(TYPE), TYPE arg); \
|
||||||
|
TYPE call_##SUFFIX(TYPE (*func)(TYPE), TYPE arg) { \
|
||||||
|
return func(arg); \
|
||||||
|
}
|
||||||
|
|
||||||
|
CALL(int, i)
|
||||||
|
CALL(float, f)
|
||||||
|
CALL(double, d)
|
||||||
|
CALL(const char*, s)
|
||||||
|
CALL(_Bool, b)
|
||||||
|
CALL(enum e8, e8)
|
||||||
|
CALL(enum e16, e16)
|
||||||
|
CALL(enum e32, e32)
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
CALL(double complex, dc)
|
||||||
|
CALL(float complex, fc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct fptr {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int (__cdecl *p)(int);
|
||||||
|
#else
|
||||||
|
int (*p)(int);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPORT int call_fptr(struct fptr* s, int val);
|
||||||
|
|
||||||
|
int call_fptr(struct fptr* s, int val) {
|
||||||
|
return (s->p)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT bool g_b;
|
||||||
|
EXPORT int8_t g_i8;
|
||||||
|
EXPORT int16_t g_i16;
|
||||||
|
EXPORT int32_t g_i32;
|
||||||
|
EXPORT int64_t g_i64;
|
||||||
|
EXPORT uint8_t g_u8;
|
||||||
|
EXPORT uint16_t g_u16;
|
||||||
|
EXPORT uint32_t g_u32;
|
||||||
|
EXPORT uint64_t g_u64;
|
||||||
|
EXPORT float g_f;
|
||||||
|
EXPORT double g_d;
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
EXPORT double complex g_dc;
|
||||||
|
EXPORT float complex g_fc;
|
||||||
|
#endif
|
||||||
|
EXPORT bool (*g_fp)(bool);
|
||||||
|
EXPORT const char g_s[];
|
||||||
|
EXPORT const char* g_sp;
|
||||||
|
EXPORT void* g_p;
|
||||||
|
EXPORT enum e8 g_e8;
|
||||||
|
EXPORT enum e16 g_e16;
|
||||||
|
EXPORT enum e32 g_e32;
|
||||||
|
EXPORT struct Date g_date;
|
||||||
|
|
||||||
|
bool g_b = true;
|
||||||
|
int8_t g_i8 = -8;
|
||||||
|
int16_t g_i16 = -16;
|
||||||
|
int32_t g_i32 = -32;
|
||||||
|
int64_t g_i64 = -64;
|
||||||
|
uint8_t g_u8 = 8;
|
||||||
|
uint16_t g_u16 = 16;
|
||||||
|
uint32_t g_u32 = 32;
|
||||||
|
uint64_t g_u64 = 64;
|
||||||
|
float g_f = 3;
|
||||||
|
double g_d = 5;
|
||||||
|
#ifdef HAVE_COMPLEX
|
||||||
|
double complex g_dc = 7+8i;
|
||||||
|
float complex g_fc = 6+9i;
|
||||||
|
#endif
|
||||||
|
bool (*g_fp)(bool) = ¬_b;
|
||||||
|
void* g_p = (void*) ¬_b;
|
||||||
|
const char g_s[] = "g_s";
|
||||||
|
const char* g_sp = "g_sp";
|
||||||
|
enum e8 g_e8 = FOO8;
|
||||||
|
enum e16 g_e16 = FOO16;
|
||||||
|
enum e32 g_e32 = FOO32;
|
||||||
|
struct Date g_date = {1,2,3,4};
|
||||||
|
|
||||||
|
EXPORT void set_errno(int val);
|
||||||
|
EXPORT int get_errno(void);
|
||||||
|
|
||||||
|
void set_errno(int val) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetLastError(val);
|
||||||
|
#else
|
||||||
|
errno = val;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_errno(void) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return GetLastError();
|
||||||
|
#else
|
||||||
|
return errno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT int va_list_size, va_list_align;
|
||||||
|
int va_list_size = sizeof(va_list);
|
||||||
|
int va_list_align = alignof(va_list);
|
||||||
|
|
|
@ -0,0 +1,890 @@
|
||||||
|
-- vim: ts=4 sw=4 sts=4 et tw=78
|
||||||
|
-- Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
||||||
|
|
||||||
|
io.stdout:setvbuf('no')
|
||||||
|
local ffi = require 'ffi'
|
||||||
|
local dlls = {}
|
||||||
|
|
||||||
|
local num_ok = 0
|
||||||
|
local num_err = 0
|
||||||
|
local assert = function(a, hint1, hint2)
|
||||||
|
num_ok = (num_ok or 0) + (a == true and 1 or 0)
|
||||||
|
num_err = (num_err or 0) + (a ~= true and 1 or 0)
|
||||||
|
if a ~= true then
|
||||||
|
print('F'..num_err..'/T'..(num_ok+num_err), 'L'..debug.getinfo(2).currentline, debug.getinfo(2).name or '', hint1 or '', hint2 or '')
|
||||||
|
end
|
||||||
|
--return _G.assert(a)
|
||||||
|
end
|
||||||
|
|
||||||
|
dlls.__cdecl = ffi.load('test_cdecl')
|
||||||
|
|
||||||
|
if ffi.arch == 'x86' and ffi.os == 'Windows' then
|
||||||
|
dlls.__stdcall = ffi.load('test_stdcall')
|
||||||
|
dlls.__fastcall = ffi.load('test_fastcall')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check(a, b)
|
||||||
|
return assert(a == b, a, b)
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Running test')
|
||||||
|
|
||||||
|
ffi.cdef [[
|
||||||
|
enum e8 {
|
||||||
|
FOO8,
|
||||||
|
BAR8,
|
||||||
|
};
|
||||||
|
enum e16 {
|
||||||
|
FOO16 = 1 << 8,
|
||||||
|
BAR16,
|
||||||
|
BIG16 = 1 << 14,
|
||||||
|
};
|
||||||
|
enum e32 {
|
||||||
|
FOO32 = 1 << 16,
|
||||||
|
BAR32,
|
||||||
|
BIG32 = 1 << 30,
|
||||||
|
};
|
||||||
|
int max_alignment();
|
||||||
|
bool is_msvc, is_msvc2 __asm__("is_msvc");
|
||||||
|
bool have_complex(void);
|
||||||
|
bool have_complex2() __asm__("have" /*foo*/ "\x5F" "complex"); // 5F is _
|
||||||
|
|
||||||
|
int8_t add_i8(int8_t a, int8_t b);
|
||||||
|
uint8_t add_u8(uint8_t a, uint8_t b);
|
||||||
|
int16_t add_i16(int16_t a, int16_t b);
|
||||||
|
uint16_t add_i16(uint16_t a, uint16_t b);
|
||||||
|
int32_t add_i32(int32_t a, int32_t b);
|
||||||
|
uint32_t add_u32(uint32_t a, uint32_t b);
|
||||||
|
int64_t add_i64(int64_t a, int64_t b);
|
||||||
|
uint64_t add_u64(uint64_t a, uint64_t b);
|
||||||
|
double add_d(double a, double b);
|
||||||
|
float add_f(float a, float b);
|
||||||
|
double complex add_dc(double complex a, double complex b);
|
||||||
|
float complex add_fc(float complex a, float complex b);
|
||||||
|
enum e8 inc_e8(enum e8);
|
||||||
|
enum e16 inc_e16(enum e16);
|
||||||
|
enum e32 inc_e32(enum e32);
|
||||||
|
bool not_b(bool v);
|
||||||
|
_Bool not_b2(_Bool v);
|
||||||
|
typedef bool (*fp)(bool);
|
||||||
|
fp ret_fp(fp v);
|
||||||
|
bool (*ret_fp2(bool (*)(bool)))(bool) __asm("ret_fp");
|
||||||
|
|
||||||
|
int print_i8(char* buf, int8_t val);
|
||||||
|
int print_u8(char* buf, uint8_t val);
|
||||||
|
int print_i16(char* buf, int16_t val);
|
||||||
|
int print_u16(char* buf, uint16_t val);
|
||||||
|
int print_i32(char* buf, int32_t val);
|
||||||
|
int print_u32(char* buf, uint32_t val);
|
||||||
|
int print_i64(char* buf, int64_t val);
|
||||||
|
int print_u64(char* buf, uint64_t val);
|
||||||
|
int print_s(char* buf, const char* val);
|
||||||
|
int print_b(char* buf, bool val);
|
||||||
|
int print_b2(char* buf, _Bool val);
|
||||||
|
int print_d(char* buf, double val);
|
||||||
|
int print_f(char* buf, float val);
|
||||||
|
int print_p(char* buf, void* val);
|
||||||
|
int print_dc(char* buf, double complex val);
|
||||||
|
int print_fc(char* buf, float complex val);
|
||||||
|
int print_e8(char* buf, enum e8 val);
|
||||||
|
int print_e16(char* buf, enum e16 val);
|
||||||
|
int print_e32(char* buf, enum e32 val);
|
||||||
|
int sprintf(char* buf, const char* format, ...);
|
||||||
|
|
||||||
|
// Examples from MSDN
|
||||||
|
|
||||||
|
// bit_fields1.cpp
|
||||||
|
// compile with: /LD
|
||||||
|
struct Date {
|
||||||
|
unsigned short nWeekDay : 3; // 0..7 (3 bits)
|
||||||
|
unsigned short nMonthDay : 6; // 0..31 (6 bits)
|
||||||
|
unsigned short nMonth : 5; // 0..12 (5 bits)
|
||||||
|
unsigned short nYear : 8; // 0..100 (8 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
// bit_fields2.cpp
|
||||||
|
// compile with: /LD
|
||||||
|
struct Date2 {
|
||||||
|
unsigned nWeekDay : 3; // 0..7 (3 bits)
|
||||||
|
unsigned nMonthDay : 6; // 0..31 (6 bits)
|
||||||
|
unsigned : 0; // Force alignment to next boundary.
|
||||||
|
unsigned nMonth : 5; // 0..12 (5 bits)
|
||||||
|
unsigned nYear : 8; // 0..100 (8 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
// For checking the alignment of short bitfields
|
||||||
|
struct Date3 {
|
||||||
|
char pad;
|
||||||
|
unsigned short nWeekDay : 3; // 0..7 (3 bits)
|
||||||
|
unsigned short nMonthDay : 6; // 0..31 (6 bits)
|
||||||
|
unsigned short nMonth : 5; // 0..12 (5 bits)
|
||||||
|
unsigned short nYear : 8; // 0..100 (8 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
// For checking the alignment and container of int64 bitfields
|
||||||
|
struct bit64 {
|
||||||
|
char pad;
|
||||||
|
uint64_t a : 15;
|
||||||
|
uint64_t b : 14;
|
||||||
|
uint64_t c : 13;
|
||||||
|
uint64_t d : 12;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Examples from SysV X86 ABI
|
||||||
|
struct sysv1 {
|
||||||
|
int j:5;
|
||||||
|
int k:6;
|
||||||
|
int m:7;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysv2 {
|
||||||
|
short s:9;
|
||||||
|
int j:9;
|
||||||
|
char c;
|
||||||
|
short t:9;
|
||||||
|
short u:9;
|
||||||
|
char d;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysv3 {
|
||||||
|
char c;
|
||||||
|
short s:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
union sysv4 {
|
||||||
|
char c;
|
||||||
|
short s:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysv5 {
|
||||||
|
char c;
|
||||||
|
int :0;
|
||||||
|
char d;
|
||||||
|
short :9;
|
||||||
|
char e;
|
||||||
|
char :0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysv6 {
|
||||||
|
char c;
|
||||||
|
int :0;
|
||||||
|
char d;
|
||||||
|
int :9;
|
||||||
|
char e;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysv7 {
|
||||||
|
int j:9;
|
||||||
|
short s:9;
|
||||||
|
char c;
|
||||||
|
short t:9;
|
||||||
|
short u:9;
|
||||||
|
};
|
||||||
|
|
||||||
|
int print_date(size_t* sz, size_t* align, char* buf, struct Date* s);
|
||||||
|
int print_date2(size_t* sz, size_t* align, char* buf, struct Date2* s);
|
||||||
|
int print_date3(size_t* sz, size_t* align, char* buf, struct Date3* d);
|
||||||
|
int print_bit64(size_t* sz, size_t* align, char* buf, struct bit64* d);
|
||||||
|
int print_sysv1(size_t* sz, size_t* align, char* buf, struct sysv1* s);
|
||||||
|
int print_sysv2(size_t* sz, size_t* align, char* buf, struct sysv2* s);
|
||||||
|
int print_sysv3(size_t* sz, size_t* align, char* buf, struct sysv3* s);
|
||||||
|
int print_sysv4(size_t* sz, size_t* align, char* buf, union sysv4* s);
|
||||||
|
int print_sysv5(size_t* sz, size_t* align, char* buf, struct sysv5* s);
|
||||||
|
int print_sysv6(size_t* sz, size_t* align, char* buf, struct sysv6* s);
|
||||||
|
int print_sysv7(size_t* sz, size_t* align, char* buf, struct sysv7* s);
|
||||||
|
|
||||||
|
struct fptr {
|
||||||
|
int (__cdecl *p)(int);
|
||||||
|
};
|
||||||
|
int call_fptr(struct fptr* s, int val);
|
||||||
|
|
||||||
|
bool g_b;
|
||||||
|
int8_t g_i8;
|
||||||
|
int16_t g_i16;
|
||||||
|
int32_t g_i32;
|
||||||
|
int64_t g_i64;
|
||||||
|
uint8_t g_u8;
|
||||||
|
uint16_t g_u16;
|
||||||
|
uint32_t g_u32;
|
||||||
|
uint64_t g_u64;
|
||||||
|
float g_f;
|
||||||
|
double g_d;
|
||||||
|
double complex g_dc;
|
||||||
|
float complex g_fc;
|
||||||
|
bool (*g_fp)(bool);
|
||||||
|
const char g_s[];
|
||||||
|
const char* g_sp;
|
||||||
|
void* g_p;
|
||||||
|
enum e8 g_e8;
|
||||||
|
enum e16 g_e16;
|
||||||
|
enum e32 g_e32;
|
||||||
|
struct Date g_date;
|
||||||
|
|
||||||
|
void set_errno(int val);
|
||||||
|
int get_errno(void);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local align = [[
|
||||||
|
struct align_ALIGN_SUFFIX {
|
||||||
|
char pad;
|
||||||
|
TYPE v;
|
||||||
|
};
|
||||||
|
|
||||||
|
int print_align_ALIGN_SUFFIX(char* buf, struct align_ALIGN_SUFFIX* p);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local palign = [[
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(ALIGN)
|
||||||
|
]] .. align .. [[
|
||||||
|
#pragma pack(pop)
|
||||||
|
]]
|
||||||
|
|
||||||
|
local bitfields = [[
|
||||||
|
struct bcTNUM {
|
||||||
|
uintTNUM_t a : 3;
|
||||||
|
intTNUM_t b : 3;
|
||||||
|
};
|
||||||
|
struct blzTNUM {
|
||||||
|
uintTNUM_t a;
|
||||||
|
uintTNUM_t :0;
|
||||||
|
uintTNUM_t b;
|
||||||
|
};
|
||||||
|
int print_bcTNUM(size_t* sz, size_t* align, char* buf, struct bcTNUM* s);
|
||||||
|
int print_blzTNUM(size_t* sz, size_t* align, char* buf, struct blzTNUM* s);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local bitalign = [[
|
||||||
|
struct ba_TNUM_BNUM {
|
||||||
|
char a;
|
||||||
|
uintTNUM_t b : BNUM;
|
||||||
|
};
|
||||||
|
struct bu_TNUM_BNUM {
|
||||||
|
char a;
|
||||||
|
uintTNUM_t :BNUM;
|
||||||
|
char b;
|
||||||
|
};
|
||||||
|
int print_ba_TNUM_BNUM(size_t* sz, size_t* align, char* buf, struct ba_TNUM_BNUM* s);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local bitzero = [[
|
||||||
|
struct bz_TNUM_ZNUM_BNUM {
|
||||||
|
uint8_t a;
|
||||||
|
uintTNUM_t b : 3;
|
||||||
|
uintZNUM_t :BNUM;
|
||||||
|
uintTNUM_t c : 3;
|
||||||
|
};
|
||||||
|
int print_bz_TNUM_ZNUM_BNUM(size_t* sz, size_t* align, char* buf, struct bz_TNUM_ZNUM_BNUM* s);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local i = ffi.C.i
|
||||||
|
local test_values = {
|
||||||
|
['void*'] = ffi.new('char[3]'),
|
||||||
|
['const char*'] = 'foo',
|
||||||
|
float = 3.4,
|
||||||
|
double = 5.6,
|
||||||
|
uint16_t = 65000,
|
||||||
|
uint32_t = ffi.new('uint32_t', 700000056),
|
||||||
|
uint64_t = 12345678901234,
|
||||||
|
bool = true,
|
||||||
|
_Bool = false,
|
||||||
|
['float complex'] = 3+4*i,
|
||||||
|
['double complex'] = 5+6*i,
|
||||||
|
['enum e8'] = ffi.C.FOO8,
|
||||||
|
['enum e16'] = ffi.C.FOO16,
|
||||||
|
['enum e32'] = ffi.C.FOO32,
|
||||||
|
}
|
||||||
|
|
||||||
|
local types = {
|
||||||
|
b = 'bool',
|
||||||
|
b2 = '_Bool',
|
||||||
|
d = 'double',
|
||||||
|
f = 'float',
|
||||||
|
u64 = 'uint64_t',
|
||||||
|
u32 = 'uint32_t',
|
||||||
|
u16 = 'uint16_t',
|
||||||
|
s = 'const char*',
|
||||||
|
p = 'void*',
|
||||||
|
e8 = 'enum e8',
|
||||||
|
e16 = 'enum e16',
|
||||||
|
e32 = 'enum e32',
|
||||||
|
}
|
||||||
|
|
||||||
|
local buf = ffi.new('char[256]')
|
||||||
|
|
||||||
|
local function checkbuf(kind, ret)
|
||||||
|
local str = tostring(test_values[kind]):gsub('^cdata%b<>: ', '')
|
||||||
|
|
||||||
|
if type(test_values[kind])=='number' and tonumber(str) % 1 == 0 then -- if not decimal place...
|
||||||
|
str = tostring(math.floor(tonumber(str)))
|
||||||
|
end
|
||||||
|
|
||||||
|
check(ffi.string(buf), str)
|
||||||
|
check(ret, #str)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkalign(kind, v, ret)
|
||||||
|
--print(v)
|
||||||
|
local str = tostring(test_values[kind]):gsub('^cdata%b<>: ', '')
|
||||||
|
|
||||||
|
if type(test_values[kind])=='number' and tonumber(str) % 1 == 0 then -- if not decimal place...
|
||||||
|
str = tostring(math.floor(tonumber(str)))
|
||||||
|
end
|
||||||
|
|
||||||
|
check(ffi.string(buf), ('size %d offset %d align %d value %s'):format(ffi.sizeof(v), ffi.offsetof(v, 'v'), ffi.alignof(v, 'v'), str))
|
||||||
|
check(ret, #str)
|
||||||
|
end
|
||||||
|
|
||||||
|
local u64 = ffi.typeof('uint64_t')
|
||||||
|
local i64 = ffi.typeof('int64_t')
|
||||||
|
|
||||||
|
local first = true
|
||||||
|
|
||||||
|
for convention,c in pairs(dlls) do
|
||||||
|
check(c.add_i8(1,1), 2)
|
||||||
|
check(c.add_i8(256,1), 1)
|
||||||
|
check(c.add_i8(127,1), -128)
|
||||||
|
check(c.add_i8(-120,120), 0)
|
||||||
|
check(c.add_u8(255,1), 0)
|
||||||
|
check(c.add_u8(120,120), 240)
|
||||||
|
check(c.add_i16(2000,4000), 6000)
|
||||||
|
check(c.add_d(20, 12), 32)
|
||||||
|
check(c.add_f(40, 32), 72)
|
||||||
|
check(c.not_b(true), false)
|
||||||
|
check(c.not_b2(false), true)
|
||||||
|
check(c.inc_e8(c.FOO8), c.BAR8)
|
||||||
|
check(c.inc_e8('FOO8'), c.BAR8)
|
||||||
|
check(c.inc_e16(c.FOO16), c.BAR16)
|
||||||
|
check(c.inc_e32(c.FOO32), c.BAR32)
|
||||||
|
check(c.ret_fp(c.g_fp), c.g_fp)
|
||||||
|
check(c.ret_fp2(c.g_fp), c.g_fp)
|
||||||
|
|
||||||
|
if c.have_complex() then
|
||||||
|
check(c.add_dc(3+4*i, 4+5*i), 7+9*i)
|
||||||
|
check(c.add_fc(2+4*i, 6+8*i), 8+12*i)
|
||||||
|
types.dc = 'double complex'
|
||||||
|
types.fc = 'float complex'
|
||||||
|
else
|
||||||
|
types.dc = nil
|
||||||
|
types.fc = nil
|
||||||
|
end
|
||||||
|
check((3+4*i).re, 3)
|
||||||
|
check((3+4*i).im, 4)
|
||||||
|
check(ffi.new('complex float', 2+8*i).re, 2)
|
||||||
|
check(ffi.new('complex float', 5+6*i).im, 6)
|
||||||
|
|
||||||
|
check(c.have_complex(), c.have_complex2())
|
||||||
|
check(c.is_msvc, c.is_msvc2)
|
||||||
|
|
||||||
|
check(c.g_b, true)
|
||||||
|
check(c.g_i8, -8)
|
||||||
|
check(c.g_i16, -16)
|
||||||
|
check(c.g_i32, -32)
|
||||||
|
check(c.g_i64, i64(-64))
|
||||||
|
check(c.g_u8, 8)
|
||||||
|
check(c.g_u16, 16)
|
||||||
|
check(c.g_u32, 32)
|
||||||
|
check(c.g_u64, u64(64))
|
||||||
|
check(c.g_f, 3)
|
||||||
|
check(c.g_d, 5)
|
||||||
|
if c.have_complex() then
|
||||||
|
check(c.g_dc, 7 + 8*i)
|
||||||
|
check(c.g_fc, 6 + 9*i)
|
||||||
|
end
|
||||||
|
check(ffi.cast('void*', c.g_fp), c.g_p)
|
||||||
|
check(c.g_s, 'g_s')
|
||||||
|
check(c.g_sp, 'g_sp')
|
||||||
|
check(c.g_e8, c.FOO8)
|
||||||
|
check(c.g_e16, c.FOO16)
|
||||||
|
check(c.g_e32, c.FOO32)
|
||||||
|
check(c.g_date.nWeekDay, 1)
|
||||||
|
check(c.g_date.nMonthDay, 2)
|
||||||
|
check(c.g_date.nMonth, 3)
|
||||||
|
check(c.g_date.nYear, 4)
|
||||||
|
|
||||||
|
c.g_b = false; check(c.g_b, false)
|
||||||
|
c.g_i8 = -108; check(c.g_i8, -108)
|
||||||
|
c.g_i16 = -1016; check(c.g_i16, -1016)
|
||||||
|
c.g_i32 = -1032; check(c.g_i32, -1032)
|
||||||
|
c.g_i64 = -1064; check(c.g_i64, i64(-1064))
|
||||||
|
c.g_u8 = 208; check(c.g_u8, 208)
|
||||||
|
c.g_u16 = 2016; check(c.g_u16, 2016)
|
||||||
|
c.g_u32 = 2032; check(c.g_u32, 2032)
|
||||||
|
c.g_u64 = 2064; check(c.g_u64, u64(2064))
|
||||||
|
c.g_f = 13; check(c.g_f, 13)
|
||||||
|
c.g_d = 15; check(c.g_d, 15)
|
||||||
|
if c.have_complex() then
|
||||||
|
c.g_dc = 17+18*i; check(c.g_dc, 17+18*i)
|
||||||
|
c.g_fc = 16+19*i; check(c.g_fc, 16+19*i)
|
||||||
|
end
|
||||||
|
c.g_sp = 'foo'; check(c.g_sp, 'foo')
|
||||||
|
c.g_e8 = c.BAR8; check(c.g_e8, c.BAR8)
|
||||||
|
c.g_e16 = c.BAR16; check(c.g_e16, c.BAR16)
|
||||||
|
c.g_e32 = c.BAR32; check(c.g_e32, c.BAR32)
|
||||||
|
c.g_date.nWeekDay = 3; check(c.g_date.nWeekDay, 3)
|
||||||
|
|
||||||
|
local align_attr = c.is_msvc and [[
|
||||||
|
struct align_attr_ALIGN_SUFFIX {
|
||||||
|
char pad;
|
||||||
|
__declspec(align(ALIGN)) TYPE v;
|
||||||
|
};
|
||||||
|
|
||||||
|
int print_align_attr_ALIGN_SUFFIX(char* buf, struct align_attr_ALIGN_SUFFIX* p);
|
||||||
|
]] or [[
|
||||||
|
struct align_attr_ALIGN_SUFFIX {
|
||||||
|
char pad;
|
||||||
|
TYPE v __attribute__(aligned(ALIGN));
|
||||||
|
};
|
||||||
|
|
||||||
|
int print_align_attr_ALIGN_SUFFIX(char* buf, struct align_attr_ALIGN_SUFFIX* p);
|
||||||
|
]]
|
||||||
|
|
||||||
|
for suffix, type in pairs(types) do
|
||||||
|
local test = test_values[type]
|
||||||
|
--print('checkbuf', suffix, type, buf, test)
|
||||||
|
checkbuf(type, c['print_' .. suffix](buf, test))
|
||||||
|
|
||||||
|
if first then
|
||||||
|
ffi.cdef(align:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = ffi.new('struct align_0_' .. suffix, {0, test})
|
||||||
|
checkalign(type, v, c['print_align_0_' .. suffix](buf, v))
|
||||||
|
|
||||||
|
for _,align in ipairs{1,2,4,8,16} do
|
||||||
|
if align > c.max_alignment() then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if first then
|
||||||
|
ffi.cdef(palign:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', align))
|
||||||
|
ffi.cdef(align_attr:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', align))
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = ffi.new('struct align_' .. align .. '_' .. suffix, {0, test})
|
||||||
|
checkalign(type, v, c['print_align_' .. align .. '_' .. suffix](buf, v))
|
||||||
|
|
||||||
|
-- MSVC doesn't support aligned attributes on enums
|
||||||
|
if not type:match('^enum e[0-9]*$') or not c.is_msvc then
|
||||||
|
local v2 = ffi.new('struct align_attr_' .. align .. '_' .. suffix, {0, test})
|
||||||
|
checkalign(type, v2, c['print_align_attr_' .. align .. '_' .. suffix](buf, v2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not c.is_msvc then
|
||||||
|
if first then
|
||||||
|
local h = [[
|
||||||
|
struct align_attr_def_SUFFIX {
|
||||||
|
char pad;
|
||||||
|
TYPE v __attribute__(aligned);
|
||||||
|
};
|
||||||
|
int print_align_attr_def_SUFFIX(char* buf, struct align_attr_def_SUFFIX* p);
|
||||||
|
]]
|
||||||
|
ffi.cdef(h:gsub('SUFFIX', suffix):gsub('TYPE', type))
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = ffi.new('struct align_attr_def_' .. suffix, {0, test})
|
||||||
|
checkalign(type, v, c['print_align_attr_def_' .. suffix](buf, v))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local psz = ffi.new('size_t[1]')
|
||||||
|
local palign = ffi.new('size_t[1]')
|
||||||
|
local function check_align(type, test, ret)
|
||||||
|
--print('check_align', type, test, ret, ffi.string(buf), psz[0], palign[0])
|
||||||
|
check(tonumber(palign[0]), ffi.alignof(type))
|
||||||
|
check(tonumber(psz[0]), ffi.sizeof(type))
|
||||||
|
check(ret, #test)
|
||||||
|
check(test, ffi.string(buf))
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, tnum in ipairs{8, 16, 32, 64} do
|
||||||
|
if first then
|
||||||
|
ffi.cdef(bitfields:gsub('TNUM',tnum))
|
||||||
|
end
|
||||||
|
|
||||||
|
check_align('struct bc'..tnum, '1 2', c['print_bc'..tnum](psz, palign, buf, {1,2}))
|
||||||
|
check_align('struct blz'..tnum, '1 2', c['print_blz'..tnum](psz, palign, buf, {1,2}))
|
||||||
|
|
||||||
|
for _, znum in ipairs{8, 16, 32, 64} do
|
||||||
|
for _, bnum in ipairs{7, 15, 31, 63} do
|
||||||
|
if bnum > znum then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if first then
|
||||||
|
ffi.cdef(bitzero:gsub('TNUM',tnum):gsub('ZNUM',znum):gsub('BNUM', bnum))
|
||||||
|
end
|
||||||
|
check_align('struct bz_'..tnum..'_'..znum..'_'..bnum, '1 2 3', c['print_bz_'..tnum..'_'..znum..'_'..bnum](psz, palign, buf, {1,2,3}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, bnum in ipairs{7, 15, 31, 63} do
|
||||||
|
if bnum > tnum then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if first then
|
||||||
|
ffi.cdef(bitalign:gsub('TNUM',tnum):gsub('BNUM',bnum))
|
||||||
|
end
|
||||||
|
check_align('struct ba_'..tnum..'_'..bnum, '1 2', c['print_ba_'..tnum..'_'..bnum](psz, palign, buf, {1,2}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
check_align('struct Date', '1 2 3 4', c.print_date(psz, palign, buf, {1,2,3,4}))
|
||||||
|
check_align('struct Date2', '1 2 3 4', c.print_date2(psz, palign, buf, {1,2,3,4}))
|
||||||
|
check_align('struct sysv1', '1 2 3', c.print_sysv1(psz, palign, buf, {1,2,3}))
|
||||||
|
check_align('struct sysv2', '1 2 3 4 5 6', c.print_sysv2(psz, palign, buf, {1,2,3,4,5,6}))
|
||||||
|
check_align('struct sysv3', '1 2', c.print_sysv3(psz, palign, buf, {1,2}))
|
||||||
|
check_align('union sysv4', '1', c.print_sysv4(psz, palign, buf, {1}))
|
||||||
|
check_align('struct sysv5', '1 2 3', c.print_sysv5(psz, palign, buf, {1,2,3}))
|
||||||
|
check_align('struct sysv6', '1 2 3', c.print_sysv6(psz, palign, buf, {1,2,3}))
|
||||||
|
check_align('struct sysv7', '1 2 3 4 5', c.print_sysv7(psz, palign, buf, {1,2,3,4,5}))
|
||||||
|
|
||||||
|
local cbs = [[
|
||||||
|
typedef const char* (*__cdecl sfunc)(const char*);
|
||||||
|
int call_i(int (*__cdecl func)(int), int arg);
|
||||||
|
float call_f(float (*__cdecl func)(float), float arg);
|
||||||
|
double call_d(double (*__cdecl func)(double), double arg);
|
||||||
|
const char* call_s(sfunc func, const char* arg);
|
||||||
|
_Bool call_b(_Bool (*__cdecl func)(_Bool), _Bool arg);
|
||||||
|
double complex call_dc(double complex (*__cdecl func)(double complex), double complex arg);
|
||||||
|
float complex call_fc(float complex (*__cdecl func)(float complex), float complex arg);
|
||||||
|
enum e8 call_e8(enum e8 (*__cdecl func)(enum e8), enum e8 arg);
|
||||||
|
enum e16 call_e16(enum e16 (*__cdecl func)(enum e16), enum e16 arg);
|
||||||
|
enum e32 call_e32(enum e32 (*__cdecl func)(enum e32), enum e32 arg);
|
||||||
|
]]
|
||||||
|
|
||||||
|
ffi.cdef(cbs:gsub('__cdecl', convention))
|
||||||
|
|
||||||
|
local u3 = ffi.new('uint64_t', 3)
|
||||||
|
check(c.call_i(function(a) return 2*a end, 3), 6)
|
||||||
|
assert(math.abs(c.call_d(function(a) return 2*a end, 3.2) - 6.4) < 0.0000000001)
|
||||||
|
assert(math.abs(c.call_f(function(a) return 2*a end, 3.2) - 6.4) < 0.000001)
|
||||||
|
check(ffi.string(c.call_s(function(s) return s + u3 end, 'foobar')), 'bar')
|
||||||
|
check(c.call_b(function(v) return not v end, true), false)
|
||||||
|
check(c.call_e8(function(v) return v + 1 end, c.FOO8), c.BAR8)
|
||||||
|
check(c.call_e16(function(v) return v + 1 end, c.FOO16), c.BAR16)
|
||||||
|
check(c.call_e32(function(v) return v + 1 end, c.FOO32), c.BAR32)
|
||||||
|
|
||||||
|
if c.have_complex() then
|
||||||
|
check(c.call_dc(function(v) return v + 2+3*i end, 4+6*i), 6+9*i)
|
||||||
|
check(c.call_fc(function(v) return v + 1+2*i end, 7+4*i), 8+6*i)
|
||||||
|
end
|
||||||
|
|
||||||
|
local u2 = ffi.new('uint64_t', 2)
|
||||||
|
local cb = ffi.new('sfunc', function(s) return s + u3 end)
|
||||||
|
check(ffi.string(cb('foobar')), 'bar')
|
||||||
|
check(ffi.string(c.call_s(cb, 'foobar')), 'bar')
|
||||||
|
cb:set(function(s) return s + u2 end)
|
||||||
|
check(ffi.string(c.call_s(cb, 'foobar')), 'obar')
|
||||||
|
|
||||||
|
local fp = ffi.new('struct fptr')
|
||||||
|
assert(fp.p == ffi.C.NULL)
|
||||||
|
fp.p = function(a) return 2*a end
|
||||||
|
assert(fp.p ~= ffi.C.NULL)
|
||||||
|
check(c.call_fptr(fp, 4), 8)
|
||||||
|
local suc, err = pcall(function() fp.p:set(function() end) end)
|
||||||
|
assert(not suc)
|
||||||
|
check(err:gsub('^.*: ',''), "can't set the function for a non-lua callback")
|
||||||
|
|
||||||
|
check(c.call_fptr({function(a) return 3*a end}, 5), 15)
|
||||||
|
|
||||||
|
local suc, err = pcall(c.call_s, function(s) error(ffi.string(s), 0) end, 'my error')
|
||||||
|
check(suc, false)
|
||||||
|
check(err, 'my error')
|
||||||
|
|
||||||
|
check(ffi.errno(), c.get_errno())
|
||||||
|
c.set_errno(3)
|
||||||
|
check(ffi.errno(), 3)
|
||||||
|
check(c.get_errno(), 3)
|
||||||
|
check(ffi.errno(4), 3)
|
||||||
|
check(ffi.errno(), 4)
|
||||||
|
check(c.get_errno(), 4)
|
||||||
|
|
||||||
|
local gccattr = {
|
||||||
|
__cdecl = 'int test_pow(int v) __attribute__((cdecl));',
|
||||||
|
__stdcall = 'int test_pow(int v) __attribute__(stdcall);',
|
||||||
|
__fastcall = '__attribute__(fastcall) int test_pow(int v);',
|
||||||
|
}
|
||||||
|
|
||||||
|
ffi.cdef(gccattr[convention])
|
||||||
|
check(c.test_pow(5), 25)
|
||||||
|
|
||||||
|
ffi.cdef [[
|
||||||
|
int va_list_size, va_list_align;
|
||||||
|
int vsnprintf(char* buf, size_t sz, const char* fmt, va_list ap);
|
||||||
|
]]
|
||||||
|
ffi.new('va_list')
|
||||||
|
assert(ffi.debug().functions.vsnprintf ~= nil)
|
||||||
|
assert(ffi.istype('va_list', ffi.new('__builtin_va_list')))
|
||||||
|
assert(ffi.istype('va_list', ffi.new('__gnuc_va_list')))
|
||||||
|
check(ffi.sizeof('va_list'), c.va_list_size)
|
||||||
|
check(ffi.alignof('va_list'), c.va_list_align)
|
||||||
|
|
||||||
|
first = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local c = ffi.C
|
||||||
|
|
||||||
|
if ffi.os ~= 'Windows' then
|
||||||
|
assert(c.sprintf(buf, "%g", 5.3) == 3 and ffi.string(buf) == '5.3')
|
||||||
|
assert(c.sprintf(buf, "%d", false) == 1 and ffi.string(buf) == '0')
|
||||||
|
assert(c.sprintf(buf, "%d%g", false, 6.7) == 4 and ffi.string(buf) == '06.7')
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(ffi.sizeof('uint32_t[?]', 32) == 32 * 4)
|
||||||
|
assert(ffi.sizeof(ffi.new('uint32_t[?]', 32)) == 32 * 4)
|
||||||
|
|
||||||
|
ffi.cdef [[
|
||||||
|
struct vls {
|
||||||
|
struct {
|
||||||
|
char a;
|
||||||
|
struct {
|
||||||
|
char b;
|
||||||
|
char v[?];
|
||||||
|
} c;
|
||||||
|
} d;
|
||||||
|
};
|
||||||
|
struct vls2 {
|
||||||
|
char pad;
|
||||||
|
union {
|
||||||
|
uint8_t a;
|
||||||
|
uint16_t b;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
]]
|
||||||
|
|
||||||
|
assert(ffi.sizeof('struct vls', 3) == 5)
|
||||||
|
assert(ffi.sizeof(ffi.new('struct vls', 4).d.c) == 5)
|
||||||
|
assert(ffi.offsetof('struct vls2', 'a') == 2)
|
||||||
|
assert(ffi.sizeof('struct vls2') == 4)
|
||||||
|
|
||||||
|
ffi.cdef [[ static const int DUMMY = 8 << 2; ]]
|
||||||
|
assert(ffi.C.DUMMY == 32)
|
||||||
|
|
||||||
|
ffi.new('struct {const char* foo;}', {'foo'})
|
||||||
|
|
||||||
|
assert(not pcall(function()
|
||||||
|
ffi.new('struct {char* foo;}', {'ff'})
|
||||||
|
end))
|
||||||
|
|
||||||
|
local mt = {}
|
||||||
|
local vls = ffi.new(ffi.metatype('struct vls', mt), 1)
|
||||||
|
|
||||||
|
assert(not pcall(function() return vls.key end))
|
||||||
|
|
||||||
|
mt.__index = function(vls, key)
|
||||||
|
return function(vls, a, b)
|
||||||
|
return 'in index ' .. key .. ' ' .. vls.d.a .. ' ' .. a .. ' ' .. b
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vls.d.a = 3
|
||||||
|
check(vls:key('a', 'b'), 'in index key 3.0 a b')
|
||||||
|
|
||||||
|
assert(not pcall(function() vls.k = 3 end))
|
||||||
|
|
||||||
|
mt.__newindex = function(vls, key, val)
|
||||||
|
error('in newindex ' .. key .. ' ' .. vls.d.a .. ' ' .. val, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
vls.d.a = 4
|
||||||
|
local suc, err = pcall(function() vls.key = 'val' end)
|
||||||
|
assert(not suc)
|
||||||
|
check(err, 'in newindex key 4.0 val')
|
||||||
|
|
||||||
|
mt.__add = function(vls, a) return vls.d.a + a end
|
||||||
|
mt.__sub = function(vls, a) return vls.d.a - a end
|
||||||
|
mt.__mul = function(vls, a) return vls.d.a * a end
|
||||||
|
mt.__div = function(vls, a) return vls.d.a / a end
|
||||||
|
mt.__mod = function(vls, a) return vls.d.a % a end
|
||||||
|
mt.__pow = function(vls, a) return vls.d.a ^ a end
|
||||||
|
mt.__eq = function(vls, a) return u64(vls.d.a) == a end
|
||||||
|
mt.__lt = function(vls, a) return u64(vls.d.a) < a end
|
||||||
|
mt.__le = function(vls, a) return u64(vls.d.a) <= a end
|
||||||
|
mt.__call = function(vls, a, b) return '__call', vls.d.a .. a .. (b or 'nil') end
|
||||||
|
mt.__unm = function(vls) return -vls.d.a end
|
||||||
|
mt.__concat = function(vls, a) return vls.d.a .. a end
|
||||||
|
mt.__len = function(vls) return vls.d.a end
|
||||||
|
mt.__tostring = function(vls) return 'string ' .. vls.d.a end
|
||||||
|
|
||||||
|
vls.d.a = 5
|
||||||
|
check(vls + 5, 10)
|
||||||
|
check(vls - 5, 0)
|
||||||
|
check(vls * 5, 25)
|
||||||
|
check(vls / 5, 1)
|
||||||
|
check(vls % 3, 2)
|
||||||
|
check(vls ^ 3, 125)
|
||||||
|
check(vls == u64(4), false)
|
||||||
|
check(vls == u64(5), true)
|
||||||
|
check(vls == u64(6), false)
|
||||||
|
check(vls < u64(4), false)
|
||||||
|
check(vls < u64(5), false)
|
||||||
|
check(vls < u64(6), true)
|
||||||
|
check(vls <= u64(4), false)
|
||||||
|
check(vls <= u64(5), true)
|
||||||
|
check(vls <= u64(6), true)
|
||||||
|
check(-vls, -5)
|
||||||
|
local a,b = vls('6')
|
||||||
|
check(a, '__call')
|
||||||
|
check(b, '5.06nil')
|
||||||
|
check(tostring(vls), 'string 5.0')
|
||||||
|
|
||||||
|
if _VERSION ~= 'Lua 5.1' then
|
||||||
|
check(vls .. 'str', '5.0str')
|
||||||
|
check(#vls, 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
check(tostring(1+3*i), '1.0+3.0i')
|
||||||
|
check(tostring((1+3*i)*(2+4*i)), '-10.0+10.0i')
|
||||||
|
check(tostring((3+2*i)*(3-2*i)), '13.0')
|
||||||
|
|
||||||
|
-- Should ignore unknown attributes
|
||||||
|
ffi.cdef [[
|
||||||
|
typedef int ALenum;
|
||||||
|
__attribute__((dllimport)) void __attribute__((__cdecl__)) alEnable( ALenum capability );
|
||||||
|
]]
|
||||||
|
|
||||||
|
check(ffi.sizeof('struct {char foo[alignof(uint64_t)];}'), ffi.alignof('uint64_t'))
|
||||||
|
|
||||||
|
-- Long double is not supported yet but it should be parsed
|
||||||
|
ffi.cdef('long double foo(long double val);')
|
||||||
|
check(tostring(ffi.debug().functions.foo):match('ctype(%b<>)'), '<long double (*)(long double)>')
|
||||||
|
|
||||||
|
ffi.cdef [[
|
||||||
|
typedef int byte1 __attribute__(mode(QI));
|
||||||
|
typedef int byte2 __attribute__(mode(HI));
|
||||||
|
typedef int byte4 __attribute__(mode(SI));
|
||||||
|
typedef int byte8 __attribute__(mode(DI));
|
||||||
|
typedef unsigned ubyte8 __attribute__(mode(DI));
|
||||||
|
typedef int word __attribute__(mode(word));
|
||||||
|
typedef int pointer __attribute__(mode(pointer));
|
||||||
|
typedef int byte __attribute__(mode(byte));
|
||||||
|
typedef float float4 __attribute__(mode(SF));
|
||||||
|
typedef float float8 __attribute__(mode(DF));
|
||||||
|
]]
|
||||||
|
assert(ffi.istype('int8_t', ffi.new('byte1')))
|
||||||
|
assert(ffi.istype('int16_t', ffi.new('byte2')))
|
||||||
|
assert(ffi.istype('int32_t', ffi.new('byte4')))
|
||||||
|
assert(ffi.istype('int64_t', ffi.new('byte8')))
|
||||||
|
assert(ffi.istype('uint64_t', ffi.new('ubyte8')))
|
||||||
|
check(ffi.sizeof('void*'), ffi.sizeof('pointer'))
|
||||||
|
check(ffi.alignof('void*'), ffi.alignof('pointer'))
|
||||||
|
check(ffi.sizeof('void*'), ffi.sizeof('word'))
|
||||||
|
check(ffi.alignof('void*'), ffi.alignof('word'))
|
||||||
|
assert(ffi.istype('int8_t', ffi.new('byte')))
|
||||||
|
assert(ffi.istype('float', ffi.new('float4')))
|
||||||
|
assert(ffi.istype('double', ffi.new('float8')))
|
||||||
|
|
||||||
|
ffi.cdef('void register_foo(register int val);')
|
||||||
|
check(tostring(ffi.debug().functions.register_foo):match('%b<>'), '<void (*)(int)>')
|
||||||
|
|
||||||
|
ffi.cdef [[
|
||||||
|
typedef struct __sFILE FILE;
|
||||||
|
FILE *fopen(const char * , const char * ) __asm("_" "fopen" );
|
||||||
|
]]
|
||||||
|
|
||||||
|
assert(not ffi.istype('int', ffi.new('int*')))
|
||||||
|
assert(not ffi.istype('int[]', ffi.new('int*')))
|
||||||
|
assert(not ffi.istype('int[3]', ffi.new('int*')))
|
||||||
|
assert(not ffi.istype('int[3]', ffi.new('int[2]')))
|
||||||
|
assert(ffi.istype('const int[3]', ffi.new('const int[3]')))
|
||||||
|
assert(ffi.istype('int[3]', ffi.new('const int[3]')))
|
||||||
|
|
||||||
|
-- Crazy function pointer that takes an int and a function pointer and returns
|
||||||
|
-- a function pointer. Type of &signal.
|
||||||
|
check(tostring(ffi.typeof('void (*foo(int, void(*)(int)))(int)')):match('%b<>'), '<void (*(*)(int, void (*)(int)))(int)>')
|
||||||
|
|
||||||
|
-- Make sure we pass all arguments to tonumber
|
||||||
|
check(tonumber('FE', 16), 0xFE)
|
||||||
|
|
||||||
|
-- Allow casts from pointer to numeric types
|
||||||
|
ffi.cast('long', ffi.C.NULL)
|
||||||
|
ffi.cast('int8_t', ffi.C.NULL)
|
||||||
|
assert(not pcall(function() ffi.new('long', ffi.C.NULL) end))
|
||||||
|
|
||||||
|
-- ffi.new and ffi.cast allow unpacked struct/arrays
|
||||||
|
assert(ffi.new('int[3]', 1)[0] == 1)
|
||||||
|
assert(ffi.new('int[3]', {1})[0] == 1)
|
||||||
|
assert(ffi.new('int[3]', 1, 2)[1] == 2)
|
||||||
|
assert(ffi.new('int[3]', {1, 2})[1] == 2)
|
||||||
|
|
||||||
|
ffi.cdef[[
|
||||||
|
struct var {
|
||||||
|
char ch[?];
|
||||||
|
};
|
||||||
|
]]
|
||||||
|
local d = ffi.new('char[4]')
|
||||||
|
local v = ffi.cast('struct var*', d)
|
||||||
|
v.ch = {1,2,3,4}
|
||||||
|
assert(v.ch[3] == 4)
|
||||||
|
v.ch = "bar"
|
||||||
|
assert(v.ch[3] == 0)
|
||||||
|
assert(v.ch[2] == string.byte('r'))
|
||||||
|
assert(d[1] == string.byte('a'))
|
||||||
|
|
||||||
|
ffi.cast('char*', 1)
|
||||||
|
|
||||||
|
-- 2 arg form of ffi.copy
|
||||||
|
ffi.copy(d, 'bar')
|
||||||
|
|
||||||
|
-- unsigned should be ignored for pointer rules
|
||||||
|
ffi.cdef[[
|
||||||
|
int strncmp(const signed char *s1, const unsigned char *s2, size_t n);
|
||||||
|
]]
|
||||||
|
assert(ffi.C.strncmp("two", "three", 3) ~= 0)
|
||||||
|
|
||||||
|
ffi.fill(d, 3, 1)
|
||||||
|
assert(d[2] == 1)
|
||||||
|
ffi.fill(d, 3)
|
||||||
|
assert(d[2] == 0)
|
||||||
|
|
||||||
|
-- tests for __new
|
||||||
|
ffi.cdef[[
|
||||||
|
struct newtest {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
};
|
||||||
|
]]
|
||||||
|
|
||||||
|
local tp = ffi.metatype("struct newtest", {__new =
|
||||||
|
function(tp, x, y, z)
|
||||||
|
tp = ffi.new(tp)
|
||||||
|
tp.a, tp.b, tp.c = x, y, z
|
||||||
|
return tp
|
||||||
|
end})
|
||||||
|
local v = tp(1, 2, 3)
|
||||||
|
assert(v.a == 1 and v.b == 2 and v.c == 3)
|
||||||
|
|
||||||
|
local tp = ffi.metatype("struct newtest", {__new =
|
||||||
|
function(tp, x, y, z)
|
||||||
|
tp = ffi.new(tp, {a = x, b = y, c = z})
|
||||||
|
return tp
|
||||||
|
end})
|
||||||
|
local v = tp(1, 2, 3)
|
||||||
|
assert(v.a == 1 and v.b == 2 and v.c == 3)
|
||||||
|
|
||||||
|
-- tests for __pairs and __ipairs; not iterating just testing what is returned
|
||||||
|
local tp = ffi.metatype("struct newtest",
|
||||||
|
{__pairs = function(tp) return tp.a, tp.b end, __ipairs = function(tp) return tp.b, tp.c end}
|
||||||
|
)
|
||||||
|
local v = tp(1, 2, 3)
|
||||||
|
x, y = pairs(v)
|
||||||
|
assert(x == 1 and y == 2)
|
||||||
|
x, y = ipairs(v)
|
||||||
|
assert(x == 2 and y == 3)
|
||||||
|
|
||||||
|
-- test for pointer to struct having same metamethods
|
||||||
|
local st = ffi.cdef "struct ptest {int a, b;};"
|
||||||
|
local tp = ffi.metatype("struct ptest", {__index = function(s, k) return k end, __len = function(s) return 3 end})
|
||||||
|
|
||||||
|
local a = tp(1, 2)
|
||||||
|
assert(a.banana == "banana")
|
||||||
|
assert(#a == 3)
|
||||||
|
local b = ffi.new("int[2]")
|
||||||
|
local c = ffi.cast("struct ptest *", b)
|
||||||
|
assert(c.banana == "banana") -- should have same methods
|
||||||
|
assert(#c == 3)
|
||||||
|
|
||||||
|
|
||||||
|
print('Done: '..num_ok..'/'..(num_ok+num_err)..' passed')
|
Loading…
Reference in New Issue