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