tools: luaffi

main
Dominik Madarász 2024-04-12 21:19:31 +02:00
parent 05f23f2ca6
commit 14a234f3ed
21 changed files with 17600 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

280
tools/luaffi/call.c 100644
View File

@ -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");
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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
------------------------------------------------------------------------------

View File

@ -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

View File

@ -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 */

View File

@ -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")

View File

@ -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

3334
tools/luaffi/ffi.c 100644

File diff suppressed because it is too large Load Diff

450
tools/luaffi/ffi.h 100644
View File

@ -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);

2552
tools/luaffi/parser.c 100644

File diff suppressed because it is too large Load Diff

677
tools/luaffi/test.c 100644
View File

@ -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) = &not_b;
void* g_p = (void*) &not_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);

View File

@ -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')