12517 lines
373 KiB
C
12517 lines
373 KiB
C
#ifndef LUAFFI_H
|
|
#define LUAFFI_H
|
|
|
|
/* 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 LUAFFI_EXPORT __declspec(dllexport)
|
|
#elif defined __GNUC__
|
|
# define LUAFFI_EXPORT __attribute__((visibility("default")))
|
|
#else
|
|
# define LUAFFI_EXPORT
|
|
#endif
|
|
|
|
EXTERN_C LUAFFI_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;
|
|
struct 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 LUAFFI_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);
|
|
|
|
|
|
|
|
|
|
/*
|
|
** 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 */
|
|
|
|
#endif
|
|
|
|
#ifdef LUAFFI_C
|
|
|
|
static cfunction compile(Dst_DECL, lua_State* L, cfunction func, int ref);
|
|
|
|
#if defined __arm__ || defined __arm || defined __ARM__ || defined __ARM || defined ARM || defined _ARM_ || defined ARMV4I || defined _M_ARM
|
|
/*
|
|
** 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
|
|
|
|
#else
|
|
/*
|
|
** 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
|
|
|
|
#endif
|
|
|
|
#if defined __arm__ || defined __arm || defined __ARM__ || defined __ARM || defined ARM || defined _ARM_ || defined ARMV4I || defined _M_ARM
|
|
/*
|
|
** This file has been pre-processed with DynASM.
|
|
** http://luajit.org/dynasm.html
|
|
** DynASM version 1.3.0, DynASM arm version 1.3.0
|
|
** DO NOT EDIT! The original file is in "call_arm.dasc".
|
|
*/
|
|
|
|
#if DASM_VERSION != 10300
|
|
#error "Version mismatch between DynASM and included encoding engine"
|
|
#endif
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
static const unsigned int build_actionlist[571] = {
|
|
0xe1a0c00d,
|
|
0xe92d000f,
|
|
0xe92d50f0,
|
|
0xe24c6010,
|
|
0xe59f5008,
|
|
0xe59f2008,
|
|
0xe59f1008,
|
|
0xea000000,
|
|
0x00050001,
|
|
0x00090000,
|
|
0x00090000,
|
|
0x00090000,
|
|
0x0006000b,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030000,
|
|
0x00000000,
|
|
0xe3a02000,
|
|
0x000b0000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030000,
|
|
0x00000000,
|
|
0xe3a02000,
|
|
0x000b0000,
|
|
0xe3e01000,
|
|
0x000b0000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030000,
|
|
0xe59f2000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe4962004,
|
|
0xe5802000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030002,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030003,
|
|
0xe59f2000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe4962004,
|
|
0xe4963004,
|
|
0xe5802000,
|
|
0xe5803004,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030002,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030003,
|
|
0xe59f2000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe4962004,
|
|
0xe5802000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030002,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030004,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a01c01,
|
|
0xe1a01c41,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030005,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe20110ff,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030006,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a01801,
|
|
0xe1a01841,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030005,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a01801,
|
|
0xe1a01821,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030006,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030005,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030006,
|
|
0x00000000,
|
|
0xe4961004,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030007,
|
|
0x00000000,
|
|
0xe8b60006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030008,
|
|
0x00000000,
|
|
0xe3a03000,
|
|
0xe3a02000,
|
|
0x000b0000,
|
|
0xe3a01000,
|
|
0x000b0000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030009,
|
|
0x00000000,
|
|
0xe3a02000,
|
|
0x000b0000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030000,
|
|
0xe59f3000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3a02000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000a,
|
|
0xe1a06000,
|
|
0xe3e01003,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000b,
|
|
0xe1a00006,
|
|
0x00000000,
|
|
0xe3a02000,
|
|
0x000b0000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030000,
|
|
0xe59f3000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e02000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000c,
|
|
0xe1a06000,
|
|
0xe3e01003,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000b,
|
|
0xe1a00006,
|
|
0x00000000,
|
|
0xe3e01001,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000b,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000d,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000e,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000f,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030010,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030011,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030012,
|
|
0x00000000,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030013,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xe3e01002,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000b,
|
|
0xe1a00006,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xe1a07001,
|
|
0xe3e01002,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000b,
|
|
0xe1a00006,
|
|
0xe1a01007,
|
|
0x00000000,
|
|
0xe89da0f0,
|
|
0x00000000,
|
|
0xe1a0c00d,
|
|
0xe92d0001,
|
|
0xe92d58f0,
|
|
0xe24cb004,
|
|
0xe1a05000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030014,
|
|
0xe1a04000,
|
|
0xe3540000,
|
|
0x000b0000,
|
|
0x00000000,
|
|
0xaa000000,
|
|
0x00050001,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030015,
|
|
0x0006000b,
|
|
0x00000000,
|
|
0x0a000000,
|
|
0x00050001,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030015,
|
|
0x0006000b,
|
|
0x00000000,
|
|
0xe04dd184,
|
|
0xe24dd010,
|
|
0xe1a0600d,
|
|
0x00000000,
|
|
0xe59f3004,
|
|
0xe59f2004,
|
|
0xea000000,
|
|
0x00050001,
|
|
0x00090000,
|
|
0x00090000,
|
|
0x0006000b,
|
|
0xe3a01000,
|
|
0x000b0000,
|
|
0xe1a00005,
|
|
0x00000000,
|
|
0xeb000000,
|
|
0x0003000a,
|
|
0x00000000,
|
|
0xeb000000,
|
|
0x00030016,
|
|
0x00000000,
|
|
0xeb000000,
|
|
0x0003000c,
|
|
0x00000000,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe3a01000,
|
|
0x000b0000,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000d,
|
|
0xe1a00c00,
|
|
0xe1a00c40,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000d,
|
|
0xe1a00800,
|
|
0xe1a00840,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000d,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000e,
|
|
0xe20000ff,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000e,
|
|
0xe1a00800,
|
|
0xe1a00820,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000e,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x0003000f,
|
|
0xe4860004,
|
|
0xe4861004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030010,
|
|
0xe4860004,
|
|
0xe4861004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030013,
|
|
0xe4860004,
|
|
0xe4861004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030011,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030012,
|
|
0xe4860004,
|
|
0x00000000,
|
|
0xe1a03006,
|
|
0xe1a02004,
|
|
0xe3a01000,
|
|
0x000b0000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030017,
|
|
0x00000000,
|
|
0xe59f0000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5900000,
|
|
0xeb000000,
|
|
0x00030018,
|
|
0x00000000,
|
|
0xe8bd000f,
|
|
0xeb000000,
|
|
0x00030019,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe59f2004,
|
|
0xe59f1004,
|
|
0xea000000,
|
|
0x00050001,
|
|
0x00090000,
|
|
0x00090000,
|
|
0x0006000b,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe5806000,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xe1a07001,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030003,
|
|
0xe59f2000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe5806000,
|
|
0xe5807004,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030003,
|
|
0xe59f2000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe3e01000,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030001,
|
|
0xe5806000,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe3a00000,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a01006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030004,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a01006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030005,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a01006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030006,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a01006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030007,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000,
|
|
0xe1a06000,
|
|
0xe1a07001,
|
|
0xeb000000,
|
|
0x0003001a,
|
|
0xe59f1000,
|
|
0xea000000,
|
|
0x00050005,
|
|
0x00090000,
|
|
0x0006000f,
|
|
0xe5810000,
|
|
0xe1a02007,
|
|
0xe1a01006,
|
|
0xe1a00005,
|
|
0xeb000000,
|
|
0x00030008,
|
|
0xe3a00001,
|
|
0xe91ba870,
|
|
0x00000000
|
|
};
|
|
|
|
static const char *const globnames[] = {
|
|
(const char *)0
|
|
};
|
|
static const char *const extnames[] = {
|
|
"lua_rawgeti",
|
|
"push_cdata",
|
|
"lua_remove",
|
|
"lua_pushnil",
|
|
"lua_pushboolean",
|
|
"push_int",
|
|
"push_uint",
|
|
"push_float",
|
|
"lua_pushnumber",
|
|
"lua_callk",
|
|
"to_typed_pointer",
|
|
"lua_settop",
|
|
"to_enum",
|
|
"to_int32",
|
|
"to_uint32",
|
|
"to_int64",
|
|
"to_uint64",
|
|
"to_uintptr",
|
|
"to_float",
|
|
"to_double",
|
|
"lua_gettop",
|
|
"luaL_error",
|
|
"to_typed_function",
|
|
"unpack_varargs_stack",
|
|
"SetLastError",
|
|
"FUNCTION",
|
|
"GetLastError",
|
|
(const char *)0
|
|
};
|
|
|
|
#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;
|
|
}
|
|
|
|
|
|
|
|
|
|
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 */
|
|
dasm_put(Dst, 0, (uintptr_t)(L), (uintptr_t)(ref), (uintptr_t)(LUA_REGISTRYINDEX));
|
|
|
|
/* get the lua function */
|
|
lua_pushvalue(L, fidx);
|
|
lua_rawseti(L, -2, ++num_upvals);
|
|
dasm_put(Dst, 17, num_upvals);
|
|
|
|
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 */
|
|
|
|
dasm_put(Dst, 24, num_upvals-1, i, (uintptr_t)(mt));
|
|
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
case UINT64_TYPE:
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
dasm_put(Dst, 47, (uintptr_t)(mt));
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
dasm_put(Dst, 68, (uintptr_t)(mt));
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 87);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 92);
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 99);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 105);
|
|
break;
|
|
|
|
case UINT16_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 112);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 119);
|
|
break;
|
|
|
|
case UINT32_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 124);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 129);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 134);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
dasm_put(Dst, 139, ((mt->pointers || mt->type != VOID_TYPE) ? 1 : 0), nargs);
|
|
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
|
|
dasm_put(Dst, 148, num_upvals-1, (uintptr_t)(mt));
|
|
} 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 */
|
|
|
|
dasm_put(Dst, 171, num_upvals-1, (uintptr_t)(mt));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
dasm_put(Dst, 194);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
dasm_put(Dst, 199);
|
|
goto single;
|
|
|
|
case UINT8_TYPE:
|
|
case UINT16_TYPE:
|
|
case UINT32_TYPE:
|
|
dasm_put(Dst, 204);
|
|
goto single;
|
|
|
|
case INT64_TYPE:
|
|
dasm_put(Dst, 209);
|
|
goto dual;
|
|
|
|
case UINT64_TYPE:
|
|
dasm_put(Dst, 214);
|
|
goto dual;
|
|
|
|
case UINTPTR_TYPE:
|
|
dasm_put(Dst, 219);
|
|
goto single;
|
|
|
|
case FLOAT_TYPE:
|
|
dasm_put(Dst, 224);
|
|
goto single;
|
|
|
|
case DOUBLE_TYPE:
|
|
dasm_put(Dst, 229);
|
|
goto dual;
|
|
|
|
single:
|
|
dasm_put(Dst, 234);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
dual:
|
|
dasm_put(Dst, 241);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback return type");
|
|
}
|
|
}
|
|
|
|
dasm_put(Dst, 250);
|
|
|
|
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);
|
|
|
|
dasm_put(Dst, 252, nargs);
|
|
if (ct->has_var_arg) {
|
|
dasm_put(Dst, 264, (uintptr_t)("too few arguments"));
|
|
} else {
|
|
dasm_put(Dst, 276, (uintptr_t)("incorrect number of arguments"));
|
|
}
|
|
|
|
/* 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 */
|
|
dasm_put(Dst, 288);
|
|
|
|
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;
|
|
|
|
dasm_put(Dst, 292, (uintptr_t)(mt), (uintptr_t)(lua_upvalueindex(num_upvals)), i);
|
|
|
|
if (mt->pointers) {
|
|
dasm_put(Dst, 303);
|
|
} else if (mt->type == FUNCTION_PTR_TYPE) {
|
|
dasm_put(Dst, 306);
|
|
} else if (mt->type == ENUM_TYPE) {
|
|
dasm_put(Dst, 309);
|
|
}
|
|
|
|
dasm_put(Dst, 312);
|
|
|
|
} else {
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 314, i);
|
|
|
|
switch (mt->type) {
|
|
case INT8_TYPE:
|
|
dasm_put(Dst, 317);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
dasm_put(Dst, 324);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
dasm_put(Dst, 331);
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
dasm_put(Dst, 336);
|
|
break;
|
|
|
|
case UINT16_TYPE:
|
|
dasm_put(Dst, 342);
|
|
break;
|
|
|
|
case UINT32_TYPE:
|
|
dasm_put(Dst, 349);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
dasm_put(Dst, 354);
|
|
break;
|
|
|
|
case UINT64_TYPE:
|
|
dasm_put(Dst, 360);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
dasm_put(Dst, 366);
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
dasm_put(Dst, 372);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
dasm_put(Dst, 377);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ct->has_var_arg) {
|
|
dasm_put(Dst, 382, nargs+1);
|
|
}
|
|
|
|
dasm_put(Dst, 390, (uintptr_t)(&jit->last_errno));
|
|
|
|
dasm_put(Dst, 399);
|
|
|
|
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 403, (uintptr_t)(&jit->last_errno), (uintptr_t)(mt), (uintptr_t)(lua_upvalueindex(num_upvals)));
|
|
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
case UINT64_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 426, (uintptr_t)(&jit->last_errno), (uintptr_t)(mt));
|
|
break;
|
|
|
|
case UINTPTR_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 453, (uintptr_t)(&jit->last_errno), (uintptr_t)(mt));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 478, (uintptr_t)(&jit->last_errno));
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 489, (uintptr_t)(&jit->last_errno));
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 505, (uintptr_t)(&jit->last_errno));
|
|
break;
|
|
|
|
case UINT8_TYPE:
|
|
case UINT16_TYPE:
|
|
case UINT32_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 521, (uintptr_t)(&jit->last_errno));
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 537, (uintptr_t)(&jit->last_errno));
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 553, (uintptr_t)(&jit->last_errno));
|
|
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);
|
|
}
|
|
|
|
#elif defined _WIN64
|
|
/*
|
|
** This file has been pre-processed with DynASM.
|
|
** http://luajit.org/dynasm.html
|
|
** DynASM version 1.3.0, DynASM x64 version 1.3.0
|
|
** DO NOT EDIT! The original file is in "call_x86.dasc".
|
|
*/
|
|
|
|
#if DASM_VERSION != 10300
|
|
#error "Version mismatch between DynASM and included encoding engine"
|
|
#endif
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
|
|
static const unsigned char build_actionlist[1965] = {
|
|
248,10,184,1,0.0,0.0,0.0,76,139,109,252,240,76,139,101,252,248,72,137,252,
|
|
236,93,195,255,248,11,232,251,1,0,72,185,237,237,137,1,184,0,0.0,0.0,0.0,
|
|
76,139,109,252,240,76,139,101,252,248,72,137,252,236,93,195,255,248,12,102.0,
|
|
15.0,214,68,36,32,232,251,1,0,72,185,237,237,137,1,252,243.0,15.0,126,76,
|
|
36,32,76,137,225,232,251,1,1,252,233,244,10,255,248,13,15.0,182,192,137,68,
|
|
36,32,232,251,1,0,72,185,237,237,137,1,139,68,36,32,72,137,194,76,137,225,
|
|
232,251,1,2,252,233,244,10,255,248,14,137,68,36,32,232,251,1,0,72,185,237,
|
|
237,137,1,139,68,36,32,72,137,194,76,137,225,232,251,1,3,252,233,244,10,255,
|
|
248,15,137,68,36,32,232,251,1,0,72,185,237,237,137,1,139,68,36,32,72,137,
|
|
194,76,137,225,232,251,1,4,252,233,244,10,255,248,16,102,184,0,0.0,72,186,
|
|
237,237,76,137,225,232,251,1,5,255,248,17,102,184,0,0.0,72,186,237,237,76,
|
|
137,225,232,251,1,5,255,248,18,72,137,77,16,72,137,85,24,76,137,69,32,76,
|
|
137,77,40,102.0,15.0,214,69,252,240,102.0,15.0,214,77,232,102.0,15.0,214,
|
|
85,224,102.0,15.0,214,93,216,195,255,72,139,141,233,255,72,137,132,253,36,
|
|
233,255,221.0,133,233,255,217.0,133,233,255,252,243.0,15.0,126,133,233,255,
|
|
252,243.0,15.0,90,133,233,255,221.0,156,253,36,233,255,217.0,156,253,36,233,
|
|
255,102.0,15.0,214,132,253,36,233,255,252,242.0,15.0,90,192,102.0,15.0,214,
|
|
132,253,36,233,255,252,242.0,15.0,90,192,102.0,15.0,126,132,253,36,233,255,
|
|
85,72,137,229,65,84,72,129.0,252,236,239,232,244,18,255,73,188,237,237,255,
|
|
73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,1,6,255,73,199.0,192,
|
|
237,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,76,137,225,232,251,
|
|
1,6,255,73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,1,6,73,184,237,
|
|
237,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,76,137,225,232,251,
|
|
1,7,255,72,137,8,72,199.0,194,252,254,252,255.0,252,255.0,252,255.0,76,137,
|
|
225,232,251,1,8,255,73,184,237,237,72,199.0,194,0,0.0,0.0,0.0,76,137,225,
|
|
232,251,1,7,255,72,137,8,255,102.0,15.0,214,0,255,217.0,24,255,217.0,88,4,
|
|
255,102.0,15.0,214,64,8,255,252,243.0,15.0,126,200,76,137,225,232,251,1,1,
|
|
255,15.0,182,201,72,137,202,76,137,225,232,251,1,2,255,15.0,182,201,255,15.0,
|
|
190,201,255,72,137,202,76,137,225,232,251,1,3,255,15.0,183,201,255,15.0,191,
|
|
201,255,72,137,202,76,137,225,232,251,1,4,255,73,185,237,237,73,199.0,192,
|
|
237,72,199.0,194,237,76,137,225,232,251,1,9,255,73,199.0,192,237,72,199.0,
|
|
194,252,254,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,6,73,185,237,
|
|
237,73,199.0,192,252,255,252,255.0,252,255.0,252,255.0,72,199.0,194,252,254,
|
|
252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,10,72,137,68,36,32,72,
|
|
199.0,194,252,252,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,72,
|
|
139,68,36,32,255,73,199.0,192,237,72,199.0,194,252,254,252,255.0,252,255.0,
|
|
252,255.0,76,137,225,232,251,1,6,73,185,237,237,73,199.0,192,252,255,252,
|
|
255.0,252,255.0,252,255.0,72,199.0,194,252,254,252,255.0,252,255.0,252,255.0,
|
|
76,137,225,232,251,1,12,137,68,36,32,72,199.0,194,252,252,252,255.0,252,255.0,
|
|
252,255.0,76,137,225,232,251,1,11,139,68,36,32,255,72,199.0,194,252,254,252,
|
|
255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,255,72,199.0,194,252,255,
|
|
252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,13,255,72,199.0,194,252,
|
|
255,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,14,255,137,68,36,32,
|
|
72,199.0,194,252,253,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,
|
|
139,68,36,32,255,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,76,137,
|
|
225,232,251,1,15,255,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,76,
|
|
137,225,232,251,1,16,255,72,137,68,36,32,72,199.0,194,252,253,252,255.0,252,
|
|
255.0,252,255.0,76,137,225,232,251,1,11,72,139,68,36,32,255,72,199.0,194,
|
|
252,255,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,17,72,137,68,36,
|
|
32,72,199.0,194,252,253,252,255.0,252,255.0,252,255.0,76,137,225,232,251,
|
|
1,11,72,139,68,36,32,255,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,
|
|
76,137,225,232,251,1,18,102.0,15.0,214,68,36,32,72,199.0,194,252,253,252,
|
|
255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,255,252,242.0,15.0,90,68,
|
|
36,32,255,252,243.0,15.0,126,68,36,32,255,72,199.0,194,252,255,252,255.0,
|
|
252,255.0,252,255.0,76,137,225,232,251,1,19,102.0,15.0,214,68,36,32,72,199.0,
|
|
194,252,253,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,252,243.0,
|
|
15.0,126,68,36,32,255,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,
|
|
76,137,225,232,251,1,20,102.0,15.0,214,68,36,32,102.0,15.0,214,76,36,40,72,
|
|
199.0,194,252,253,252,255.0,252,255.0,252,255.0,76,137,225,232,251,1,11,252,
|
|
243.0,15.0,126,68,36,32,252,243.0,15.0,126,76,36,40,255,72,139,141,233,73,
|
|
199.0,192,252,255,252,255.0,252,255.0,252,255.0,76,137,226,72,137,201,232,
|
|
251,1,20,72,131.0,252,236,4,72,199.0,194,252,253,252,255.0,252,255.0,252,
|
|
255.0,76,137,225,232,251,1,11,255,76,139,101,252,248,72,137,252,236,93,194,
|
|
236,255,85,72,137,229,65,84,65,85,73,137,204,72,131.0,252,236,32,76,137,225,
|
|
232,251,1,21,73,137,197,72,129.0,252,248,239,15.0,140,244,16,255,15.0,143,
|
|
244,17,255,72,193.0,224,4,72,41,196,72,129.0,252,236,239,255,73,184,237,237,
|
|
72,199.0,194,0,0.0,0.0,0.0,76,137,225,232,251,1,7,72,131.0,252,236,16,255,
|
|
73,185,237,237,73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,1,10,
|
|
255,73,185,237,237,73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,1,
|
|
22,255,73,185,237,237,73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,
|
|
1,12,255,72,199.0,194,237,76,137,225,232,251,1,14,255,15.0,182,192,255,15.0,
|
|
190,192,255,15.0,183,192,255,15.0,191,192,255,72,199.0,194,237,76,137,225,
|
|
232,251,1,14,131.0,252,248,0,15.0,149.0,208,15.0,182,192,255,72,199.0,194,
|
|
237,76,137,225,232,251,1,13,255,72,199.0,194,237,76,137,225,232,251,1,17,
|
|
255,72,199.0,194,237,76,137,225,232,251,1,15,255,72,199.0,194,237,76,137,
|
|
225,232,251,1,16,255,72,199.0,194,237,76,137,225,232,251,1,18,255,72,199.0,
|
|
194,237,76,137,225,232,251,1,20,255,252,243.0,15.0,126,193,255,72,141,132,
|
|
253,36,233,72,131.0,252,236,4,73,199.0,192,237,76,137,226,72,137,193,232,
|
|
251,1,20,255,72,199.0,194,237,76,137,225,232,251,1,19,255,72,199.0,194,237,
|
|
76,137,225,232,251,1,19,137,4,36,217.0,4,36,255,137,20,36,217.0,4,36,255,
|
|
73,129.0,252,253,239,15.0,142,244,247,72,137,224,72,129.0,192,239,73,137,
|
|
193,77,137,232,72,199.0,194,237,76,137,225,232,251,1,23,72,137,224,72,129.0,
|
|
192,239,73,137,193,73,199.0,192,237,72,199.0,194,237,76,137,225,232,251,1,
|
|
24,252,233,244,248,248,1,72,137,224,72,129.0,192,239,73,137,193,77,137,232,
|
|
72,199.0,194,237,76,137,225,232,251,1,24,248,2,255,72,137,224,72,129.0,192,
|
|
239,73,137,193,77,137,232,72,199.0,194,237,76,137,225,232,251,1,23,255,72,
|
|
185,237,237,139,1,72,137,193,232,251,1,25,255,72,131.0,196,32,255,252,243.0,
|
|
15.0,126,156,253,36,233,255,76,139,140,253,36,233,255,252,243.0,15.0,126,
|
|
148,253,36,233,255,76,139,132,253,36,233,255,252,243.0,15.0,126,140,253,36,
|
|
233,255,72,139,148,253,36,233,255,252,243.0,15.0,126,4,36,255,72,139,12,36,
|
|
255,232,251,1,26,72,131.0,252,236,48,255,72,137,68,36,32,232,251,1,0,72,185,
|
|
237,237,137,1,73,184,237,237,72,199.0,194,237,76,137,225,232,251,1,7,72,139,
|
|
76,36,32,72,137,8,252,233,244,10,255,72,137,68,36,32,232,251,1,0,72,185,237,
|
|
237,137,1,73,184,237,237,72,199.0,194,0,0.0,0.0,0.0,76,137,225,232,251,1,
|
|
7,72,139,76,36,32,72,137,8,252,233,244,10,255,102.0,15.0,214,68,36,32,232,
|
|
251,1,0,72,185,237,237,137,1,73,184,237,237,72,199.0,194,0,0.0,0.0,0.0,76,
|
|
137,225,232,251,1,7,72,139,76,36,32,72,137,8,252,233,244,10,255,102.0,15.0,
|
|
214,76,36,40,102.0,15.0,214,68,36,32,232,251,1,0,72,185,237,237,137,1,73,
|
|
184,237,237,72,199.0,194,0,0.0,0.0,0.0,76,137,225,232,251,1,7,72,139,76,36,
|
|
40,72,137,72,8,72,139,76,36,32,72,137,8,252,233,244,10,255,252,233,244,11,
|
|
255,252,233,244,13,255,252,233,244,14,255,252,233,244,15,255,252,243.0,15.0,
|
|
90,192,252,233,244,12,255
|
|
};
|
|
|
|
static const char *const globnames[] = {
|
|
"lua_return_arg",
|
|
"lua_return_void",
|
|
"lua_return_double",
|
|
"lua_return_bool",
|
|
"lua_return_int",
|
|
"lua_return_uint",
|
|
"too_few_arguments",
|
|
"too_many_arguments",
|
|
"save_registers",
|
|
(const char *)0
|
|
};
|
|
static const char *const extnames[] = {
|
|
"GetLastError",
|
|
"lua_pushnumber",
|
|
"lua_pushboolean",
|
|
"push_int",
|
|
"push_uint",
|
|
"luaL_error",
|
|
"lua_rawgeti",
|
|
"push_cdata",
|
|
"lua_remove",
|
|
"lua_callk",
|
|
"check_typed_pointer",
|
|
"lua_settop",
|
|
"check_enum",
|
|
"check_uint32",
|
|
"check_int32",
|
|
"check_uint64",
|
|
"check_int64",
|
|
"check_uintptr",
|
|
"check_double",
|
|
"check_complex_float",
|
|
"check_complex_double",
|
|
"lua_gettop",
|
|
"check_typed_cfunction",
|
|
"unpack_varargs_stack",
|
|
"unpack_varargs_reg",
|
|
"SetLastError",
|
|
"FUNCTION",
|
|
(const char *)0
|
|
};
|
|
|
|
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define JUMP_SIZE 14
|
|
#else
|
|
#define JUMP_SIZE 4
|
|
#endif
|
|
|
|
#define MIN_BRANCH INT32_MIN
|
|
#define MAX_BRANCH INT32_MAX
|
|
#define BRANCH_OFF 4
|
|
|
|
static void compile_extern_jump(struct jit* jit, lua_State* L, cfunction func, uint8_t* code)
|
|
{
|
|
/* The jump code is the function pointer followed by a stub to call the
|
|
* function pointer. The stub exists in 64 bit so we can jump to functions
|
|
* with an offset greater than 2 GB.
|
|
*
|
|
* Note we have to manually set this up since there are commands buffered
|
|
* in the jit state and dynasm doesn't support rip relative addressing.
|
|
*
|
|
* eg on 64 bit:
|
|
* 0-8: function ptr
|
|
* 8-14: jmp aword [rip-14]
|
|
*
|
|
* for 32 bit we only set the function ptr as it can always fit in a 32
|
|
* bit displacement
|
|
*/
|
|
#if defined _WIN64 || defined __amd64__
|
|
*(cfunction*) code = func;
|
|
code[8] = 0xFF; /* FF /4 operand for jmp */
|
|
code[9] = 0x25; /* RIP displacement */
|
|
*(int32_t*) &code[10] = -14;
|
|
#else
|
|
*(cfunction*) code = func;
|
|
#endif
|
|
}
|
|
|
|
void compile_globals(struct jit* jit, lua_State* L)
|
|
{
|
|
struct jit* Dst = jit;
|
|
int* perr = &jit->last_errno;
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
/* Note: since the return code uses EBP to reset the stack pointer, we
|
|
* don't have to track the amount of stack space used. It also means we
|
|
* can handle stdcall and cdecl with the same code.
|
|
*/
|
|
|
|
/* Note the various call_* functions want 32 bytes of 16 byte aligned
|
|
* stack
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* the general idea for the return functions is:
|
|
* 1) Save return value on stack
|
|
* 2) Call get_errno (this trashes the registers hence #1)
|
|
* 3) Unpack return value from stack
|
|
* 4) Call lua push function
|
|
* 5) Set eax to number of returned args (0 or 1)
|
|
* 6) Call return which pops our stack frame
|
|
*/
|
|
|
|
dasm_put(Dst, 0);
|
|
|
|
dasm_put(Dst, 24, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 58, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 95, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 133, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 168, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 203, (unsigned int)((uintptr_t)(&"too few arguments")), (unsigned int)(((uintptr_t)(&"too few arguments"))>>32));
|
|
|
|
dasm_put(Dst, 221, (unsigned int)((uintptr_t)(&"too many arguments")), (unsigned int)(((uintptr_t)(&"too many arguments"))>>32));
|
|
|
|
dasm_put(Dst, 239);
|
|
|
|
compile(Dst, L, NULL, LUA_NOREF);
|
|
}
|
|
|
|
int x86_return_size(lua_State* L, int usr, const struct ctype* ct)
|
|
{
|
|
int ret = 0;
|
|
const struct ctype* mt;
|
|
|
|
if (ct->calling_convention != C_CALL) {
|
|
size_t i;
|
|
size_t argn = lua_rawlen(L, usr);
|
|
for (i = 1; i <= argn; i++) {
|
|
lua_rawgeti(L, usr, (int) i);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
ret += sizeof(void*);
|
|
} else {
|
|
switch (mt->type) {
|
|
case DOUBLE_TYPE:
|
|
case COMPLEX_FLOAT_TYPE:
|
|
case INT64_TYPE:
|
|
ret += 8;
|
|
break;
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
ret += 16;
|
|
break;
|
|
case INTPTR_TYPE:
|
|
ret += sizeof(intptr_t);
|
|
break;
|
|
case FUNCTION_PTR_TYPE:
|
|
ret += sizeof(cfunction);
|
|
break;
|
|
case BOOL_TYPE:
|
|
case FLOAT_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
ret += 4;
|
|
break;
|
|
default:
|
|
return luaL_error(L, "NYI - argument type");
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
ret += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
#define MAX_REGISTERS(ct) 4 /* rcx, rdx, r8, r9 */
|
|
|
|
#elif defined __amd64__
|
|
#define MAX_INT_REGISTERS(ct) 6 /* rdi, rsi, rdx, rcx, r8, r9 */
|
|
#define MAX_FLOAT_REGISTERS(ct) 8 /* xmm0-7 */
|
|
|
|
#else
|
|
#define MAX_INT_REGISTERS(ct) ((ct)->calling_convention == FAST_CALL ? 2 /* ecx, edx */ : 0)
|
|
#define MAX_FLOAT_REGISTERS(ct) 0
|
|
#endif
|
|
|
|
struct reg_alloc {
|
|
#ifdef _WIN64
|
|
int regs;
|
|
int is_float[4];
|
|
int is_int[4];
|
|
#else
|
|
int floats;
|
|
int ints;
|
|
#endif
|
|
int off;
|
|
};
|
|
|
|
#ifdef _WIN64
|
|
#define REGISTER_STACK_SPACE(ct) (4*8)
|
|
#elif defined __amd64__
|
|
#define REGISTER_STACK_SPACE(ct) (14*8)
|
|
#else
|
|
#define REGISTER_STACK_SPACE(ct) LUAFFI_ALIGN_UP(((ct)->calling_convention == FAST_CALL ? 2*4 : 0), 15)
|
|
#endif
|
|
|
|
/* Fastcall:
|
|
* Uses ecx, edx as first two int registers
|
|
* Everything else on stack (include 64bit ints)
|
|
* No overflow stack space
|
|
* Pops the stack before returning
|
|
* Returns int in eax, float in ST0
|
|
* We use the same register allocation logic as posix x64 with 2 int regs and 0 float regs
|
|
*/
|
|
|
|
static void LUAFFI_get_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
/* grab the register from the shadow space */
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 280, 16 + 8*reg->regs);
|
|
reg->regs++;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 280, - 80 - 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 281, - 8 - 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 280, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 281, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void add_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 285, 32 + 8*(reg->regs));
|
|
reg->is_int[reg->regs++] = 1;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 285, 32 + 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 285, 32 + 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 285, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 286, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void get_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 292, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 296, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
int off;
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->regs;
|
|
reg->regs++;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->floats;
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
else {
|
|
off = reg->off;
|
|
reg->off += is_double ? 8 : 4;
|
|
}
|
|
|
|
if (is_double) {
|
|
dasm_put(Dst, 300, off);
|
|
} else {
|
|
dasm_put(Dst, 307, off);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void add_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 314, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 320, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 326, 32 + 8*(reg->regs));
|
|
} else {
|
|
dasm_put(Dst, 334, 32 + 8*(reg->regs));
|
|
}
|
|
reg->is_float[reg->regs++] = 1;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 326, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
} else {
|
|
dasm_put(Dst, 334, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
}
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_double) {
|
|
dasm_put(Dst, 326, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 347, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 1)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 1)
|
|
#else
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 0)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 0)
|
|
#endif
|
|
|
|
cfunction compile_callback(lua_State* L, int fidx, int ct_usr, const struct ctype* ct)
|
|
{
|
|
int i, nargs;
|
|
cfunction* pf;
|
|
struct ctype ct2 = *ct;
|
|
const struct ctype* mt;
|
|
struct reg_alloc reg;
|
|
int num_upvals = 0;
|
|
int top = lua_gettop(L);
|
|
struct jit* Dst = get_jit(L);
|
|
int ref;
|
|
int hidden_arg_off = 0;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
fidx = lua_absindex(L, fidx);
|
|
|
|
assert(lua_isnil(L, fidx) || lua_isfunction(L, fidx));
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
#ifdef _WIN64
|
|
reg.off = 16 + REGISTER_STACK_SPACE(ct); /* stack registers are above the shadow space */
|
|
#elif __amd64__
|
|
reg.off = 16;
|
|
#else
|
|
reg.off = 8;
|
|
#endif
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
// add a table to store ctype and function upvalues
|
|
// callback_set assumes the first value is the lua function
|
|
nargs = (int) lua_rawlen(L, ct_usr);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1);
|
|
ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
if (ct->has_var_arg) {
|
|
luaL_error(L, "can't create callbacks with varargs");
|
|
}
|
|
|
|
// setup a stack frame to hold args for the call into lua_call
|
|
|
|
dasm_put(Dst, 360, 8 + 16 + 32 + REGISTER_STACK_SPACE(ct));
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
}
|
|
|
|
// hardcode the lua_State* value into the assembly
|
|
dasm_put(Dst, 375, (unsigned int)((uintptr_t)(L)), (unsigned int)(((uintptr_t)(L))>>32));
|
|
|
|
/* get the upval table */
|
|
dasm_put(Dst, 380, ref, LUA_REGISTRYINDEX);
|
|
|
|
/* get the lua function */
|
|
lua_pushvalue(L, fidx);
|
|
lua_rawseti(L, -2, ++num_upvals);
|
|
assert(num_upvals == CALLBACK_FUNC_USR_IDX);
|
|
dasm_put(Dst, 396, num_upvals);
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
hidden_arg_off = reg.off;
|
|
reg.off += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#else
|
|
(void) hidden_arg_off;
|
|
#endif
|
|
|
|
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 */
|
|
/* on the lua stack in the callback:
|
|
* upval tbl, lua func, i-1 args
|
|
*/
|
|
dasm_put(Dst, 419, num_upvals-1, -i-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 457);
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 479, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
LUAFFI_get_int(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 498);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 479, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 498);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if defined _WIN64 || defined __amd64__
|
|
/* complex floats are two floats packed into a double */
|
|
dasm_put(Dst, 479, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 502);
|
|
#else
|
|
/* complex floats are real followed by imag on the stack */
|
|
dasm_put(Dst, 479, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 507);
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 510);
|
|
#endif
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 479, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
/* real */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 502);
|
|
/* imag */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 514);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
get_float(Dst, ct, ®, mt->type == DOUBLE_TYPE);
|
|
dasm_put(Dst, 520);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 533);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 547);
|
|
} else {
|
|
dasm_put(Dst, 551);
|
|
}
|
|
dasm_put(Dst, 555);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 566);
|
|
} else {
|
|
dasm_put(Dst, 570);
|
|
}
|
|
dasm_put(Dst, 555);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 574);
|
|
} else {
|
|
dasm_put(Dst, 555);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
dasm_put(Dst, 585, (unsigned int)((uintptr_t)(0)), (unsigned int)(((uintptr_t)(0))>>32), (mt->pointers || mt->type != VOID_TYPE) ? 1 : 0, nargs);
|
|
|
|
// Unpack the return argument if not "void", also clean-up the lua stack
|
|
// to remove the return argument and bind table. Use lua_settop rather
|
|
// than lua_pop as lua_pop is implemented as a macro.
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
dasm_put(Dst, 605, num_upvals-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
|
|
} 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 */
|
|
dasm_put(Dst, 689, num_upvals-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 771);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 790);
|
|
} else {
|
|
dasm_put(Dst, 809);
|
|
}
|
|
dasm_put(Dst, 828);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
lua_pop(L, 1);
|
|
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 855);
|
|
} else {
|
|
dasm_put(Dst, 874);
|
|
}
|
|
|
|
dasm_put(Dst, 893);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 922);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 969);
|
|
if (mt->type == FLOAT_TYPE) {
|
|
dasm_put(Dst, 1012);
|
|
} else {
|
|
dasm_put(Dst, 1020);
|
|
}
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit complex floats are two floats packed into a double,
|
|
* on 32 bit returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1028);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored which is popped by the called
|
|
* function */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1078);
|
|
#else
|
|
dasm_put(Dst, 1141, hidden_arg_off);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback return type");
|
|
}
|
|
}
|
|
|
|
dasm_put(Dst, 1190, x86_return_size(L, ct_usr, ct));
|
|
|
|
lua_pop(L, 1); /* upval table - already in registry */
|
|
assert(lua_gettop(L) == top);
|
|
|
|
ct2.is_jitted = 1;
|
|
pf = (cfunction*) push_cdata(L, ct_usr, &ct2);
|
|
*pf = compile(Dst, L, NULL, ref);
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
return *pf;
|
|
}
|
|
|
|
void compile_function(lua_State* L, cfunction func, int ct_usr, const struct ctype* ct)
|
|
{
|
|
size_t i, nargs;
|
|
int num_upvals;
|
|
const struct ctype* mbr_ct;
|
|
struct jit* Dst = get_jit(L);
|
|
struct reg_alloc reg;
|
|
void* p;
|
|
int top = lua_gettop(L);
|
|
int* perr = &Dst->last_errno;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
reg.off = 32 + REGISTER_STACK_SPACE(ct);
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
p = push_cdata(L, ct_usr, ct);
|
|
*(cfunction*) p = func;
|
|
num_upvals = 1;
|
|
|
|
nargs = lua_rawlen(L, ct_usr);
|
|
|
|
if (ct->calling_convention != C_CALL && ct->has_var_arg) {
|
|
luaL_error(L, "vararg is only allowed with the c calling convention");
|
|
}
|
|
|
|
dasm_put(Dst, 1203, nargs);
|
|
|
|
if (!ct->has_var_arg) {
|
|
dasm_put(Dst, 1239);
|
|
}
|
|
|
|
/* no need to zero extend eax returned by lua_gettop to rax as x86-64
|
|
* preguarentees that the upper 32 bits will be zero */
|
|
dasm_put(Dst, 1244, 32 + REGISTER_STACK_SPACE(ct));
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
/* Returned complex doubles require a hidden first parameter where the
|
|
* data is stored, which is popped by the calling code. */
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mbr_ct->pointers && mbr_ct->type == COMPLEX_DOUBLE_TYPE) {
|
|
/* we can allocate more space for arguments as long as no add_*
|
|
* function has been called yet, mbr_ct will be added as an upvalue in
|
|
* the return processing later */
|
|
dasm_put(Dst, 1257, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
add_pointer(Dst, ct, ®);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
for (i = 1; i <= nargs; i++) {
|
|
lua_rawgeti(L, ct_usr, (int) i);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1281, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1301, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1321, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_int(Dst, ct, ®, 0);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
dasm_put(Dst, 1341, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1353);
|
|
} else {
|
|
dasm_put(Dst, 1357);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
dasm_put(Dst, 1341, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1361);
|
|
} else {
|
|
dasm_put(Dst, 1365);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
dasm_put(Dst, 1369, i);
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1391, i);
|
|
} else {
|
|
dasm_put(Dst, 1341, i);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
dasm_put(Dst, 1403, i);
|
|
add_pointer(Dst, ct, ®);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1415, i);
|
|
} else {
|
|
dasm_put(Dst, 1427, i);
|
|
}
|
|
add_int(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
dasm_put(Dst, 1439, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored (this is popped by the called
|
|
* function) */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1451, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 1463);
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
dasm_put(Dst, 1469, reg.off, i);
|
|
reg.off += 16;
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
dasm_put(Dst, 1439, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1495, i);
|
|
/* complex floats are two floats packed into a double */
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
/* returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1507, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 1525);
|
|
add_float(Dst, ct, ®, 0);
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ct->has_var_arg) {
|
|
#ifdef _WIN64
|
|
if (reg.regs < MAX_REGISTERS(ct)) {
|
|
assert(reg.regs == nargs);
|
|
dasm_put(Dst, 1532, MAX_REGISTERS(ct), 32 + 8*MAX_REGISTERS(ct), MAX_REGISTERS(ct)+1, 32 + 8*(reg.regs), MAX_REGISTERS(ct), nargs+1, 32 + 8*(reg.regs), nargs+1);
|
|
} else {
|
|
dasm_put(Dst, 1623, reg.off, nargs+1);
|
|
}
|
|
|
|
for (i = nargs; i < MAX_REGISTERS(ct); i++) {
|
|
reg.is_int[i] = reg.is_float[i] = 1;
|
|
}
|
|
reg.regs = MAX_REGISTERS(ct);
|
|
#elif defined __amd64__
|
|
if (reg.floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
}
|
|
|
|
if (reg.ints < MAX_INT_REGISTERS(ct)) {
|
|
}
|
|
|
|
|
|
reg.floats = MAX_FLOAT_REGISTERS(ct);
|
|
reg.ints = MAX_INT_REGISTERS(ct);
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
dasm_put(Dst, 1648, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
/* remove the stack space to call local functions */
|
|
dasm_put(Dst, 1662);
|
|
|
|
#ifdef _WIN64
|
|
switch (reg.regs) {
|
|
case 4:
|
|
if (reg.is_float[3]) {
|
|
dasm_put(Dst, 1667, 8*3);
|
|
}
|
|
if (reg.is_int[3]) {
|
|
dasm_put(Dst, 1676, 8*3);
|
|
}
|
|
case 3:
|
|
if (reg.is_float[2]) {
|
|
dasm_put(Dst, 1683, 8*2);
|
|
}
|
|
if (reg.is_int[2]) {
|
|
dasm_put(Dst, 1692, 8*2);
|
|
}
|
|
case 2:
|
|
if (reg.is_float[1]) {
|
|
dasm_put(Dst, 1699, 8*1);
|
|
}
|
|
if (reg.is_int[1]) {
|
|
dasm_put(Dst, 1708, 8*1);
|
|
}
|
|
case 1:
|
|
if (reg.is_float[0]) {
|
|
dasm_put(Dst, 1715);
|
|
}
|
|
if (reg.is_int[0]) {
|
|
dasm_put(Dst, 1722);
|
|
}
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
/* don't remove the space for the registers as we need 32 bytes of register overflow space */
|
|
assert(REGISTER_STACK_SPACE(ct) == 32);
|
|
|
|
#elif defined __amd64__
|
|
switch (reg.floats) {
|
|
case 8:
|
|
case 7:
|
|
case 6:
|
|
case 5:
|
|
case 4:
|
|
case 3:
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
switch (reg.ints) {
|
|
case 6:
|
|
case 5:
|
|
case 4:
|
|
case 3:
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
#else
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
switch (reg.ints) {
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef __amd64__
|
|
if (ct->has_var_arg) {
|
|
/* al stores an upper limit on the number of float register, note that
|
|
* its allowed to be more than the actual number of float registers used as
|
|
* long as its 0-8 */
|
|
}
|
|
#endif
|
|
|
|
dasm_put(Dst, 1727);
|
|
|
|
/* note on windows X86 the stack may be only aligned to 4 (stdcall will
|
|
* have popped a multiple of 4 bytes), but we don't need 16 byte alignment on
|
|
* that platform
|
|
*/
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers || mbr_ct->type == INTPTR_TYPE) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1737, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals));
|
|
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1737, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals));
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1780, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1826, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1873, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1935);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1940);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1353);
|
|
} else {
|
|
dasm_put(Dst, 1357);
|
|
}
|
|
dasm_put(Dst, 1945);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1361);
|
|
} else {
|
|
dasm_put(Dst, 1365);
|
|
}
|
|
dasm_put(Dst, 1945);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1950);
|
|
} else {
|
|
dasm_put(Dst, 1945);
|
|
}
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1955);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1960);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call return type");
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + num_upvals);
|
|
{
|
|
cfunction f = compile(Dst, L, func, LUA_NOREF);
|
|
/* add a callback as an upval so that the jitted code gets cleaned up when
|
|
* the function gets gc'd */
|
|
push_callback(L, f);
|
|
lua_pushcclosure(L, (lua_CFunction) f, num_upvals+1);
|
|
}
|
|
}
|
|
|
|
#elif defined __amd64__
|
|
/*
|
|
** This file has been pre-processed with DynASM.
|
|
** http://luajit.org/dynasm.html
|
|
** DynASM version 1.3.0, DynASM x64 version 1.3.0
|
|
** DO NOT EDIT! The original file is in "call_x86.dasc".
|
|
*/
|
|
|
|
#if DASM_VERSION != 10300
|
|
#error "Version mismatch between DynASM and included encoding engine"
|
|
#endif
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
|
|
static const unsigned char build_actionlist[2022] = {
|
|
248,10,184,1,0.0,0.0,0.0,76,139,109,252,240,76,139,101,252,248,72,137,252,
|
|
236,93,195,255,248,11,232,251,1,0,72,185,237,237,137,1,184,0,0.0,0.0,0.0,
|
|
76,139,109,252,240,76,139,101,252,248,72,137,252,236,93,195,255,248,12,102.0,
|
|
15.0,214,68,36,32,232,251,1,0,72,185,237,237,137,1,252,243.0,15.0,126,68,
|
|
36,32,76,137,231,232,251,1,1,252,233,244,10,255,248,13,15.0,182,192,137,68,
|
|
36,32,232,251,1,0,72,185,237,237,137,1,139,68,36,32,72,137,198,76,137,231,
|
|
232,251,1,2,252,233,244,10,255,248,14,137,68,36,32,232,251,1,0,72,185,237,
|
|
237,137,1,139,68,36,32,72,137,198,76,137,231,232,251,1,3,252,233,244,10,255,
|
|
248,15,137,68,36,32,232,251,1,0,72,185,237,237,137,1,139,68,36,32,72,137,
|
|
198,76,137,231,232,251,1,4,252,233,244,10,255,248,16,102,184,0,0.0,72,190,
|
|
237,237,76,137,231,232,251,1,5,255,248,17,102,184,0,0.0,72,190,237,237,76,
|
|
137,231,232,251,1,5,255,248,18,102.0,15.0,214,69,252,240,102.0,15.0,214,77,
|
|
232,102.0,15.0,214,85,224,102.0,15.0,214,93,216,102.0,15.0,214,101,208,102.0,
|
|
15.0,214,109,200,102.0,15.0,214,117,192,102.0,15.0,214,125,184,72,137,125,
|
|
176,72,137,117,168,72,137,85,160,72,137,77,152,76,137,69,144,76,137,77,136,
|
|
195,255,72,139,141,233,255,72,137,132,253,36,233,255,221.0,133,233,255,217.0,
|
|
133,233,255,252,243.0,15.0,126,133,233,255,252,243.0,15.0,90,133,233,255,
|
|
221.0,156,253,36,233,255,217.0,156,253,36,233,255,102.0,15.0,214,132,253,
|
|
36,233,255,252,242.0,15.0,90,192,102.0,15.0,214,132,253,36,233,255,252,242.0,
|
|
15.0,90,192,102.0,15.0,126,132,253,36,233,255,85,72,137,229,65,84,72,129.0,
|
|
252,236,239,232,244,18,255,73,188,237,237,255,72,199.0,194,237,72,199.0,198,
|
|
237,76,137,231,232,251,1,6,255,72,199.0,194,237,72,199.0,198,252,255,252,
|
|
255.0,252,255.0,252,255.0,76,137,231,232,251,1,6,255,72,199.0,194,237,72,
|
|
199.0,198,237,76,137,231,232,251,1,6,72,186,237,237,72,199.0,198,252,255,
|
|
252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,7,255,72,137,8,72,199.0,
|
|
198,252,254,252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,8,255,72,186,
|
|
237,237,72,199.0,198,0,0.0,0.0,0.0,76,137,231,232,251,1,7,255,72,137,8,255,
|
|
102.0,15.0,214,0,255,217.0,24,255,217.0,88,4,255,102.0,15.0,214,64,8,255,
|
|
76,137,231,232,251,1,1,255,15.0,182,201,72,137,206,76,137,231,232,251,1,2,
|
|
255,15.0,182,201,255,15.0,190,201,255,72,137,206,76,137,231,232,251,1,3,255,
|
|
15.0,183,201,255,15.0,191,201,255,72,137,206,76,137,231,232,251,1,4,255,72,
|
|
185,237,237,72,199.0,194,237,72,199.0,198,237,76,137,231,232,251,1,9,255,
|
|
72,199.0,194,237,72,199.0,198,252,254,252,255.0,252,255.0,252,255.0,76,137,
|
|
231,232,251,1,6,72,185,237,237,72,199.0,194,252,255,252,255.0,252,255.0,252,
|
|
255.0,72,199.0,198,252,254,252,255.0,252,255.0,252,255.0,76,137,231,232,251,
|
|
1,10,72,137,68,36,32,72,199.0,198,252,252,252,255.0,252,255.0,252,255.0,76,
|
|
137,231,232,251,1,11,72,139,68,36,32,255,72,199.0,194,237,72,199.0,198,252,
|
|
254,252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,6,72,185,237,237,72,
|
|
199.0,194,252,255,252,255.0,252,255.0,252,255.0,72,199.0,198,252,254,252,
|
|
255.0,252,255.0,252,255.0,76,137,231,232,251,1,12,137,68,36,32,72,199.0,198,
|
|
252,252,252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,11,139,68,36,32,
|
|
255,72,199.0,198,252,254,252,255.0,252,255.0,252,255.0,76,137,231,232,251,
|
|
1,11,255,72,199.0,198,252,255,252,255.0,252,255.0,252,255.0,76,137,231,232,
|
|
251,1,13,255,72,199.0,198,252,255,252,255.0,252,255.0,252,255.0,76,137,231,
|
|
232,251,1,14,255,137,68,36,32,72,199.0,198,252,253,252,255.0,252,255.0,252,
|
|
255.0,76,137,231,232,251,1,11,139,68,36,32,255,72,199.0,198,252,255,252,255.0,
|
|
252,255.0,252,255.0,76,137,231,232,251,1,15,255,72,199.0,198,252,255,252,
|
|
255.0,252,255.0,252,255.0,76,137,231,232,251,1,16,255,72,137,68,36,32,72,
|
|
199.0,198,252,253,252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,11,72,
|
|
139,68,36,32,255,72,199.0,198,252,255,252,255.0,252,255.0,252,255.0,76,137,
|
|
231,232,251,1,17,72,137,68,36,32,72,199.0,198,252,253,252,255.0,252,255.0,
|
|
252,255.0,76,137,231,232,251,1,11,72,139,68,36,32,255,72,199.0,198,252,255,
|
|
252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,18,102.0,15.0,214,68,36,
|
|
32,72,199.0,198,252,253,252,255.0,252,255.0,252,255.0,76,137,231,232,251,
|
|
1,11,255,252,242.0,15.0,90,68,36,32,255,252,243.0,15.0,126,68,36,32,255,72,
|
|
199.0,198,252,255,252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,19,102.0,
|
|
15.0,214,68,36,32,72,199.0,198,252,253,252,255.0,252,255.0,252,255.0,76,137,
|
|
231,232,251,1,11,252,243.0,15.0,126,68,36,32,255,72,199.0,198,252,255,252,
|
|
255.0,252,255.0,252,255.0,76,137,231,232,251,1,20,102.0,15.0,214,68,36,32,
|
|
102.0,15.0,214,76,36,40,72,199.0,198,252,253,252,255.0,252,255.0,252,255.0,
|
|
76,137,231,232,251,1,11,252,243.0,15.0,126,68,36,32,252,243.0,15.0,126,76,
|
|
36,40,255,72,139,141,233,72,199.0,194,252,255,252,255.0,252,255.0,252,255.0,
|
|
76,137,230,72,137,207,232,251,1,20,72,131.0,252,236,4,72,199.0,198,252,253,
|
|
252,255.0,252,255.0,252,255.0,76,137,231,232,251,1,11,255,76,139,101,252,
|
|
248,72,137,252,236,93,194,236,255,85,72,137,229,65,84,65,85,73,137,252,252,
|
|
76,137,231,232,251,1,21,73,137,197,72,129.0,252,248,239,15.0,140,244,16,255,
|
|
15.0,143,244,17,255,72,193.0,224,4,72,41,196,72,129.0,252,236,239,255,72,
|
|
186,237,237,72,199.0,198,0,0.0,0.0,0.0,76,137,231,232,251,1,7,72,131.0,252,
|
|
236,16,255,72,185,237,237,72,199.0,194,237,72,199.0,198,237,76,137,231,232,
|
|
251,1,10,255,72,185,237,237,72,199.0,194,237,72,199.0,198,237,76,137,231,
|
|
232,251,1,22,255,72,185,237,237,72,199.0,194,237,72,199.0,198,237,76,137,
|
|
231,232,251,1,12,255,72,199.0,198,237,76,137,231,232,251,1,14,255,15.0,182,
|
|
192,255,15.0,190,192,255,15.0,183,192,255,15.0,191,192,255,72,199.0,198,237,
|
|
76,137,231,232,251,1,14,131.0,252,248,0,15.0,149.0,208,15.0,182,192,255,72,
|
|
199.0,198,237,76,137,231,232,251,1,13,255,72,199.0,198,237,76,137,231,232,
|
|
251,1,17,255,72,199.0,198,237,76,137,231,232,251,1,15,255,72,199.0,198,237,
|
|
76,137,231,232,251,1,16,255,72,199.0,198,237,76,137,231,232,251,1,18,255,
|
|
72,199.0,198,237,76,137,231,232,251,1,20,255,252,243.0,15.0,126,193,255,72,
|
|
141,132,253,36,233,72,131.0,252,236,4,72,199.0,194,237,76,137,230,72,137,
|
|
199,232,251,1,20,255,72,199.0,198,237,76,137,231,232,251,1,19,255,72,199.0,
|
|
198,237,76,137,231,232,251,1,19,137,4,36,217.0,4,36,255,137,20,36,217.0,4,
|
|
36,255,72,137,224,72,129.0,192,239,73,137,192,72,199.0,193,237,76,137,252,
|
|
234,72,199.0,198,237,76,137,231,232,251,1,23,255,72,137,224,72,129.0,192,
|
|
239,73,137,192,72,199.0,193,237,76,137,252,234,72,199.0,198,237,76,137,231,
|
|
232,251,1,24,255,72,137,224,72,129.0,192,239,73,137,193,73,199.0,192,237,
|
|
72,199.0,193,237,76,137,252,234,72,199.0,198,237,76,137,231,232,251,1,25,
|
|
255,72,185,237,237,139,1,72,137,199,232,251,1,26,255,72,131.0,196,32,255,
|
|
252,243.0,15.0,126,188,253,36,233,255,252,243.0,15.0,126,180,253,36,233,255,
|
|
252,243.0,15.0,126,172,253,36,233,255,252,243.0,15.0,126,164,253,36,233,255,
|
|
252,243.0,15.0,126,156,253,36,233,255,252,243.0,15.0,126,148,253,36,233,255,
|
|
252,243.0,15.0,126,140,253,36,233,255,252,243.0,15.0,126,132,253,36,233,255,
|
|
76,139,140,253,36,233,255,76,139,132,253,36,233,255,72,139,140,253,36,233,
|
|
255,72,139,148,253,36,233,255,72,139,180,253,36,233,255,72,139,60,36,255,
|
|
72,129.0,196,239,255,176,8,255,232,251,1,27,72,131.0,252,236,48,255,72,137,
|
|
68,36,32,232,251,1,0,72,185,237,237,137,1,72,186,237,237,72,199.0,198,237,
|
|
76,137,231,232,251,1,7,72,139,76,36,32,72,137,8,252,233,244,10,255,72,137,
|
|
68,36,32,232,251,1,0,72,185,237,237,137,1,72,186,237,237,72,199.0,198,0,0.0,
|
|
0.0,0.0,76,137,231,232,251,1,7,72,139,76,36,32,72,137,8,252,233,244,10,255,
|
|
102.0,15.0,214,68,36,32,232,251,1,0,72,185,237,237,137,1,72,186,237,237,72,
|
|
199.0,198,0,0.0,0.0,0.0,76,137,231,232,251,1,7,72,139,76,36,32,72,137,8,252,
|
|
233,244,10,255,102.0,15.0,214,76,36,40,102.0,15.0,214,68,36,32,232,251,1,
|
|
0,72,185,237,237,137,1,72,186,237,237,72,199.0,198,0,0.0,0.0,0.0,76,137,231,
|
|
232,251,1,7,72,139,76,36,40,72,137,72,8,72,139,76,36,32,72,137,8,252,233,
|
|
244,10,255,252,233,244,11,255,252,233,244,13,255,252,233,244,14,255,252,233,
|
|
244,15,255,252,243.0,15.0,90,192,252,233,244,12,255
|
|
};
|
|
|
|
static const char *const globnames[] = {
|
|
"lua_return_arg",
|
|
"lua_return_void",
|
|
"lua_return_double",
|
|
"lua_return_bool",
|
|
"lua_return_int",
|
|
"lua_return_uint",
|
|
"too_few_arguments",
|
|
"too_many_arguments",
|
|
"save_registers",
|
|
(const char *)0
|
|
};
|
|
static const char *const extnames[] = {
|
|
"GetLastError",
|
|
"lua_pushnumber",
|
|
"lua_pushboolean",
|
|
"push_int",
|
|
"push_uint",
|
|
"luaL_error",
|
|
"lua_rawgeti",
|
|
"push_cdata",
|
|
"lua_remove",
|
|
"lua_callk",
|
|
"check_typed_pointer",
|
|
"lua_settop",
|
|
"check_enum",
|
|
"check_uint32",
|
|
"check_int32",
|
|
"check_uint64",
|
|
"check_int64",
|
|
"check_uintptr",
|
|
"check_double",
|
|
"check_complex_float",
|
|
"check_complex_double",
|
|
"lua_gettop",
|
|
"check_typed_cfunction",
|
|
"unpack_varargs_float",
|
|
"unpack_varargs_int",
|
|
"unpack_varargs_stack_skip",
|
|
"SetLastError",
|
|
"FUNCTION",
|
|
(const char *)0
|
|
};
|
|
|
|
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define JUMP_SIZE 14
|
|
#else
|
|
#define JUMP_SIZE 4
|
|
#endif
|
|
|
|
#define MIN_BRANCH INT32_MIN
|
|
#define MAX_BRANCH INT32_MAX
|
|
#define BRANCH_OFF 4
|
|
|
|
static void compile_extern_jump(struct jit* jit, lua_State* L, cfunction func, uint8_t* code)
|
|
{
|
|
/* The jump code is the function pointer followed by a stub to call the
|
|
* function pointer. The stub exists in 64 bit so we can jump to functions
|
|
* with an offset greater than 2 GB.
|
|
*
|
|
* Note we have to manually set this up since there are commands buffered
|
|
* in the jit state and dynasm doesn't support rip relative addressing.
|
|
*
|
|
* eg on 64 bit:
|
|
* 0-8: function ptr
|
|
* 8-14: jmp aword [rip-14]
|
|
*
|
|
* for 32 bit we only set the function ptr as it can always fit in a 32
|
|
* bit displacement
|
|
*/
|
|
#if defined _WIN64 || defined __amd64__
|
|
*(cfunction*) code = func;
|
|
code[8] = 0xFF; /* FF /4 operand for jmp */
|
|
code[9] = 0x25; /* RIP displacement */
|
|
*(int32_t*) &code[10] = -14;
|
|
#else
|
|
*(cfunction*) code = func;
|
|
#endif
|
|
}
|
|
|
|
void compile_globals(struct jit* jit, lua_State* L)
|
|
{
|
|
struct jit* Dst = jit;
|
|
int* perr = &jit->last_errno;
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
/* Note: since the return code uses EBP to reset the stack pointer, we
|
|
* don't have to track the amount of stack space used. It also means we
|
|
* can handle stdcall and cdecl with the same code.
|
|
*/
|
|
|
|
/* Note the various call_* functions want 32 bytes of 16 byte aligned
|
|
* stack
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* the general idea for the return functions is:
|
|
* 1) Save return value on stack
|
|
* 2) Call get_errno (this trashes the registers hence #1)
|
|
* 3) Unpack return value from stack
|
|
* 4) Call lua push function
|
|
* 5) Set eax to number of returned args (0 or 1)
|
|
* 6) Call return which pops our stack frame
|
|
*/
|
|
|
|
dasm_put(Dst, 0);
|
|
|
|
dasm_put(Dst, 24, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 58, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 95, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 133, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 168, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
dasm_put(Dst, 203, (unsigned int)((uintptr_t)(&"too few arguments")), (unsigned int)(((uintptr_t)(&"too few arguments"))>>32));
|
|
|
|
dasm_put(Dst, 221, (unsigned int)((uintptr_t)(&"too many arguments")), (unsigned int)(((uintptr_t)(&"too many arguments"))>>32));
|
|
|
|
dasm_put(Dst, 239);
|
|
|
|
compile(Dst, L, NULL, LUA_NOREF);
|
|
}
|
|
|
|
int x86_return_size(lua_State* L, int usr, const struct ctype* ct)
|
|
{
|
|
int ret = 0;
|
|
const struct ctype* mt;
|
|
|
|
if (ct->calling_convention != C_CALL) {
|
|
size_t i;
|
|
size_t argn = lua_rawlen(L, usr);
|
|
for (i = 1; i <= argn; i++) {
|
|
lua_rawgeti(L, usr, (int) i);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
ret += sizeof(void*);
|
|
} else {
|
|
switch (mt->type) {
|
|
case DOUBLE_TYPE:
|
|
case COMPLEX_FLOAT_TYPE:
|
|
case INT64_TYPE:
|
|
ret += 8;
|
|
break;
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
ret += 16;
|
|
break;
|
|
case INTPTR_TYPE:
|
|
ret += sizeof(intptr_t);
|
|
break;
|
|
case FUNCTION_PTR_TYPE:
|
|
ret += sizeof(cfunction);
|
|
break;
|
|
case BOOL_TYPE:
|
|
case FLOAT_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
ret += 4;
|
|
break;
|
|
default:
|
|
return luaL_error(L, "NYI - argument type");
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
ret += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
#define MAX_REGISTERS(ct) 4 /* rcx, rdx, r8, r9 */
|
|
|
|
#elif defined __amd64__
|
|
#define MAX_INT_REGISTERS(ct) 6 /* rdi, rsi, rdx, rcx, r8, r9 */
|
|
#define MAX_FLOAT_REGISTERS(ct) 8 /* xmm0-7 */
|
|
|
|
#else
|
|
#define MAX_INT_REGISTERS(ct) ((ct)->calling_convention == FAST_CALL ? 2 /* ecx, edx */ : 0)
|
|
#define MAX_FLOAT_REGISTERS(ct) 0
|
|
#endif
|
|
|
|
struct reg_alloc {
|
|
#ifdef _WIN64
|
|
int regs;
|
|
int is_float[4];
|
|
int is_int[4];
|
|
#else
|
|
int floats;
|
|
int ints;
|
|
#endif
|
|
int off;
|
|
};
|
|
|
|
#ifdef _WIN64
|
|
#define REGISTER_STACK_SPACE(ct) (4*8)
|
|
#elif defined __amd64__
|
|
#define REGISTER_STACK_SPACE(ct) (14*8)
|
|
#else
|
|
#define REGISTER_STACK_SPACE(ct) LUAFFI_ALIGN_UP(((ct)->calling_convention == FAST_CALL ? 2*4 : 0), 15)
|
|
#endif
|
|
|
|
/* Fastcall:
|
|
* Uses ecx, edx as first two int registers
|
|
* Everything else on stack (include 64bit ints)
|
|
* No overflow stack space
|
|
* Pops the stack before returning
|
|
* Returns int in eax, float in ST0
|
|
* We use the same register allocation logic as posix x64 with 2 int regs and 0 float regs
|
|
*/
|
|
|
|
static void LUAFFI_get_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
/* grab the register from the shadow space */
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 308, 16 + 8*reg->regs);
|
|
reg->regs++;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 308, - 80 - 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 309, - 8 - 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 308, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 309, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void add_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 313, 32 + 8*(reg->regs));
|
|
reg->is_int[reg->regs++] = 1;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 313, 32 + 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 313, 32 + 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 313, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 314, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void get_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 320, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 324, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
int off;
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->regs;
|
|
reg->regs++;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->floats;
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
else {
|
|
off = reg->off;
|
|
reg->off += is_double ? 8 : 4;
|
|
}
|
|
|
|
if (is_double) {
|
|
dasm_put(Dst, 328, off);
|
|
} else {
|
|
dasm_put(Dst, 335, off);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void add_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 342, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 348, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 354, 32 + 8*(reg->regs));
|
|
} else {
|
|
dasm_put(Dst, 362, 32 + 8*(reg->regs));
|
|
}
|
|
reg->is_float[reg->regs++] = 1;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 354, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
} else {
|
|
dasm_put(Dst, 362, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
}
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_double) {
|
|
dasm_put(Dst, 354, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 375, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 1)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 1)
|
|
#else
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 0)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 0)
|
|
#endif
|
|
|
|
cfunction compile_callback(lua_State* L, int fidx, int ct_usr, const struct ctype* ct)
|
|
{
|
|
int i, nargs;
|
|
cfunction* pf;
|
|
struct ctype ct2 = *ct;
|
|
const struct ctype* mt;
|
|
struct reg_alloc reg;
|
|
int num_upvals = 0;
|
|
int top = lua_gettop(L);
|
|
struct jit* Dst = get_jit(L);
|
|
int ref;
|
|
int hidden_arg_off = 0;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
fidx = lua_absindex(L, fidx);
|
|
|
|
assert(lua_isnil(L, fidx) || lua_isfunction(L, fidx));
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
#ifdef _WIN64
|
|
reg.off = 16 + REGISTER_STACK_SPACE(ct); /* stack registers are above the shadow space */
|
|
#elif __amd64__
|
|
reg.off = 16;
|
|
#else
|
|
reg.off = 8;
|
|
#endif
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
// add a table to store ctype and function upvalues
|
|
// callback_set assumes the first value is the lua function
|
|
nargs = (int) lua_rawlen(L, ct_usr);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1);
|
|
ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
if (ct->has_var_arg) {
|
|
luaL_error(L, "can't create callbacks with varargs");
|
|
}
|
|
|
|
// setup a stack frame to hold args for the call into lua_call
|
|
|
|
dasm_put(Dst, 388, 8 + 16 + 32 + REGISTER_STACK_SPACE(ct));
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
}
|
|
|
|
// hardcode the lua_State* value into the assembly
|
|
dasm_put(Dst, 403, (unsigned int)((uintptr_t)(L)), (unsigned int)(((uintptr_t)(L))>>32));
|
|
|
|
/* get the upval table */
|
|
dasm_put(Dst, 408, ref, LUA_REGISTRYINDEX);
|
|
|
|
/* get the lua function */
|
|
lua_pushvalue(L, fidx);
|
|
lua_rawseti(L, -2, ++num_upvals);
|
|
assert(num_upvals == CALLBACK_FUNC_USR_IDX);
|
|
dasm_put(Dst, 424, num_upvals);
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
hidden_arg_off = reg.off;
|
|
reg.off += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#else
|
|
(void) hidden_arg_off;
|
|
#endif
|
|
|
|
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 */
|
|
/* on the lua stack in the callback:
|
|
* upval tbl, lua func, i-1 args
|
|
*/
|
|
dasm_put(Dst, 447, num_upvals-1, -i-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 485);
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 507, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
LUAFFI_get_int(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 526);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 507, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 526);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if defined _WIN64 || defined __amd64__
|
|
/* complex floats are two floats packed into a double */
|
|
dasm_put(Dst, 507, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 530);
|
|
#else
|
|
/* complex floats are real followed by imag on the stack */
|
|
dasm_put(Dst, 507, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 535);
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 538);
|
|
#endif
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 507, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
/* real */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 530);
|
|
/* imag */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 542);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
get_float(Dst, ct, ®, mt->type == DOUBLE_TYPE);
|
|
dasm_put(Dst, 548);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 556);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 570);
|
|
} else {
|
|
dasm_put(Dst, 574);
|
|
}
|
|
dasm_put(Dst, 578);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 589);
|
|
} else {
|
|
dasm_put(Dst, 593);
|
|
}
|
|
dasm_put(Dst, 578);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 597);
|
|
} else {
|
|
dasm_put(Dst, 578);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
dasm_put(Dst, 608, (unsigned int)((uintptr_t)(0)), (unsigned int)(((uintptr_t)(0))>>32), (mt->pointers || mt->type != VOID_TYPE) ? 1 : 0, nargs);
|
|
|
|
// Unpack the return argument if not "void", also clean-up the lua stack
|
|
// to remove the return argument and bind table. Use lua_settop rather
|
|
// than lua_pop as lua_pop is implemented as a macro.
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
dasm_put(Dst, 628, num_upvals-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
|
|
} 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 */
|
|
dasm_put(Dst, 712, num_upvals-1, (unsigned int)((uintptr_t)(mt)), (unsigned int)(((uintptr_t)(mt))>>32));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 794);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 813);
|
|
} else {
|
|
dasm_put(Dst, 832);
|
|
}
|
|
dasm_put(Dst, 851);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
lua_pop(L, 1);
|
|
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 878);
|
|
} else {
|
|
dasm_put(Dst, 897);
|
|
}
|
|
|
|
dasm_put(Dst, 916);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 945);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 992);
|
|
if (mt->type == FLOAT_TYPE) {
|
|
dasm_put(Dst, 1035);
|
|
} else {
|
|
dasm_put(Dst, 1043);
|
|
}
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit complex floats are two floats packed into a double,
|
|
* on 32 bit returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1051);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored which is popped by the called
|
|
* function */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1101);
|
|
#else
|
|
dasm_put(Dst, 1164, hidden_arg_off);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback return type");
|
|
}
|
|
}
|
|
|
|
dasm_put(Dst, 1213, x86_return_size(L, ct_usr, ct));
|
|
|
|
lua_pop(L, 1); /* upval table - already in registry */
|
|
assert(lua_gettop(L) == top);
|
|
|
|
ct2.is_jitted = 1;
|
|
pf = (cfunction*) push_cdata(L, ct_usr, &ct2);
|
|
*pf = compile(Dst, L, NULL, ref);
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
return *pf;
|
|
}
|
|
|
|
void compile_function(lua_State* L, cfunction func, int ct_usr, const struct ctype* ct)
|
|
{
|
|
size_t i, nargs;
|
|
int num_upvals;
|
|
const struct ctype* mbr_ct;
|
|
struct jit* Dst = get_jit(L);
|
|
struct reg_alloc reg;
|
|
void* p;
|
|
int top = lua_gettop(L);
|
|
int* perr = &Dst->last_errno;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
reg.off = 32 + REGISTER_STACK_SPACE(ct);
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
p = push_cdata(L, ct_usr, ct);
|
|
*(cfunction*) p = func;
|
|
num_upvals = 1;
|
|
|
|
nargs = lua_rawlen(L, ct_usr);
|
|
|
|
if (ct->calling_convention != C_CALL && ct->has_var_arg) {
|
|
luaL_error(L, "vararg is only allowed with the c calling convention");
|
|
}
|
|
|
|
dasm_put(Dst, 1226, nargs);
|
|
|
|
if (!ct->has_var_arg) {
|
|
dasm_put(Dst, 1258);
|
|
}
|
|
|
|
/* no need to zero extend eax returned by lua_gettop to rax as x86-64
|
|
* preguarentees that the upper 32 bits will be zero */
|
|
dasm_put(Dst, 1263, 32 + REGISTER_STACK_SPACE(ct));
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
/* Returned complex doubles require a hidden first parameter where the
|
|
* data is stored, which is popped by the calling code. */
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mbr_ct->pointers && mbr_ct->type == COMPLEX_DOUBLE_TYPE) {
|
|
/* we can allocate more space for arguments as long as no add_*
|
|
* function has been called yet, mbr_ct will be added as an upvalue in
|
|
* the return processing later */
|
|
dasm_put(Dst, 1276, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
add_pointer(Dst, ct, ®);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
for (i = 1; i <= nargs; i++) {
|
|
lua_rawgeti(L, ct_usr, (int) i);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1300, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1320, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1340, (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals), i);
|
|
add_int(Dst, ct, ®, 0);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
dasm_put(Dst, 1360, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1372);
|
|
} else {
|
|
dasm_put(Dst, 1376);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
dasm_put(Dst, 1360, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1380);
|
|
} else {
|
|
dasm_put(Dst, 1384);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
dasm_put(Dst, 1388, i);
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1410, i);
|
|
} else {
|
|
dasm_put(Dst, 1360, i);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
dasm_put(Dst, 1422, i);
|
|
add_pointer(Dst, ct, ®);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1434, i);
|
|
} else {
|
|
dasm_put(Dst, 1446, i);
|
|
}
|
|
add_int(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
dasm_put(Dst, 1458, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored (this is popped by the called
|
|
* function) */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1470, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 1482);
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
dasm_put(Dst, 1488, reg.off, i);
|
|
reg.off += 16;
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
dasm_put(Dst, 1458, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1514, i);
|
|
/* complex floats are two floats packed into a double */
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
/* returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1526, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 1544);
|
|
add_float(Dst, ct, ®, 0);
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ct->has_var_arg) {
|
|
#ifdef _WIN64
|
|
if (reg.regs < MAX_REGISTERS(ct)) {
|
|
assert(reg.regs == nargs);
|
|
} else {
|
|
}
|
|
|
|
for (i = nargs; i < MAX_REGISTERS(ct); i++) {
|
|
reg.is_int[i] = reg.is_float[i] = 1;
|
|
}
|
|
reg.regs = MAX_REGISTERS(ct);
|
|
#elif defined __amd64__
|
|
if (reg.floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 1551, 32 + 8*(MAX_INT_REGISTERS(ct) + reg.floats), MAX_FLOAT_REGISTERS(ct) - reg.floats, nargs+1);
|
|
}
|
|
|
|
if (reg.ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 1581, 32 + 8*(reg.ints), MAX_INT_REGISTERS(ct) - reg.ints, nargs+1);
|
|
}
|
|
|
|
dasm_put(Dst, 1611, reg.off, MAX_FLOAT_REGISTERS(ct) - reg.floats, MAX_INT_REGISTERS(ct) - reg.ints, nargs+1);
|
|
|
|
reg.floats = MAX_FLOAT_REGISTERS(ct);
|
|
reg.ints = MAX_INT_REGISTERS(ct);
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
dasm_put(Dst, 1645, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32));
|
|
|
|
/* remove the stack space to call local functions */
|
|
dasm_put(Dst, 1659);
|
|
|
|
#ifdef _WIN64
|
|
switch (reg.regs) {
|
|
case 4:
|
|
if (reg.is_float[3]) {
|
|
}
|
|
if (reg.is_int[3]) {
|
|
}
|
|
case 3:
|
|
if (reg.is_float[2]) {
|
|
}
|
|
if (reg.is_int[2]) {
|
|
}
|
|
case 2:
|
|
if (reg.is_float[1]) {
|
|
}
|
|
if (reg.is_int[1]) {
|
|
}
|
|
case 1:
|
|
if (reg.is_float[0]) {
|
|
}
|
|
if (reg.is_int[0]) {
|
|
}
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
/* don't remove the space for the registers as we need 32 bytes of register overflow space */
|
|
assert(REGISTER_STACK_SPACE(ct) == 32);
|
|
|
|
#elif defined __amd64__
|
|
switch (reg.floats) {
|
|
case 8:
|
|
dasm_put(Dst, 1664, 8*(MAX_INT_REGISTERS(ct)+7));
|
|
case 7:
|
|
dasm_put(Dst, 1673, 8*(MAX_INT_REGISTERS(ct)+6));
|
|
case 6:
|
|
dasm_put(Dst, 1682, 8*(MAX_INT_REGISTERS(ct)+5));
|
|
case 5:
|
|
dasm_put(Dst, 1691, 8*(MAX_INT_REGISTERS(ct)+4));
|
|
case 4:
|
|
dasm_put(Dst, 1700, 8*(MAX_INT_REGISTERS(ct)+3));
|
|
case 3:
|
|
dasm_put(Dst, 1709, 8*(MAX_INT_REGISTERS(ct)+2));
|
|
case 2:
|
|
dasm_put(Dst, 1718, 8*(MAX_INT_REGISTERS(ct)+1));
|
|
case 1:
|
|
dasm_put(Dst, 1727, 8*(MAX_INT_REGISTERS(ct)));
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
switch (reg.ints) {
|
|
case 6:
|
|
dasm_put(Dst, 1736, 8*5);
|
|
case 5:
|
|
dasm_put(Dst, 1743, 8*4);
|
|
case 4:
|
|
dasm_put(Dst, 1750, 8*3);
|
|
case 3:
|
|
dasm_put(Dst, 1757, 8*2);
|
|
case 2:
|
|
dasm_put(Dst, 1764, 8*1);
|
|
case 1:
|
|
dasm_put(Dst, 1771);
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
dasm_put(Dst, 1776, REGISTER_STACK_SPACE(ct));
|
|
#else
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
switch (reg.ints) {
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef __amd64__
|
|
if (ct->has_var_arg) {
|
|
/* al stores an upper limit on the number of float register, note that
|
|
* its allowed to be more than the actual number of float registers used as
|
|
* long as its 0-8 */
|
|
dasm_put(Dst, 1781);
|
|
}
|
|
#endif
|
|
|
|
dasm_put(Dst, 1784);
|
|
|
|
/* note on windows X86 the stack may be only aligned to 4 (stdcall will
|
|
* have popped a multiple of 4 bytes), but we don't need 16 byte alignment on
|
|
* that platform
|
|
*/
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers || mbr_ct->type == INTPTR_TYPE) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1794, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals));
|
|
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1794, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32), lua_upvalueindex(num_upvals));
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1837, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1883, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1930, (unsigned int)((uintptr_t)(perr)), (unsigned int)(((uintptr_t)(perr))>>32), (unsigned int)((uintptr_t)(mbr_ct)), (unsigned int)(((uintptr_t)(mbr_ct))>>32));
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1992);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1997);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1372);
|
|
} else {
|
|
dasm_put(Dst, 1376);
|
|
}
|
|
dasm_put(Dst, 2002);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1380);
|
|
} else {
|
|
dasm_put(Dst, 1384);
|
|
}
|
|
dasm_put(Dst, 2002);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 2007);
|
|
} else {
|
|
dasm_put(Dst, 2002);
|
|
}
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 2012);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 2017);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call return type");
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + num_upvals);
|
|
{
|
|
cfunction f = compile(Dst, L, func, LUA_NOREF);
|
|
/* add a callback as an upval so that the jitted code gets cleaned up when
|
|
* the function gets gc'd */
|
|
push_callback(L, f);
|
|
lua_pushcclosure(L, (lua_CFunction) f, num_upvals+1);
|
|
}
|
|
}
|
|
|
|
#else
|
|
/*
|
|
** This file has been pre-processed with DynASM.
|
|
** http://luajit.org/dynasm.html
|
|
** DynASM version 1.3.0, DynASM x86 version 1.3.0
|
|
** DO NOT EDIT! The original file is in "call_x86.dasc".
|
|
*/
|
|
|
|
#if DASM_VERSION != 10300
|
|
#error "Version mismatch between DynASM and included encoding engine"
|
|
#endif
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
|
|
static const unsigned char build_actionlist[1803] = {
|
|
248,10,184,1,0.0,0.0,0.0,139,117,252,248,139,125,252,252,137,252,236,93,195,
|
|
255,248,11,232,251,1,0,185,237,137,1,184,0,0.0,0.0,0.0,139,117,252,248,139,
|
|
125,252,252,137,252,236,93,195,255,248,12,221.0,92,36,4,232,251,1,0,185,237,
|
|
137,1,137,60,36,232,251,1,1,252,233,244,10,255,248,13,15.0,182,192,137,68,
|
|
36,32,232,251,1,0,185,237,137,1,139,68,36,32,137,68,36,4,137,60,36,232,251,
|
|
1,2,252,233,244,10,255,248,14,137,68,36,32,232,251,1,0,185,237,137,1,139,
|
|
68,36,32,137,68,36,4,137,60,36,232,251,1,3,252,233,244,10,255,248,15,137,
|
|
68,36,32,232,251,1,0,185,237,137,1,139,68,36,32,137,68,36,4,137,60,36,232,
|
|
251,1,4,252,233,244,10,255,248,16,102,184,0,0.0,199.0,68,36,4,237,137,60,
|
|
36,232,251,1,5,255,248,17,102,184,0,0.0,199.0,68,36,4,237,137,60,36,232,251,
|
|
1,5,255,248,18,137,77,252,248,137,85,252,244,195,255,139,141,233,255,139,
|
|
141,233,139,149,233,255,137,132,253,36,233,255,137,132,253,36,233,137,148,
|
|
253,36,233,255,221.0,133,233,255,217.0,133,233,255,252,243.0,15.0,126,133,
|
|
233,255,252,243.0,15.0,90,133,233,255,221.0,156,253,36,233,255,217.0,156,
|
|
253,36,233,255,102.0,15.0,214,132,253,36,233,255,252,242.0,15.0,90,192,102.0,
|
|
15.0,214,132,253,36,233,255,252,242.0,15.0,90,192,102.0,15.0,126,132,253,
|
|
36,233,255,85,137,229,87,129.0,252,236,239,255,232,244,18,255,191,237,255,
|
|
199.0,68,36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,6,255,199.0,68,36,
|
|
8,237,199.0,68,36,4,252,255,252,255.0,252,255.0,252,255.0,137,60,36,232,251,
|
|
1,6,255,199.0,68,36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,6,199.0,68,
|
|
36,8,237,199.0,68,36,4,252,255,252,255.0,252,255.0,252,255.0,137,60,36,232,
|
|
251,1,7,255,137,8,199.0,68,36,4,252,254,252,255.0,252,255.0,252,255.0,137,
|
|
60,36,232,251,1,8,255,199.0,68,36,8,237,199.0,68,36,4,0,0.0,0.0,0.0,137,60,
|
|
36,232,251,1,7,255,137,8,137,80,4,255,137,8,255,102.0,15.0,214,0,255,217.0,
|
|
24,255,217.0,88,4,255,221.0,24,255,221.0,88,8,255,221.0,92,36,4,137,60,36,
|
|
232,251,1,1,255,15.0,182,201,137,76,36,4,137,60,36,232,251,1,2,255,15.0,182,
|
|
201,255,15.0,190,201,255,137,76,36,4,137,60,36,232,251,1,3,255,15.0,183,201,
|
|
255,15.0,191,201,255,137,76,36,4,137,60,36,232,251,1,4,255,199.0,68,36,12,
|
|
0,0.0,0.0,0.0,199.0,68,36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,9,255,
|
|
199.0,68,36,8,237,199.0,68,36,4,252,254,252,255.0,252,255.0,252,255.0,137,
|
|
60,36,232,251,1,6,199.0,68,36,12,237,199.0,68,36,8,252,255,252,255.0,252,
|
|
255.0,252,255.0,199.0,68,36,4,252,254,252,255.0,252,255.0,252,255.0,137,60,
|
|
36,232,251,1,10,137,68,36,32,199.0,68,36,4,252,252,252,255.0,252,255.0,252,
|
|
255.0,137,60,36,232,251,1,11,139,68,36,32,255,199.0,68,36,8,237,199.0,68,
|
|
36,4,252,254,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,6,199.0,68,
|
|
36,12,237,199.0,68,36,8,252,255,252,255.0,252,255.0,252,255.0,199.0,68,36,
|
|
4,252,254,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,12,137,68,36,
|
|
32,199.0,68,36,4,252,252,252,255.0,252,255.0,252,255.0,137,60,36,232,251,
|
|
1,11,139,68,36,32,255,199.0,68,36,4,252,254,252,255.0,252,255.0,252,255.0,
|
|
137,60,36,232,251,1,11,255,199.0,68,36,4,252,255,252,255.0,252,255.0,252,
|
|
255.0,137,60,36,232,251,1,13,255,199.0,68,36,4,252,255,252,255.0,252,255.0,
|
|
252,255.0,137,60,36,232,251,1,14,255,137,68,36,32,199.0,68,36,4,252,253,252,
|
|
255.0,252,255.0,252,255.0,137,60,36,232,251,1,11,139,68,36,32,255,199.0,68,
|
|
36,4,252,255,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,15,255,199.0,
|
|
68,36,4,252,255,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,16,255,
|
|
137,68,36,32,137,84,36,36,199.0,68,36,4,252,253,252,255.0,252,255.0,252,255.0,
|
|
137,60,36,232,251,1,11,139,68,36,32,139,84,36,36,255,199.0,68,36,4,252,255,
|
|
252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,17,137,68,36,32,199.0,68,
|
|
36,4,252,253,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,11,139,68,
|
|
36,32,255,199.0,68,36,4,252,255,252,255.0,252,255.0,252,255.0,137,60,36,232,
|
|
251,1,18,255,221.0,92,36,32,199.0,68,36,4,252,253,252,255.0,252,255.0,252,
|
|
255.0,137,60,36,232,251,1,11,221.0,68,36,32,255,199.0,68,36,4,252,255,252,
|
|
255.0,252,255.0,252,255.0,137,60,36,232,251,1,19,137,68,36,32,137,84,36,36,
|
|
199.0,68,36,4,252,253,252,255.0,252,255.0,252,255.0,137,60,36,232,251,1,11,
|
|
139,68,36,32,139,84,36,36,255,199.0,68,36,4,252,255,252,255.0,252,255.0,252,
|
|
255.0,137,60,36,232,251,1,20,102.0,15.0,214,68,36,32,102.0,15.0,214,76,36,
|
|
40,199.0,68,36,4,252,253,252,255.0,252,255.0,252,255.0,137,60,36,232,251,
|
|
1,11,252,243.0,15.0,126,68,36,32,252,243.0,15.0,126,76,36,40,255,139,141,
|
|
233,199.0,68,36,8,252,255,252,255.0,252,255.0,252,255.0,137,124,36,4,137,
|
|
12,36,232,251,1,20,131.0,252,236,4,199.0,68,36,4,252,253,252,255.0,252,255.0,
|
|
252,255.0,137,60,36,232,251,1,11,255,139,125,252,252,137,252,236,93,194,236,
|
|
255,85,137,229,87,86,139,189,233,131.0,252,236,16,137,60,36,232,251,1,21,
|
|
137,198,129.0,252,248,239,15.0,140,244,16,255,15.0,143,244,17,255,193.0,224,
|
|
4,41,196,129.0,252,236,239,255,199.0,68,36,8,237,199.0,68,36,4,0,0.0,0.0,
|
|
0.0,137,60,36,232,251,1,7,131.0,252,236,16,255,199.0,68,36,12,237,199.0,68,
|
|
36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,10,255,199.0,68,36,12,237,
|
|
199.0,68,36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,22,255,199.0,68,36,
|
|
12,237,199.0,68,36,8,237,199.0,68,36,4,237,137,60,36,232,251,1,12,255,199.0,
|
|
68,36,4,237,137,60,36,232,251,1,14,255,15.0,182,192,255,15.0,190,192,255,
|
|
15.0,183,192,255,15.0,191,192,255,199.0,68,36,4,237,137,60,36,232,251,1,14,
|
|
131.0,252,248,0,15.0,149.0,208,15.0,182,192,255,199.0,68,36,4,237,137,60,
|
|
36,232,251,1,13,255,199.0,68,36,4,237,137,60,36,232,251,1,17,255,199.0,68,
|
|
36,4,237,137,60,36,232,251,1,15,255,199.0,68,36,4,237,137,60,36,232,251,1,
|
|
16,255,199.0,68,36,4,237,137,60,36,232,251,1,18,255,199.0,68,36,4,237,137,
|
|
60,36,232,251,1,20,255,252,243.0,15.0,126,193,255,141,132,253,36,233,131.0,
|
|
252,236,4,199.0,68,36,8,237,137,124,36,4,137,4,36,232,251,1,20,255,199.0,
|
|
68,36,4,237,137,60,36,232,251,1,19,255,199.0,68,36,4,237,137,60,36,232,251,
|
|
1,19,137,4,36,217.0,4,36,255,137,20,36,217.0,4,36,255,137,224,129.0,192,239,
|
|
137,68,36,12,137,116,36,8,199.0,68,36,4,237,137,60,36,232,251,1,23,255,185,
|
|
237,139,1,137,4,36,232,251,1,24,255,131.0,196,28,255,139,148,253,36,233,255,
|
|
139,12,36,255,129.0,196,239,255,232,251,1,25,131.0,252,236,48,255,137,68,
|
|
36,32,232,251,1,0,185,237,137,1,199.0,68,36,8,237,199.0,68,36,4,237,137,60,
|
|
36,232,251,1,7,139,76,36,32,137,8,252,233,244,10,255,137,84,36,36,137,68,
|
|
36,32,232,251,1,0,185,237,137,1,199.0,68,36,8,237,199.0,68,36,4,0,0.0,0.0,
|
|
0.0,137,60,36,232,251,1,7,139,76,36,36,139,84,36,32,137,72,4,137,16,252,233,
|
|
244,10,255,137,68,36,32,137,84,36,36,232,251,1,0,185,237,137,1,199.0,68,36,
|
|
8,237,199.0,68,36,4,0,0.0,0.0,0.0,137,60,36,232,251,1,7,139,76,36,32,137,
|
|
8,139,76,36,36,137,72,4,252,233,244,10,255,131.0,252,236,4,232,251,1,0,185,
|
|
237,137,1,252,233,244,10,255,252,233,244,11,255,252,233,244,13,255,252,233,
|
|
244,14,255,252,233,244,15,255,252,233,244,12,255
|
|
};
|
|
|
|
static const char *const globnames[] = {
|
|
"lua_return_arg",
|
|
"lua_return_void",
|
|
"lua_return_double",
|
|
"lua_return_bool",
|
|
"lua_return_int",
|
|
"lua_return_uint",
|
|
"too_few_arguments",
|
|
"too_many_arguments",
|
|
"save_registers",
|
|
(const char *)0
|
|
};
|
|
static const char *const extnames[] = {
|
|
"GetLastError",
|
|
"lua_pushnumber",
|
|
"lua_pushboolean",
|
|
"push_int",
|
|
"push_uint",
|
|
"luaL_error",
|
|
"lua_rawgeti",
|
|
"push_cdata",
|
|
"lua_remove",
|
|
"lua_callk",
|
|
"check_typed_pointer",
|
|
"lua_settop",
|
|
"check_enum",
|
|
"check_uint32",
|
|
"check_int32",
|
|
"check_uint64",
|
|
"check_int64",
|
|
"check_uintptr",
|
|
"check_double",
|
|
"check_complex_float",
|
|
"check_complex_double",
|
|
"lua_gettop",
|
|
"check_typed_cfunction",
|
|
"unpack_varargs_stack",
|
|
"SetLastError",
|
|
"FUNCTION",
|
|
(const char *)0
|
|
};
|
|
|
|
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define JUMP_SIZE 14
|
|
#else
|
|
#define JUMP_SIZE 4
|
|
#endif
|
|
|
|
#define MIN_BRANCH INT32_MIN
|
|
#define MAX_BRANCH INT32_MAX
|
|
#define BRANCH_OFF 4
|
|
|
|
static void compile_extern_jump(struct jit* jit, lua_State* L, cfunction func, uint8_t* code)
|
|
{
|
|
/* The jump code is the function pointer followed by a stub to call the
|
|
* function pointer. The stub exists in 64 bit so we can jump to functions
|
|
* with an offset greater than 2 GB.
|
|
*
|
|
* Note we have to manually set this up since there are commands buffered
|
|
* in the jit state and dynasm doesn't support rip relative addressing.
|
|
*
|
|
* eg on 64 bit:
|
|
* 0-8: function ptr
|
|
* 8-14: jmp aword [rip-14]
|
|
*
|
|
* for 32 bit we only set the function ptr as it can always fit in a 32
|
|
* bit displacement
|
|
*/
|
|
#if defined _WIN64 || defined __amd64__
|
|
*(cfunction*) code = func;
|
|
code[8] = 0xFF; /* FF /4 operand for jmp */
|
|
code[9] = 0x25; /* RIP displacement */
|
|
*(int32_t*) &code[10] = -14;
|
|
#else
|
|
*(cfunction*) code = func;
|
|
#endif
|
|
}
|
|
|
|
void compile_globals(struct jit* jit, lua_State* L)
|
|
{
|
|
struct jit* Dst = jit;
|
|
int* perr = &jit->last_errno;
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
/* Note: since the return code uses EBP to reset the stack pointer, we
|
|
* don't have to track the amount of stack space used. It also means we
|
|
* can handle stdcall and cdecl with the same code.
|
|
*/
|
|
|
|
/* Note the various call_* functions want 32 bytes of 16 byte aligned
|
|
* stack
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* the general idea for the return functions is:
|
|
* 1) Save return value on stack
|
|
* 2) Call get_errno (this trashes the registers hence #1)
|
|
* 3) Unpack return value from stack
|
|
* 4) Call lua push function
|
|
* 5) Set eax to number of returned args (0 or 1)
|
|
* 6) Call return which pops our stack frame
|
|
*/
|
|
|
|
dasm_put(Dst, 0);
|
|
|
|
dasm_put(Dst, 21, perr);
|
|
|
|
dasm_put(Dst, 50, perr);
|
|
|
|
dasm_put(Dst, 76, perr);
|
|
|
|
dasm_put(Dst, 113, perr);
|
|
|
|
dasm_put(Dst, 147, perr);
|
|
|
|
dasm_put(Dst, 181, (ptrdiff_t)("too few arguments"));
|
|
|
|
dasm_put(Dst, 200, (ptrdiff_t)("too many arguments"));
|
|
|
|
dasm_put(Dst, 219);
|
|
|
|
compile(Dst, L, NULL, LUA_NOREF);
|
|
}
|
|
|
|
int x86_return_size(lua_State* L, int usr, const struct ctype* ct)
|
|
{
|
|
int ret = 0;
|
|
const struct ctype* mt;
|
|
|
|
if (ct->calling_convention != C_CALL) {
|
|
size_t i;
|
|
size_t argn = lua_rawlen(L, usr);
|
|
for (i = 1; i <= argn; i++) {
|
|
lua_rawgeti(L, usr, (int) i);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mt->pointers) {
|
|
ret += sizeof(void*);
|
|
} else {
|
|
switch (mt->type) {
|
|
case DOUBLE_TYPE:
|
|
case COMPLEX_FLOAT_TYPE:
|
|
case INT64_TYPE:
|
|
ret += 8;
|
|
break;
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
ret += 16;
|
|
break;
|
|
case INTPTR_TYPE:
|
|
ret += sizeof(intptr_t);
|
|
break;
|
|
case FUNCTION_PTR_TYPE:
|
|
ret += sizeof(cfunction);
|
|
break;
|
|
case BOOL_TYPE:
|
|
case FLOAT_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
ret += 4;
|
|
break;
|
|
default:
|
|
return luaL_error(L, "NYI - argument type");
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
ret += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
#define MAX_REGISTERS(ct) 4 /* rcx, rdx, r8, r9 */
|
|
|
|
#elif defined __amd64__
|
|
#define MAX_INT_REGISTERS(ct) 6 /* rdi, rsi, rdx, rcx, r8, r9 */
|
|
#define MAX_FLOAT_REGISTERS(ct) 8 /* xmm0-7 */
|
|
|
|
#else
|
|
#define MAX_INT_REGISTERS(ct) ((ct)->calling_convention == FAST_CALL ? 2 /* ecx, edx */ : 0)
|
|
#define MAX_FLOAT_REGISTERS(ct) 0
|
|
#endif
|
|
|
|
struct reg_alloc {
|
|
#ifdef _WIN64
|
|
int regs;
|
|
int is_float[4];
|
|
int is_int[4];
|
|
#else
|
|
int floats;
|
|
int ints;
|
|
#endif
|
|
int off;
|
|
};
|
|
|
|
#ifdef _WIN64
|
|
#define REGISTER_STACK_SPACE(ct) (4*8)
|
|
#elif defined __amd64__
|
|
#define REGISTER_STACK_SPACE(ct) (14*8)
|
|
#else
|
|
#define REGISTER_STACK_SPACE(ct) LUAFFI_ALIGN_UP(((ct)->calling_convention == FAST_CALL ? 2*4 : 0), 15)
|
|
#endif
|
|
|
|
/* Fastcall:
|
|
* Uses ecx, edx as first two int registers
|
|
* Everything else on stack (include 64bit ints)
|
|
* No overflow stack space
|
|
* Pops the stack before returning
|
|
* Returns int in eax, float in ST0
|
|
* We use the same register allocation logic as posix x64 with 2 int regs and 0 float regs
|
|
*/
|
|
|
|
static void LUAFFI_get_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
/* grab the register from the shadow space */
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 231, 16 + 8*reg->regs);
|
|
reg->regs++;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 231, - 80 - 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 231, - 8 - 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 235, reg->off, reg->off + 4);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 231, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void add_int(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_int64)
|
|
{
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
dasm_put(Dst, 242, 32 + 8*(reg->regs));
|
|
reg->is_int[reg->regs++] = 1;
|
|
}
|
|
#elif __amd64__
|
|
if (reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 242, 32 + 8*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#else
|
|
if (!is_int64 && reg->ints < MAX_INT_REGISTERS(ct)) {
|
|
dasm_put(Dst, 242, 32 + 4*reg->ints);
|
|
reg->ints++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_int64) {
|
|
dasm_put(Dst, 248, reg->off, reg->off + 4);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 242, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
}
|
|
|
|
static void get_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 259, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 263, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
int off;
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->regs;
|
|
reg->regs++;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
off = -16 - 8*reg->floats;
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
else {
|
|
off = reg->off;
|
|
reg->off += is_double ? 8 : 4;
|
|
}
|
|
|
|
if (is_double) {
|
|
dasm_put(Dst, 267, off);
|
|
} else {
|
|
dasm_put(Dst, 274, off);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void add_float(Dst_DECL, const struct ctype* ct, struct reg_alloc* reg, int is_double)
|
|
{
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
assert(MAX_FLOAT_REGISTERS(ct) == 0);
|
|
if (is_double) {
|
|
dasm_put(Dst, 281, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 287, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#else
|
|
|
|
#ifdef _WIN64
|
|
if (reg->regs < MAX_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 293, 32 + 8*(reg->regs));
|
|
} else {
|
|
dasm_put(Dst, 301, 32 + 8*(reg->regs));
|
|
}
|
|
reg->is_float[reg->regs++] = 1;
|
|
}
|
|
#else
|
|
if (reg->floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
if (is_double) {
|
|
dasm_put(Dst, 293, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
} else {
|
|
dasm_put(Dst, 301, 32 + 8*(MAX_INT_REGISTERS(ct) + reg->floats));
|
|
}
|
|
reg->floats++;
|
|
}
|
|
#endif
|
|
|
|
else if (is_double) {
|
|
dasm_put(Dst, 293, reg->off);
|
|
reg->off += 8;
|
|
} else {
|
|
dasm_put(Dst, 314, reg->off);
|
|
reg->off += 4;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined _WIN64 || defined __amd64__
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 1)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 1)
|
|
#else
|
|
#define add_pointer(jit, ct, reg) add_int(jit, ct, reg, 0)
|
|
#define get_pointer(jit, ct, reg) LUAFFI_get_int(jit, ct, reg, 0)
|
|
#endif
|
|
|
|
cfunction compile_callback(lua_State* L, int fidx, int ct_usr, const struct ctype* ct)
|
|
{
|
|
int i, nargs;
|
|
cfunction* pf;
|
|
struct ctype ct2 = *ct;
|
|
const struct ctype* mt;
|
|
struct reg_alloc reg;
|
|
int num_upvals = 0;
|
|
int top = lua_gettop(L);
|
|
struct jit* Dst = get_jit(L);
|
|
int ref;
|
|
int hidden_arg_off = 0;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
fidx = lua_absindex(L, fidx);
|
|
|
|
assert(lua_isnil(L, fidx) || lua_isfunction(L, fidx));
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
#ifdef _WIN64
|
|
reg.off = 16 + REGISTER_STACK_SPACE(ct); /* stack registers are above the shadow space */
|
|
#elif __amd64__
|
|
reg.off = 16;
|
|
#else
|
|
reg.off = 8;
|
|
#endif
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
// add a table to store ctype and function upvalues
|
|
// callback_set assumes the first value is the lua function
|
|
nargs = (int) lua_rawlen(L, ct_usr);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1);
|
|
ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
if (ct->has_var_arg) {
|
|
luaL_error(L, "can't create callbacks with varargs");
|
|
}
|
|
|
|
// setup a stack frame to hold args for the call into lua_call
|
|
|
|
dasm_put(Dst, 327, 4 + 16 + 32 + REGISTER_STACK_SPACE(ct));
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
dasm_put(Dst, 336);
|
|
}
|
|
|
|
// hardcode the lua_State* value into the assembly
|
|
dasm_put(Dst, 340, L);
|
|
|
|
/* get the upval table */
|
|
dasm_put(Dst, 343, ref, LUA_REGISTRYINDEX);
|
|
|
|
/* get the lua function */
|
|
lua_pushvalue(L, fidx);
|
|
lua_rawseti(L, -2, ++num_upvals);
|
|
assert(num_upvals == CALLBACK_FUNC_USR_IDX);
|
|
dasm_put(Dst, 361, num_upvals);
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mt->pointers && mt->type == COMPLEX_DOUBLE_TYPE) {
|
|
hidden_arg_off = reg.off;
|
|
reg.off += sizeof(void*);
|
|
}
|
|
lua_pop(L, 1);
|
|
#else
|
|
(void) hidden_arg_off;
|
|
#endif
|
|
|
|
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 */
|
|
/* on the lua stack in the callback:
|
|
* upval tbl, lua func, i-1 args
|
|
*/
|
|
dasm_put(Dst, 386, num_upvals-1, -i-1, mt);
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 428);
|
|
} else {
|
|
switch (mt->type) {
|
|
case INT64_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 450, mt);
|
|
LUAFFI_get_int(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 471);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* mt */
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 450, mt);
|
|
get_pointer(Dst, ct, ®);
|
|
dasm_put(Dst, 477);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if defined _WIN64 || defined __amd64__
|
|
/* complex floats are two floats packed into a double */
|
|
dasm_put(Dst, 450, mt);
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 480);
|
|
#else
|
|
/* complex floats are real followed by imag on the stack */
|
|
dasm_put(Dst, 450, mt);
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 485);
|
|
get_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 488);
|
|
#endif
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 450, mt);
|
|
/* real */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 492);
|
|
/* imag */
|
|
get_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 495);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
get_float(Dst, ct, ®, mt->type == DOUBLE_TYPE);
|
|
dasm_put(Dst, 499);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 511);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 526);
|
|
} else {
|
|
dasm_put(Dst, 530);
|
|
}
|
|
dasm_put(Dst, 534);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 546);
|
|
} else {
|
|
dasm_put(Dst, 550);
|
|
}
|
|
dasm_put(Dst, 534);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
LUAFFI_get_int(Dst, ct, ®, 0);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 554);
|
|
} else {
|
|
dasm_put(Dst, 534);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mt = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
dasm_put(Dst, 566, (mt->pointers || mt->type != VOID_TYPE) ? 1 : 0, nargs);
|
|
|
|
// Unpack the return argument if not "void", also clean-up the lua stack
|
|
// to remove the return argument and bind table. Use lua_settop rather
|
|
// than lua_pop as lua_pop is implemented as a macro.
|
|
if (mt->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
lua_rawseti(L, -3, ++num_upvals); /* usr value */
|
|
lua_rawseti(L, -2, ++num_upvals); /* mt */
|
|
dasm_put(Dst, 592, num_upvals-1, mt);
|
|
|
|
} 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 */
|
|
dasm_put(Dst, 680, num_upvals-1, mt);
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 768);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 788);
|
|
} else {
|
|
dasm_put(Dst, 808);
|
|
}
|
|
dasm_put(Dst, 828);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
lua_pop(L, 1);
|
|
|
|
if (mt->is_unsigned) {
|
|
dasm_put(Dst, 856);
|
|
} else {
|
|
dasm_put(Dst, 876);
|
|
}
|
|
|
|
dasm_put(Dst, 896);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 932);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 979);
|
|
if (mt->type == FLOAT_TYPE) {
|
|
} else {
|
|
}
|
|
dasm_put(Dst, 999);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit complex floats are two floats packed into a double,
|
|
* on 32 bit returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1027);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
#if !defined HAVE_COMPLEX
|
|
luaL_error(L, "ffi lib compiled without complex number support");
|
|
#endif
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored which is popped by the called
|
|
* function */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1082);
|
|
#else
|
|
dasm_put(Dst, 1147, hidden_arg_off);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: callback return type");
|
|
}
|
|
}
|
|
|
|
dasm_put(Dst, 1197, x86_return_size(L, ct_usr, ct));
|
|
|
|
lua_pop(L, 1); /* upval table - already in registry */
|
|
assert(lua_gettop(L) == top);
|
|
|
|
ct2.is_jitted = 1;
|
|
pf = (cfunction*) push_cdata(L, ct_usr, &ct2);
|
|
*pf = compile(Dst, L, NULL, ref);
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
return *pf;
|
|
}
|
|
|
|
void compile_function(lua_State* L, cfunction func, int ct_usr, const struct ctype* ct)
|
|
{
|
|
size_t i, nargs;
|
|
int num_upvals;
|
|
const struct ctype* mbr_ct;
|
|
struct jit* Dst = get_jit(L);
|
|
struct reg_alloc reg;
|
|
void* p;
|
|
int top = lua_gettop(L);
|
|
int* perr = &Dst->last_errno;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
memset(®, 0, sizeof(reg));
|
|
reg.off = 32 + REGISTER_STACK_SPACE(ct);
|
|
|
|
dasm_setup(Dst, build_actionlist);
|
|
|
|
p = push_cdata(L, ct_usr, ct);
|
|
*(cfunction*) p = func;
|
|
num_upvals = 1;
|
|
|
|
nargs = lua_rawlen(L, ct_usr);
|
|
|
|
if (ct->calling_convention != C_CALL && ct->has_var_arg) {
|
|
luaL_error(L, "vararg is only allowed with the c calling convention");
|
|
}
|
|
|
|
dasm_put(Dst, 1208, 8, nargs);
|
|
|
|
if (!ct->has_var_arg) {
|
|
dasm_put(Dst, 1238);
|
|
}
|
|
|
|
/* no need to zero extend eax returned by lua_gettop to rax as x86-64
|
|
* preguarentees that the upper 32 bits will be zero */
|
|
dasm_put(Dst, 1243, 32 + REGISTER_STACK_SPACE(ct));
|
|
|
|
#if !defined _WIN64 && !defined __amd64__
|
|
/* Returned complex doubles require a hidden first parameter where the
|
|
* data is stored, which is popped by the calling code. */
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
if (!mbr_ct->pointers && mbr_ct->type == COMPLEX_DOUBLE_TYPE) {
|
|
/* we can allocate more space for arguments as long as no add_*
|
|
* function has been called yet, mbr_ct will be added as an upvalue in
|
|
* the return processing later */
|
|
dasm_put(Dst, 1253, mbr_ct);
|
|
add_pointer(Dst, ct, ®);
|
|
}
|
|
lua_pop(L, 1);
|
|
#endif
|
|
|
|
for (i = 1; i <= nargs; i++) {
|
|
lua_rawgeti(L, ct_usr, (int) i);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1278, mbr_ct, lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1301, mbr_ct, lua_upvalueindex(num_upvals), i);
|
|
add_pointer(Dst, ct, ®);
|
|
break;
|
|
|
|
case ENUM_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1324, mbr_ct, lua_upvalueindex(num_upvals), i);
|
|
add_int(Dst, ct, ®, 0);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
dasm_put(Dst, 1347, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1360);
|
|
} else {
|
|
dasm_put(Dst, 1364);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
dasm_put(Dst, 1347, i);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1368);
|
|
} else {
|
|
dasm_put(Dst, 1372);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
dasm_put(Dst, 1376, i);
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1399, i);
|
|
} else {
|
|
dasm_put(Dst, 1347, i);
|
|
}
|
|
add_int(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
dasm_put(Dst, 1412, i);
|
|
add_pointer(Dst, ct, ®);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1425, i);
|
|
} else {
|
|
dasm_put(Dst, 1438, i);
|
|
}
|
|
add_int(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
dasm_put(Dst, 1451, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
/* on 64 bit, returned complex doubles use xmm0, xmm1, on 32 bit
|
|
* there is a hidden first parameter that points to 16 bytes where
|
|
* the returned arg is stored (this is popped by the called
|
|
* function) */
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1464, i);
|
|
add_float(Dst, ct, ®, 1);
|
|
dasm_put(Dst, 1477);
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
dasm_put(Dst, 1483, reg.off, i);
|
|
reg.off += 16;
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
dasm_put(Dst, 1451, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
#if defined _WIN64 || defined __amd64__
|
|
dasm_put(Dst, 1509, i);
|
|
/* complex floats are two floats packed into a double */
|
|
add_float(Dst, ct, ®, 1);
|
|
#else
|
|
/* returned complex floats use eax and edx */
|
|
dasm_put(Dst, 1522, i);
|
|
add_float(Dst, ct, ®, 0);
|
|
dasm_put(Dst, 1541);
|
|
add_float(Dst, ct, ®, 0);
|
|
#endif
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call arg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ct->has_var_arg) {
|
|
#ifdef _WIN64
|
|
if (reg.regs < MAX_REGISTERS(ct)) {
|
|
assert(reg.regs == nargs);
|
|
} else {
|
|
}
|
|
|
|
for (i = nargs; i < MAX_REGISTERS(ct); i++) {
|
|
reg.is_int[i] = reg.is_float[i] = 1;
|
|
}
|
|
reg.regs = MAX_REGISTERS(ct);
|
|
#elif defined __amd64__
|
|
if (reg.floats < MAX_FLOAT_REGISTERS(ct)) {
|
|
}
|
|
|
|
if (reg.ints < MAX_INT_REGISTERS(ct)) {
|
|
}
|
|
|
|
|
|
reg.floats = MAX_FLOAT_REGISTERS(ct);
|
|
reg.ints = MAX_INT_REGISTERS(ct);
|
|
#else
|
|
dasm_put(Dst, 1548, reg.off, nargs+1);
|
|
#endif
|
|
}
|
|
|
|
dasm_put(Dst, 1574, perr);
|
|
|
|
/* remove the stack space to call local functions */
|
|
dasm_put(Dst, 1586);
|
|
|
|
#ifdef _WIN64
|
|
switch (reg.regs) {
|
|
case 4:
|
|
if (reg.is_float[3]) {
|
|
}
|
|
if (reg.is_int[3]) {
|
|
}
|
|
case 3:
|
|
if (reg.is_float[2]) {
|
|
}
|
|
if (reg.is_int[2]) {
|
|
}
|
|
case 2:
|
|
if (reg.is_float[1]) {
|
|
}
|
|
if (reg.is_int[1]) {
|
|
}
|
|
case 1:
|
|
if (reg.is_float[0]) {
|
|
}
|
|
if (reg.is_int[0]) {
|
|
}
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
/* don't remove the space for the registers as we need 32 bytes of register overflow space */
|
|
assert(REGISTER_STACK_SPACE(ct) == 32);
|
|
|
|
#elif defined __amd64__
|
|
switch (reg.floats) {
|
|
case 8:
|
|
case 7:
|
|
case 6:
|
|
case 5:
|
|
case 4:
|
|
case 3:
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
switch (reg.ints) {
|
|
case 6:
|
|
case 5:
|
|
case 4:
|
|
case 3:
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
#else
|
|
if (ct->calling_convention == FAST_CALL) {
|
|
switch (reg.ints) {
|
|
case 2:
|
|
dasm_put(Dst, 1590, 4);
|
|
case 1:
|
|
dasm_put(Dst, 1596);
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
dasm_put(Dst, 1600, REGISTER_STACK_SPACE(ct));
|
|
}
|
|
#endif
|
|
|
|
#ifdef __amd64__
|
|
if (ct->has_var_arg) {
|
|
/* al stores an upper limit on the number of float register, note that
|
|
* its allowed to be more than the actual number of float registers used as
|
|
* long as its 0-8 */
|
|
}
|
|
#endif
|
|
|
|
dasm_put(Dst, 1604);
|
|
|
|
/* note on windows X86 the stack may be only aligned to 4 (stdcall will
|
|
* have popped a multiple of 4 bytes), but we don't need 16 byte alignment on
|
|
* that platform
|
|
*/
|
|
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
mbr_ct = (const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
if (mbr_ct->pointers || mbr_ct->type == INTPTR_TYPE) {
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1613, perr, mbr_ct, lua_upvalueindex(num_upvals));
|
|
|
|
} else {
|
|
switch (mbr_ct->type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_getuservalue(L, -1);
|
|
num_upvals += 2;
|
|
dasm_put(Dst, 1613, perr, mbr_ct, lua_upvalueindex(num_upvals));
|
|
break;
|
|
|
|
case INT64_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1653, perr, mbr_ct);
|
|
break;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1707, perr, mbr_ct);
|
|
break;
|
|
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
num_upvals++;
|
|
dasm_put(Dst, 1761, perr);
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1778);
|
|
break;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1783);
|
|
break;
|
|
|
|
case INT8_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1360);
|
|
} else {
|
|
dasm_put(Dst, 1364);
|
|
}
|
|
dasm_put(Dst, 1788);
|
|
break;
|
|
|
|
case INT16_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1368);
|
|
} else {
|
|
dasm_put(Dst, 1372);
|
|
}
|
|
dasm_put(Dst, 1788);
|
|
break;
|
|
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pop(L, 1);
|
|
if (mbr_ct->is_unsigned) {
|
|
dasm_put(Dst, 1793);
|
|
} else {
|
|
dasm_put(Dst, 1788);
|
|
}
|
|
break;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1798);
|
|
break;
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pop(L, 1);
|
|
dasm_put(Dst, 1798);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "NYI: call return type");
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + num_upvals);
|
|
{
|
|
cfunction f = compile(Dst, L, func, LUA_NOREF);
|
|
/* add a callback as an upval so that the jitted code gets cleaned up when
|
|
* the function gets gc'd */
|
|
push_callback(L, f);
|
|
lua_pushcclosure(L, (lua_CFunction) f, num_upvals+1);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* 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 = LUAFFI_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");
|
|
}
|
|
|
|
/* 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 = LUAFFI_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;
|
|
}
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
//#include "ffi.h"
|
|
|
|
#define IS_CONST(tok) (IS_LITERAL(tok, "const") || IS_LITERAL(tok, "__const") || IS_LITERAL(tok, "__const__"))
|
|
#define IS_VOLATILE(tok) (IS_LITERAL(tok, "volatile") || IS_LITERAL(tok, "__volatile") || IS_LITERAL(tok, "__volatile__"))
|
|
#define IS_RESTRICT(tok) (IS_LITERAL(tok, "restrict") || IS_LITERAL(tok, "__restrict") || IS_LITERAL(tok, "__restrict__"))
|
|
|
|
enum etoken {
|
|
TOK_NIL,
|
|
TOK_NUMBER,
|
|
TOK_STRING,
|
|
TOK_TOKEN,
|
|
|
|
/* the order of these values must match the token strings in lex.c */
|
|
|
|
TOK_3_BEGIN,
|
|
TOK_VA_ARG,
|
|
|
|
TOK_2_BEGIN,
|
|
TOK_LEFT_SHIFT, TOK_RIGHT_SHIFT, TOK_LOGICAL_AND, TOK_LOGICAL_OR, TOK_LESS_EQUAL,
|
|
TOK_GREATER_EQUAL, TOK_EQUAL, TOK_NOT_EQUAL,
|
|
|
|
TOK_1_BEGIN,
|
|
TOK_OPEN_CURLY, TOK_CLOSE_CURLY, TOK_SEMICOLON, TOK_COMMA, TOK_COLON,
|
|
TOK_ASSIGN, TOK_OPEN_PAREN, TOK_CLOSE_PAREN, TOK_OPEN_SQUARE, TOK_CLOSE_SQUARE,
|
|
TOK_DOT, TOK_AMPERSAND, TOK_LOGICAL_NOT, TOK_BITWISE_NOT, TOK_MINUS,
|
|
TOK_PLUS, TOK_STAR, TOK_DIVIDE, TOK_MODULUS, TOK_LESS,
|
|
TOK_GREATER, TOK_BITWISE_XOR, TOK_BITWISE_OR, TOK_QUESTION, TOK_POUND,
|
|
|
|
TOK_REFERENCE = TOK_AMPERSAND,
|
|
TOK_MULTIPLY = TOK_STAR,
|
|
TOK_BITWISE_AND = TOK_AMPERSAND,
|
|
};
|
|
|
|
struct token {
|
|
enum etoken type;
|
|
int64_t integer;
|
|
const char* str;
|
|
size_t size;
|
|
};
|
|
|
|
#define IS_LITERAL(TOK, STR) \
|
|
(((TOK).size == sizeof(STR) - 1) && 0 == memcmp((TOK).str, STR, sizeof(STR) - 1))
|
|
|
|
/* the order of tokens _must_ match the order of the enum etoken enum */
|
|
|
|
static char tok3[][4] = {
|
|
"...", /* unused ">>=", "<<=", */
|
|
};
|
|
|
|
static char tok2[][3] = {
|
|
"<<", ">>", "&&", "||", "<=",
|
|
">=", "==", "!=",
|
|
/* unused "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "++", "--", "->", "::", */
|
|
};
|
|
|
|
static char tok1[] = {
|
|
'{', '}', ';', ',', ':',
|
|
'=', '(', ')', '[', ']',
|
|
'.', '&', '!', '~', '-',
|
|
'+', '*', '/', '%', '<',
|
|
'>', '^', '|', '?', '#'
|
|
};
|
|
|
|
static int next_token(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
size_t i;
|
|
const char* s = P->next;
|
|
|
|
/* UTF8 BOM */
|
|
if (s[0] == '\xEF' && s[1] == '\xBB' && s[2] == '\xBF') {
|
|
s += 3;
|
|
}
|
|
|
|
/* consume whitespace and comments */
|
|
for (;;) {
|
|
/* consume whitespace */
|
|
while(*s == '\t' || *s == '\n' || *s == ' ' || *s == '\v' || *s == '\r') {
|
|
if (*s == '\n') {
|
|
P->line++;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
/* consume comments */
|
|
if (*s == '/' && *(s+1) == '/') {
|
|
|
|
s = strchr(s, '\n');
|
|
if (!s) {
|
|
luaL_error(L, "non-terminated comment");
|
|
}
|
|
|
|
} else if (*s == '/' && *(s+1) == '*') {
|
|
s += 2;
|
|
|
|
for (;;) {
|
|
if (s[0] == '\0') {
|
|
luaL_error(L, "non-terminated comment");
|
|
} else if (s[0] == '*' && s[1] == '/') {
|
|
s += 2;
|
|
break;
|
|
} else if (s[0] == '\n') {
|
|
P->line++;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
} else if (*s == '\0') {
|
|
tok->type = TOK_NIL;
|
|
return 0;
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
P->prev = s;
|
|
|
|
for (i = 0; i < sizeof(tok3) / sizeof(tok3[0]); i++) {
|
|
if (s[0] == tok3[i][0] && s[1] == tok3[i][1] && s[2] == tok3[i][2]) {
|
|
tok->type = (enum etoken) (TOK_3_BEGIN + 1 + i);
|
|
P->next = s + 3;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < sizeof(tok2) / sizeof(tok2[0]); i++) {
|
|
if (s[0] == tok2[i][0] && s[1] == tok2[i][1]) {
|
|
tok->type = (enum etoken) (TOK_2_BEGIN + 1 + i);
|
|
P->next = s + 2;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < sizeof(tok1) / sizeof(tok1[0]); i++) {
|
|
if (s[0] == tok1[i]) {
|
|
tok->type = (enum etoken) (TOK_1_BEGIN + 1 + i);
|
|
P->next = s + 1;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
if (*s == '.' || *s == '-' || ('0' <= *s && *s <= '9')) {
|
|
/* number */
|
|
tok->type = TOK_NUMBER;
|
|
|
|
/* split out the negative case so we get the full range of bits for
|
|
* unsigned (eg to support 0xFFFFFFFF where sizeof(long) == 4)
|
|
*/
|
|
if (*s == '-') {
|
|
tok->integer = strtol(s, (char**) &s, 0);
|
|
} else {
|
|
tok->integer = strtoul(s, (char**) &s, 0);
|
|
}
|
|
|
|
while (*s == 'u' || *s == 'U' || *s == 'l' || *s == 'L') {
|
|
s++;
|
|
}
|
|
|
|
P->next = s;
|
|
goto end;
|
|
|
|
} else if (*s == '\'' || *s == '\"') {
|
|
/* "..." or '...' */
|
|
char quote = *s;
|
|
s++; /* jump over " */
|
|
|
|
tok->type = TOK_STRING;
|
|
tok->str = s;
|
|
|
|
while (*s != quote) {
|
|
|
|
if (*s == '\0' || (*s == '\\' && *(s+1) == '\0')) {
|
|
return luaL_error(L, "string not finished");
|
|
}
|
|
|
|
if (*s == '\\') {
|
|
s++;
|
|
}
|
|
|
|
s++;
|
|
}
|
|
|
|
tok->size = s - tok->str;
|
|
s++; /* jump over " */
|
|
P->next = s;
|
|
goto end;
|
|
|
|
} else if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_') {
|
|
/* tokens */
|
|
tok->type = TOK_TOKEN;
|
|
tok->str = s;
|
|
|
|
while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_' || ('0' <= *s && *s <= '9')) {
|
|
s++;
|
|
}
|
|
|
|
tok->size = s - tok->str;
|
|
P->next = s;
|
|
goto end;
|
|
|
|
} else {
|
|
return luaL_error(L, "invalid character %d", P->line);
|
|
}
|
|
|
|
end:
|
|
/*fprintf(stderr, "token %d %d %.*s %.10s\n", tok->type, (int) tok->size, (tok->type == TOK_TOKEN || tok->type == TOK_STRING) ? (int) tok->size : 0, tok->str, P->next);*/
|
|
return 1;
|
|
}
|
|
|
|
static void require_token(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
if (!next_token(L, P, tok)) {
|
|
luaL_error(L, "unexpected end");
|
|
}
|
|
}
|
|
|
|
static void check_token(lua_State* L, struct parser* P, int type, const char* str, const char* err, ...)
|
|
{
|
|
struct token tok;
|
|
if (!next_token(L, P, &tok) || tok.type != type || (tok.type == TOK_TOKEN && (tok.size != strlen(str) || memcmp(tok.str, str, tok.size) != 0))) {
|
|
va_list ap;
|
|
va_start(ap, err);
|
|
lua_pushvfstring(L, err, ap);
|
|
lua_error(L);
|
|
}
|
|
}
|
|
|
|
static void put_back(struct parser* P)
|
|
{ P->next = P->prev; }
|
|
|
|
|
|
int64_t calculate_constant(lua_State* L, struct parser* P);
|
|
|
|
static int g_name_key;
|
|
static int g_front_name_key;
|
|
static int g_back_name_key;
|
|
|
|
#ifndef max
|
|
#define max(a,b) ((a) < (b) ? (b) : (a))
|
|
#endif
|
|
|
|
#ifndef min
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
enum test {TEST};
|
|
|
|
/* Parses an enum definition from after the open curly through to the close
|
|
* curly. Expects the user table to be on the top of the stack
|
|
*/
|
|
static int parse_enum(lua_State* L, struct parser* P, struct ctype* type)
|
|
{
|
|
struct token tok;
|
|
int value = -1;
|
|
int ct_usr = lua_gettop(L);
|
|
|
|
for (;;) {
|
|
require_token(L, P, &tok);
|
|
|
|
assert(lua_gettop(L) == ct_usr);
|
|
|
|
if (tok.type == TOK_CLOSE_CURLY) {
|
|
break;
|
|
} else if (tok.type != TOK_TOKEN) {
|
|
return luaL_error(L, "unexpected token in enum at line %d", P->line);
|
|
}
|
|
|
|
lua_pushlstring(L, tok.str, tok.size);
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_COMMA || tok.type == TOK_CLOSE_CURLY) {
|
|
/* we have an auto calculated enum value */
|
|
value++;
|
|
} else if (tok.type == TOK_ASSIGN) {
|
|
/* we have an explicit enum value */
|
|
value = (int) calculate_constant(L, P);
|
|
require_token(L, P, &tok);
|
|
} else {
|
|
return luaL_error(L, "unexpected token in enum at line %d", P->line);
|
|
}
|
|
|
|
assert(lua_gettop(L) == ct_usr + 1);
|
|
|
|
/* add the enum value to the constants table */
|
|
push_upval(L, &constants_key);
|
|
lua_pushvalue(L, -2);
|
|
lua_pushnumber(L, value);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1);
|
|
|
|
assert(lua_gettop(L) == ct_usr + 1);
|
|
|
|
/* add the enum value to the enum usr value table */
|
|
lua_pushnumber(L, value);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
if (tok.type == TOK_CLOSE_CURLY) {
|
|
break;
|
|
} else if (tok.type != TOK_COMMA) {
|
|
return luaL_error(L, "unexpected token in enum at line %d", P->line);
|
|
}
|
|
}
|
|
|
|
type->base_size = sizeof(enum test);
|
|
type->align_mask = sizeof(enum test) - 1;
|
|
|
|
assert(lua_gettop(L) == ct_usr);
|
|
return 0;
|
|
}
|
|
|
|
static void calculate_member_position(lua_State* L, struct parser* P, struct ctype* ct, struct ctype* mt, int* pbit_offset, int* pbitfield_type)
|
|
{
|
|
int bit_offset = *pbit_offset;
|
|
|
|
if (ct->type == UNION_TYPE) {
|
|
size_t msize;
|
|
|
|
if (mt->is_variable_struct || mt->is_variable_array) {
|
|
luaL_error(L, "NYI: variable sized members in unions");
|
|
return;
|
|
|
|
} else if (mt->is_bitfield) {
|
|
msize = (mt->align_mask + 1);
|
|
#ifdef _WIN32
|
|
/* MSVC has a bug where it doesn't update the alignment of
|
|
* a union for bitfield members. */
|
|
mt->align_mask = 0;
|
|
#endif
|
|
|
|
} else if (mt->is_array) {
|
|
msize = mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size);
|
|
|
|
} else {
|
|
msize = mt->pointers ? sizeof(void*) : mt->base_size;
|
|
}
|
|
|
|
ct->base_size = max(ct->base_size, msize);
|
|
|
|
} else if (mt->is_bitfield) {
|
|
if (mt->has_member_name && mt->bit_size == 0) {
|
|
luaL_error(L, "zero length bitfields must be unnamed on line %d", P->line);
|
|
}
|
|
|
|
#if defined _WIN32
|
|
/* MSVC uses a seperate storage unit for each size. This is aligned
|
|
* before the first bitfield. :0 finishes up the storage unit using
|
|
* the greater alignment of the storage unit or the type used with the
|
|
* :0. This is equivalent to the :0 always creating a new storage
|
|
* unit, but not necesserily using it yet.
|
|
*/
|
|
|
|
if (*pbitfield_type == -1 && mt->bit_size == 0) {
|
|
/* :0 not after a bitfield are ignored */
|
|
return;
|
|
}
|
|
|
|
{
|
|
int different_storage = mt->align_mask != *pbitfield_type;
|
|
int no_room_left = bit_offset + mt->bit_size > (mt->align_mask + 1) * CHAR_BIT;
|
|
|
|
if (different_storage || no_room_left || !mt->bit_size) {
|
|
ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT;
|
|
bit_offset = 0;
|
|
if (*pbitfield_type >= 0) {
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, *pbitfield_type);
|
|
}
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, mt->align_mask);
|
|
}
|
|
}
|
|
|
|
mt->bit_offset = bit_offset;
|
|
mt->offset = ct->base_size;
|
|
|
|
*pbitfield_type = mt->align_mask;
|
|
bit_offset += mt->bit_size;
|
|
|
|
#elif defined OS_OSX
|
|
/* OSX doesn't use containers and bitfields are not aligned. So
|
|
* bitfields never add any padding, except for :0 which still forces
|
|
* an alignment based off the type used with the :0 */
|
|
if (mt->bit_size) {
|
|
mt->offset = ct->base_size;
|
|
mt->bit_offset = bit_offset;
|
|
bit_offset += mt->bit_size;
|
|
ct->base_size += bit_offset / CHAR_BIT;
|
|
bit_offset = bit_offset % CHAR_BIT;
|
|
} else {
|
|
ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT;
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, mt->align_mask);
|
|
bit_offset = 0;
|
|
}
|
|
|
|
if (!mt->has_member_name) {
|
|
/* unnamed bitfields don't update the struct alignment */
|
|
mt->align_mask = 0;
|
|
}
|
|
|
|
#elif defined __GNUC__ || defined __TINYC__ //< @r-lyeh: tcc case
|
|
/* GCC tries to pack bitfields in as close as much as possible, but
|
|
* still making sure that they don't cross alignment boundaries.
|
|
* :0 forces an alignment based off the type used with the :0
|
|
*/
|
|
|
|
int bits_used = (ct->base_size - ALIGN_DOWN(ct->base_size, mt->align_mask)) * CHAR_BIT + bit_offset;
|
|
int need_to_realign = bits_used + mt->bit_size > mt->base_size * CHAR_BIT;
|
|
|
|
if (!mt->is_packed && (!mt->bit_size || need_to_realign)) {
|
|
ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT;
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, mt->align_mask);
|
|
bit_offset = 0;
|
|
}
|
|
|
|
mt->bit_offset = bit_offset;
|
|
mt->offset = ct->base_size;
|
|
|
|
bit_offset += mt->bit_size;
|
|
ct->base_size += bit_offset / CHAR_BIT;
|
|
bit_offset = bit_offset % CHAR_BIT;
|
|
|
|
/* unnamed bitfields don't update the struct alignment */
|
|
if (!mt->has_member_name) {
|
|
mt->align_mask = 0;
|
|
}
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
} else {
|
|
/* finish up the current bitfield storage unit */
|
|
ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT;
|
|
bit_offset = 0;
|
|
|
|
if (*pbitfield_type >= 0) {
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, *pbitfield_type);
|
|
}
|
|
|
|
*pbitfield_type = -1;
|
|
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, mt->align_mask);
|
|
mt->offset = ct->base_size;
|
|
|
|
if (mt->is_variable_array) {
|
|
ct->is_variable_struct = 1;
|
|
ct->variable_increment = mt->pointers > 1 ? sizeof(void*) : mt->base_size;
|
|
|
|
} else if (mt->is_variable_struct) {
|
|
assert(!mt->variable_size_known && !mt->is_array && !mt->pointers);
|
|
ct->base_size += mt->base_size;
|
|
ct->is_variable_struct = 1;
|
|
ct->variable_increment = mt->variable_increment;
|
|
|
|
} else if (mt->is_array) {
|
|
ct->base_size += mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size);
|
|
|
|
} else {
|
|
ct->base_size += mt->pointers ? sizeof(void*) : mt->base_size;
|
|
}
|
|
}
|
|
|
|
/* increase the outer struct/union alignment if needed */
|
|
if (mt->align_mask > (int) ct->align_mask) {
|
|
ct->align_mask = mt->align_mask;
|
|
}
|
|
|
|
if (mt->has_bitfield || mt->is_bitfield) {
|
|
ct->has_bitfield = 1;
|
|
}
|
|
|
|
*pbit_offset = bit_offset;
|
|
}
|
|
|
|
static int copy_submembers(lua_State* L, int to_usr, int from_usr, const struct ctype* ft, int* midx)
|
|
{
|
|
struct ctype ct;
|
|
int i, sublen;
|
|
|
|
from_usr = lua_absindex(L, from_usr);
|
|
to_usr = lua_absindex(L, to_usr);
|
|
|
|
/* integer keys */
|
|
sublen = (int) lua_rawlen(L, from_usr);
|
|
for (i = 1; i <= sublen; i++) {
|
|
lua_rawgeti(L, from_usr, i);
|
|
|
|
ct = *(const struct ctype*) lua_touserdata(L, -1);
|
|
ct.offset += ft->offset;
|
|
lua_getuservalue(L, -1);
|
|
|
|
push_ctype(L, -1, &ct);
|
|
lua_rawseti(L, to_usr, (*midx)++);
|
|
|
|
lua_pop(L, 2); /* ctype, user value */
|
|
}
|
|
|
|
/* string keys */
|
|
lua_pushnil(L);
|
|
while (lua_next(L, from_usr)) {
|
|
if (lua_type(L, -2) == LUA_TSTRING) {
|
|
struct ctype ct = *(const struct ctype*) lua_touserdata(L, -1);
|
|
ct.offset += ft->offset;
|
|
lua_getuservalue(L, -1);
|
|
|
|
/* uservalue[sub_mname] = new_sub_mtype */
|
|
lua_pushvalue(L, -3);
|
|
push_ctype(L, -2, &ct);
|
|
lua_rawset(L, to_usr);
|
|
|
|
lua_pop(L, 1); /* remove submember user value */
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_member(lua_State* L, int ct_usr, int mname, int mbr_usr, const struct ctype* mt, int* midx)
|
|
{
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
mname = lua_absindex(L, mname);
|
|
|
|
push_ctype(L, mbr_usr, mt);
|
|
|
|
/* usrvalue[mbr index] = pushed mtype */
|
|
lua_pushvalue(L, -1);
|
|
lua_rawseti(L, ct_usr, (*midx)++);
|
|
|
|
/* set usrvalue[mname] = pushed mtype */
|
|
lua_pushvalue(L, mname);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
/* set usrvalue[mtype] = mname */
|
|
lua_pushvalue(L, -1);
|
|
lua_pushvalue(L, mname);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Parses a struct from after the open curly through to the close curly.
|
|
*/
|
|
static int parse_struct(lua_State* L, struct parser* P, int tmp_usr, const struct ctype* ct)
|
|
{
|
|
struct token tok;
|
|
int midx = 1;
|
|
int top = lua_gettop(L);
|
|
|
|
tmp_usr = lua_absindex(L, tmp_usr);
|
|
|
|
/* parse members */
|
|
for (;;) {
|
|
struct ctype mbase;
|
|
|
|
assert(lua_gettop(L) == top);
|
|
|
|
/* see if we're at the end of the struct */
|
|
require_token(L, P, &tok);
|
|
if (tok.type == TOK_CLOSE_CURLY) {
|
|
break;
|
|
} else if (ct->is_variable_struct) {
|
|
return luaL_error(L, "can't have members after a variable sized member on line %d", P->line);
|
|
} else {
|
|
put_back(P);
|
|
}
|
|
|
|
/* members are of the form
|
|
* <base type> <arg>, <arg>, <arg>;
|
|
* eg struct foo bar, *bar2[2];
|
|
* mbase is 'struct foo'
|
|
* mtype is '' then '*[2]'
|
|
* mname is 'bar' then 'bar2'
|
|
*/
|
|
|
|
parse_type(L, P, &mbase);
|
|
|
|
for (;;) {
|
|
struct token mname;
|
|
struct ctype mt = mbase;
|
|
|
|
memset(&mname, 0, sizeof(mname));
|
|
|
|
if (ct->is_variable_struct) {
|
|
return luaL_error(L, "can't have members after a variable sized member on line %d", P->line);
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
parse_argument(L, P, -1, &mt, &mname, NULL);
|
|
assert(lua_gettop(L) == top + 2);
|
|
|
|
if (!mt.is_defined && (mt.pointers - mt.is_array) == 0) {
|
|
return luaL_error(L, "member type is undefined on line %d", P->line);
|
|
}
|
|
|
|
if (mt.type == VOID_TYPE && (mt.pointers - mt.is_array) == 0) {
|
|
return luaL_error(L, "member type can not be void on line %d", P->line);
|
|
}
|
|
|
|
mt.has_member_name = (mname.size > 0);
|
|
lua_pushlstring(L, mname.str, mname.size);
|
|
|
|
add_member(L, tmp_usr, -1, -2, &mt, &midx);
|
|
|
|
/* pop the usr value from push_argument and the member name */
|
|
lua_pop(L, 2);
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
require_token(L, P, &tok);
|
|
if (tok.type == TOK_SEMICOLON) {
|
|
break;
|
|
} else if (tok.type != TOK_COMMA) {
|
|
luaL_error(L, "unexpected token in struct definition on line %d", P->line);
|
|
}
|
|
}
|
|
|
|
/* pop the usr value from push_type */
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
assert(lua_gettop(L) == top);
|
|
return 0;
|
|
}
|
|
|
|
static int calculate_struct_offsets(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, int tmp_usr)
|
|
{
|
|
int i;
|
|
int midx = 1;
|
|
int sz = (int) lua_rawlen(L, tmp_usr);
|
|
int bit_offset = 0;
|
|
int bitfield_type = -1;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
tmp_usr = lua_absindex(L, tmp_usr);
|
|
|
|
for (i = 1; i <= sz; i++) {
|
|
struct ctype mt;
|
|
|
|
/* get the member type */
|
|
lua_rawgeti(L, tmp_usr, i);
|
|
mt = *(const struct ctype*) lua_touserdata(L, -1);
|
|
|
|
/* get the member user table */
|
|
lua_getuservalue(L, -1);
|
|
|
|
/* get the member name */
|
|
lua_pushvalue(L, -2);
|
|
lua_rawget(L, tmp_usr);
|
|
|
|
calculate_member_position(L, P, ct, &mt, &bit_offset, &bitfield_type);
|
|
|
|
if (mt.has_member_name) {
|
|
assert(!lua_isnil(L, -1));
|
|
add_member(L, ct_usr, -1, -2, &mt, &midx);
|
|
|
|
} else if (mt.type == STRUCT_TYPE || mt.type == UNION_TYPE) {
|
|
/* With an unnamed member we copy all of the submembers into our
|
|
* usr value adjusting the offset as necessary. Note ctypes are
|
|
* immutable so need to push a new ctype to update the offset.
|
|
*/
|
|
copy_submembers(L, ct_usr, -2, &mt, &midx);
|
|
|
|
} else {
|
|
/* We ignore unnamed members that aren't structs or unions. These
|
|
* are there just to change the padding */
|
|
}
|
|
|
|
lua_pop(L, 3);
|
|
}
|
|
|
|
/* finish up the current bitfield storage unit */
|
|
ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT;
|
|
|
|
/* only void is allowed 0 size */
|
|
if (ct->base_size == 0) {
|
|
ct->base_size = 1;
|
|
}
|
|
|
|
ct->base_size = LUAFFI_ALIGN_UP(ct->base_size, ct->align_mask);
|
|
return 0;
|
|
}
|
|
|
|
/* copy over attributes that could be specified before the typedef eg
|
|
* __attribute__(packed) const type_t */
|
|
static void instantiate_typedef(struct parser* P, struct ctype* tt, const struct ctype* ft)
|
|
{
|
|
struct ctype pt = *tt;
|
|
*tt = *ft;
|
|
|
|
tt->const_mask |= pt.const_mask;
|
|
tt->is_packed = pt.is_packed;
|
|
|
|
if (tt->is_packed) {
|
|
tt->align_mask = 0;
|
|
} else {
|
|
/* Instantiate the typedef in the current packing. This may be
|
|
* further updated if a pointer is added or another alignment
|
|
* attribute is applied. If pt.align_mask is already non-zero than an
|
|
* increased alignment via __declspec(aligned(#)) has been set. */
|
|
tt->align_mask = max(min(P->align_mask, tt->align_mask), pt.align_mask);
|
|
}
|
|
}
|
|
|
|
/* this parses a struct or union starting with the optional
|
|
* name before the opening brace
|
|
* leaves the type usr value on the stack
|
|
*/
|
|
static int parse_record(lua_State* L, struct parser* P, struct ctype* ct)
|
|
{
|
|
struct token tok;
|
|
int top = lua_gettop(L);
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
/* name is optional */
|
|
if (tok.type == TOK_TOKEN) {
|
|
/* declaration */
|
|
lua_pushlstring(L, tok.str, tok.size);
|
|
|
|
assert(lua_gettop(L) == top+1);
|
|
|
|
/* lookup the name to see if we've seen this type before */
|
|
push_upval(L, &types_key);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawget(L, top+2);
|
|
|
|
assert(lua_gettop(L) == top+3);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1); /* pop the nil usr value */
|
|
lua_newtable(L); /* the new usr table */
|
|
|
|
/* stack layout is:
|
|
* top+1: record name
|
|
* top+2: types table
|
|
* top+3: new usr table
|
|
*/
|
|
|
|
lua_pushlightuserdata(L, &g_name_key);
|
|
lua_pushvalue(L, top+1);
|
|
lua_rawset(L, top+3); /* usr[name_key] = name */
|
|
|
|
lua_pushvalue(L, top+1);
|
|
push_ctype(L, top+3, ct);
|
|
lua_rawset(L, top+2); /* types[name] = new_ctype */
|
|
|
|
} else {
|
|
/* get the exsting declared type */
|
|
const struct ctype* prevt = (const struct ctype*) lua_touserdata(L, top+3);
|
|
|
|
if (prevt->type != ct->type) {
|
|
lua_getuservalue(L, top+3);
|
|
push_type_name(L, -1, ct);
|
|
push_type_name(L, top+3, prevt);
|
|
luaL_error(L, "type '%s' previously declared as '%s'", lua_tostring(L, -2), lua_tostring(L, -1));
|
|
}
|
|
|
|
instantiate_typedef(P, ct, prevt);
|
|
|
|
/* replace the ctype with its usr value */
|
|
lua_getuservalue(L, -1);
|
|
lua_replace(L, -2);
|
|
}
|
|
|
|
/* remove the extra name and types table */
|
|
lua_replace(L, -3);
|
|
lua_pop(L, 1);
|
|
|
|
assert(lua_gettop(L) == top + 1 && lua_istable(L, -1));
|
|
|
|
/* if a name is given then we may be at the end of the string
|
|
* eg for ffi.new('struct foo')
|
|
*/
|
|
if (!next_token(L, P, &tok)) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
/* create a new unnamed record */
|
|
int num;
|
|
|
|
/* get the next unnamed number */
|
|
push_upval(L, &next_unnamed_key);
|
|
num = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
/* increment the unnamed upval */
|
|
lua_pushinteger(L, num + 1);
|
|
set_upval(L, &next_unnamed_key);
|
|
|
|
lua_newtable(L); /* the new usr table - leave on stack */
|
|
|
|
/* usr[name_key] = num */
|
|
lua_pushlightuserdata(L, &g_name_key);
|
|
lua_pushfstring(L, "%d", num);
|
|
lua_rawset(L, -3);
|
|
}
|
|
|
|
if (tok.type != TOK_OPEN_CURLY) {
|
|
/* this may just be a declaration or use of the type as an argument or
|
|
* member */
|
|
put_back(P);
|
|
return 0;
|
|
}
|
|
|
|
if (ct->is_defined) {
|
|
return luaL_error(L, "redefinition in line %d", P->line);
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + 1 && lua_istable(L, -1));
|
|
|
|
if (ct->type == ENUM_TYPE) {
|
|
parse_enum(L, P, ct);
|
|
} else {
|
|
/* we do a two stage parse, where we parse the content first and build up
|
|
* the temp user table. We then iterate over that to calculate the offsets
|
|
* and fill out ct_usr. This is so we can handle out of order members
|
|
* (eg vtable) and attributes specified at the end of the struct.
|
|
*/
|
|
lua_newtable(L);
|
|
parse_struct(L, P, -1, ct);
|
|
calculate_struct_offsets(L, P, -2, ct, -1);
|
|
assert(lua_gettop(L) == top + 2 && lua_istable(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + 1 && lua_istable(L, -1));
|
|
set_defined(L, -1, ct);
|
|
assert(lua_gettop(L) == top + 1);
|
|
return 0;
|
|
}
|
|
|
|
/* parses single or multi work built in types, and pushes it onto the stack */
|
|
static int parse_type_name(lua_State* L, struct parser* P)
|
|
{
|
|
struct token tok;
|
|
int flags = 0;
|
|
|
|
enum {
|
|
UNSIGNED = 0x01,
|
|
SIGNED = 0x02,
|
|
LONG = 0x04,
|
|
SHORT = 0x08,
|
|
INT = 0x10,
|
|
CHAR = 0x20,
|
|
LONG_LONG = 0x40,
|
|
INT8 = 0x80,
|
|
INT16 = 0x100,
|
|
INT32 = 0x200,
|
|
INT64 = 0x400,
|
|
DOUBLE = 0x800,
|
|
FLOAT = 0x1000,
|
|
COMPLEX = 0x2000,
|
|
};
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
/* we have to manually decode the builtin types since they can take up
|
|
* more then one token
|
|
*/
|
|
for (;;) {
|
|
if (tok.type != TOK_TOKEN) {
|
|
break;
|
|
} else if (IS_LITERAL(tok, "unsigned")) {
|
|
flags |= UNSIGNED;
|
|
} else if (IS_LITERAL(tok, "signed")) {
|
|
flags |= SIGNED;
|
|
} else if (IS_LITERAL(tok, "short")) {
|
|
flags |= SHORT;
|
|
} else if (IS_LITERAL(tok, "char")) {
|
|
flags |= CHAR;
|
|
} else if (IS_LITERAL(tok, "long")) {
|
|
flags |= (flags & LONG) ? LONG_LONG : LONG;
|
|
} else if (IS_LITERAL(tok, "int")) {
|
|
flags |= INT;
|
|
} else if (IS_LITERAL(tok, "__int8")) {
|
|
flags |= INT8;
|
|
} else if (IS_LITERAL(tok, "__int16")) {
|
|
flags |= INT16;
|
|
} else if (IS_LITERAL(tok, "__int32")) {
|
|
flags |= INT32;
|
|
} else if (IS_LITERAL(tok, "__int64")) {
|
|
flags |= INT64;
|
|
} else if (IS_LITERAL(tok, "double")) {
|
|
flags |= DOUBLE;
|
|
} else if (IS_LITERAL(tok, "float")) {
|
|
flags |= FLOAT;
|
|
} else if (IS_LITERAL(tok, "complex") || IS_LITERAL(tok, "_Complex")) {
|
|
flags |= COMPLEX;
|
|
} else if (IS_LITERAL(tok, "register")) {
|
|
/* ignore */
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (!next_token(L, P, &tok)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (flags) {
|
|
put_back(P);
|
|
}
|
|
|
|
if (flags & CHAR) {
|
|
if (flags & SIGNED) {
|
|
lua_pushliteral(L, "int8_t");
|
|
} else if (flags & UNSIGNED) {
|
|
lua_pushliteral(L, "uint8_t");
|
|
} else {
|
|
lua_pushstring(L, (((char) -1) > 0) ? "uint8_t" : "int8_t");
|
|
}
|
|
|
|
} else if (flags & INT8) {
|
|
lua_pushstring(L, (flags & UNSIGNED) ? "uint8_t" : "int8_t");
|
|
} else if (flags & INT16) {
|
|
lua_pushstring(L, (flags & UNSIGNED) ? "uint16_t" : "int16_t");
|
|
} else if (flags & INT32) {
|
|
lua_pushstring(L, (flags & UNSIGNED) ? "uint32_t" : "int32_t");
|
|
} else if (flags & (INT64 | LONG_LONG)) {
|
|
lua_pushstring(L, (flags & UNSIGNED) ? "uint64_t" : "int64_t");
|
|
|
|
} else if (flags & COMPLEX) {
|
|
if (flags & LONG) {
|
|
lua_pushliteral(L, "complex long double");
|
|
} else if (flags & FLOAT) {
|
|
lua_pushliteral(L, "complex float");
|
|
} else {
|
|
lua_pushliteral(L, "complex double");
|
|
}
|
|
|
|
} else if (flags & DOUBLE) {
|
|
if (flags & LONG) {
|
|
lua_pushliteral(L, "long double");
|
|
} else {
|
|
lua_pushliteral(L, "double");
|
|
}
|
|
|
|
} else if (flags & FLOAT) {
|
|
lua_pushliteral(L, "float");
|
|
|
|
} else if (flags & SHORT) {
|
|
#define SHORT_TYPE(u) (sizeof(short) == sizeof(int64_t) ? u "int64_t" : sizeof(short) == sizeof(int32_t) ? u "int32_t" : u "int16_t")
|
|
if (flags & UNSIGNED) {
|
|
lua_pushstring(L, SHORT_TYPE("u"));
|
|
} else {
|
|
lua_pushstring(L, SHORT_TYPE(""));
|
|
}
|
|
#undef SHORT_TYPE
|
|
|
|
} else if (flags & LONG) {
|
|
#define LONG_TYPE(u) (sizeof(long) == sizeof(int64_t) ? u "int64_t" : u "int32_t")
|
|
if (flags & UNSIGNED) {
|
|
lua_pushstring(L, LONG_TYPE("u"));
|
|
} else {
|
|
lua_pushstring(L, LONG_TYPE(""));
|
|
}
|
|
#undef LONG_TYPE
|
|
|
|
} else if (flags) {
|
|
#define INT_TYPE(u) (sizeof(int) == sizeof(int64_t) ? u "int64_t" : sizeof(int) == sizeof(int32_t) ? u "int32_t" : u "int16_t")
|
|
if (flags & UNSIGNED) {
|
|
lua_pushstring(L, INT_TYPE("u"));
|
|
} else {
|
|
lua_pushstring(L, INT_TYPE(""));
|
|
}
|
|
#undef INT_TYPE
|
|
|
|
} else {
|
|
lua_pushlstring(L, tok.str, tok.size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* parse_attribute parses a token to see if it is an attribute. It may then
|
|
* parse some following tokens to decode the attribute setting the appropriate
|
|
* fields in ct. It will return 1 if the token was used (and possibly some
|
|
* more following it) or 0 if not. If the token was used, the next token must
|
|
* be retrieved using next_token/require_token.
|
|
*/
|
|
static int parse_attribute(lua_State* L, struct parser* P, struct token* tok, struct ctype* ct, struct parser* asmname)
|
|
{
|
|
if (tok->type != TOK_TOKEN) {
|
|
return 0;
|
|
|
|
} else if (asmname && (IS_LITERAL(*tok, "__asm__") || IS_LITERAL(*tok, "__asm"))) {
|
|
check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token after __asm__ on line %d", P->line);
|
|
*asmname = *P;
|
|
|
|
require_token(L, P, tok);
|
|
while (tok->type == TOK_STRING) {
|
|
require_token(L, P, tok);
|
|
}
|
|
|
|
if (tok->type != TOK_CLOSE_PAREN) {
|
|
luaL_error(L, "unexpected token after __asm__ on line %d", P->line);
|
|
}
|
|
return 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "__attribute__") || IS_LITERAL(*tok, "__declspec")) {
|
|
int parens = 1;
|
|
check_token(L, P, TOK_OPEN_PAREN, NULL, "expected parenthesis after __attribute__ or __declspec on line %d", P->line);
|
|
|
|
for (;;) {
|
|
require_token(L, P, tok);
|
|
if (tok->type == TOK_OPEN_PAREN) {
|
|
parens++;
|
|
} else if (tok->type == TOK_CLOSE_PAREN) {
|
|
if (--parens == 0) {
|
|
break;
|
|
}
|
|
|
|
} else if (tok->type != TOK_TOKEN) {
|
|
/* ignore unknown symbols within parentheses */
|
|
|
|
} else if (IS_LITERAL(*tok, "align") || IS_LITERAL(*tok, "aligned") || IS_LITERAL(*tok, "__aligned__")) {
|
|
unsigned align = 0;
|
|
require_token(L, P, tok);
|
|
|
|
switch (tok->type) {
|
|
case TOK_CLOSE_PAREN:
|
|
align = ALIGNED_DEFAULT;
|
|
put_back(P);
|
|
break;
|
|
|
|
case TOK_OPEN_PAREN:
|
|
require_token(L, P, tok);
|
|
|
|
if (tok->type != TOK_NUMBER) {
|
|
luaL_error(L, "expected align(#) on line %d", P->line);
|
|
}
|
|
|
|
switch (tok->integer) {
|
|
case 1: align = 0; break;
|
|
case 2: align = 1; break;
|
|
case 4: align = 3; break;
|
|
case 8: align = 7; break;
|
|
case 16: align = 15; break;
|
|
default:
|
|
luaL_error(L, "unsupported align size on line %d", P->line);
|
|
}
|
|
|
|
check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected align(#) on line %d", P->line);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "expected align(#) on line %d", P->line);
|
|
}
|
|
|
|
/* __attribute__(aligned(#)) is only supposed to increase alignment */
|
|
ct->align_mask = max(align, ct->align_mask);
|
|
|
|
} else if (IS_LITERAL(*tok, "packed") || IS_LITERAL(*tok, "__packed__")) {
|
|
ct->align_mask = 0;
|
|
ct->is_packed = 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "mode") || IS_LITERAL(*tok, "__mode__")) {
|
|
|
|
check_token(L, P, TOK_OPEN_PAREN, NULL, "expected mode(MODE) on line %d", P->line);
|
|
|
|
require_token(L, P, tok);
|
|
if (tok->type != TOK_TOKEN) {
|
|
luaL_error(L, "expected mode(MODE) on line %d", P->line);
|
|
}
|
|
|
|
if (ct->type == FLOAT_TYPE || ct->type == DOUBLE_TYPE) {
|
|
struct {char ch; float v;} af;
|
|
struct {char ch; double v;} ad;
|
|
|
|
if (IS_LITERAL(*tok, "SF") || IS_LITERAL(*tok, "__SF__")) {
|
|
ct->type = FLOAT_TYPE;
|
|
ct->base_size = sizeof(float);
|
|
ct->align_mask = ALIGNOF(af);
|
|
|
|
} else if (IS_LITERAL(*tok, "DF") || IS_LITERAL(*tok, "__DF__")) {
|
|
ct->type = DOUBLE_TYPE;
|
|
ct->base_size = sizeof(double);
|
|
ct->align_mask = ALIGNOF(ad);
|
|
|
|
} else {
|
|
luaL_error(L, "unexpected mode on line %d", P->line);
|
|
}
|
|
|
|
} else {
|
|
struct {char ch; uint16_t v;} a16;
|
|
struct {char ch; uint32_t v;} a32;
|
|
struct {char ch; uint64_t v;} a64;
|
|
|
|
if (IS_LITERAL(*tok, "QI") || IS_LITERAL(*tok, "__QI__")
|
|
|| IS_LITERAL(*tok, "byte") || IS_LITERAL(*tok, "__byte__")
|
|
) {
|
|
ct->type = INT8_TYPE;
|
|
ct->base_size = sizeof(uint8_t);
|
|
ct->align_mask = 0;
|
|
|
|
} else if (IS_LITERAL(*tok, "HI") || IS_LITERAL(*tok, "__HI__")) {
|
|
ct->type = INT16_TYPE;
|
|
ct->base_size = sizeof(uint16_t);
|
|
ct->align_mask = ALIGNOF(a16);
|
|
|
|
} else if (IS_LITERAL(*tok, "SI") || IS_LITERAL(*tok, "__SI__")
|
|
#if defined ARCH_X86 || defined ARCH_ARM
|
|
|| IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__")
|
|
|| IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__")
|
|
#endif
|
|
) {
|
|
ct->type = INT32_TYPE;
|
|
ct->base_size = sizeof(uint32_t);
|
|
ct->align_mask = ALIGNOF(a32);
|
|
|
|
} else if (IS_LITERAL(*tok, "DI") || IS_LITERAL(*tok, "__DI__")
|
|
#if defined ARCH_X64
|
|
|| IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__")
|
|
|| IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__")
|
|
#endif
|
|
) {
|
|
ct->type = INT64_TYPE;
|
|
ct->base_size = sizeof(uint64_t);
|
|
ct->align_mask = ALIGNOF(a64);
|
|
|
|
} else {
|
|
luaL_error(L, "unexpected mode on line %d", P->line);
|
|
}
|
|
}
|
|
|
|
check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected mode(MODE) on line %d", P->line);
|
|
|
|
} else if (IS_LITERAL(*tok, "cdecl") || IS_LITERAL(*tok, "__cdecl__")) {
|
|
ct->calling_convention = C_CALL;
|
|
|
|
} else if (IS_LITERAL(*tok, "fastcall") || IS_LITERAL(*tok, "__fastcall__")) {
|
|
ct->calling_convention = FAST_CALL;
|
|
|
|
} else if (IS_LITERAL(*tok, "stdcall") || IS_LITERAL(*tok, "__stdcall__")) {
|
|
ct->calling_convention = STD_CALL;
|
|
}
|
|
/* ignore unknown tokens within parentheses */
|
|
}
|
|
return 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "__cdecl")) {
|
|
ct->calling_convention = C_CALL;
|
|
return 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "__fastcall")) {
|
|
ct->calling_convention = FAST_CALL;
|
|
return 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "__stdcall")) {
|
|
ct->calling_convention = STD_CALL;
|
|
return 1;
|
|
|
|
} else if (IS_LITERAL(*tok, "__extension__") || IS_LITERAL(*tok, "extern")) {
|
|
/* ignore */
|
|
return 1;
|
|
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* parses out the base type of a type expression in a function declaration,
|
|
* struct definition, typedef etc
|
|
*
|
|
* leaves the usr value of the type on the stack
|
|
*/
|
|
int parse_type(lua_State* L, struct parser* P, struct ctype* ct)
|
|
{
|
|
struct token tok;
|
|
int top = lua_gettop(L);
|
|
|
|
memset(ct, 0, sizeof(*ct));
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
/* get function attributes before the return type */
|
|
while (parse_attribute(L, P, &tok, ct, NULL)) {
|
|
require_token(L, P, &tok);
|
|
}
|
|
|
|
/* get const/volatile before the base type */
|
|
for (;;) {
|
|
if (tok.type != TOK_TOKEN) {
|
|
return luaL_error(L, "unexpected value before type name on line %d", P->line);
|
|
|
|
} else if (IS_CONST(tok)) {
|
|
ct->const_mask = 1;
|
|
require_token(L, P, &tok);
|
|
|
|
} else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) {
|
|
/* ignored for now */
|
|
require_token(L, P, &tok);
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* get base type */
|
|
if (tok.type != TOK_TOKEN) {
|
|
return luaL_error(L, "unexpected value before type name on line %d", P->line);
|
|
|
|
} else if (IS_LITERAL(tok, "struct")) {
|
|
ct->type = STRUCT_TYPE;
|
|
parse_record(L, P, ct);
|
|
|
|
} else if (IS_LITERAL(tok, "union")) {
|
|
ct->type = UNION_TYPE;
|
|
parse_record(L, P, ct);
|
|
|
|
} else if (IS_LITERAL(tok, "enum")) {
|
|
ct->type = ENUM_TYPE;
|
|
parse_record(L, P, ct);
|
|
|
|
} else {
|
|
put_back(P);
|
|
|
|
/* lookup type */
|
|
push_upval(L, &types_key);
|
|
parse_type_name(L, P);
|
|
lua_rawget(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pushlstring(L, tok.str, tok.size);
|
|
return luaL_error(L, "unknown type %s on line %d", lua_tostring(L, -1), P->line);
|
|
}
|
|
|
|
instantiate_typedef(P, ct, (const struct ctype*) lua_touserdata(L, -1));
|
|
|
|
/* we only want the usr tbl from the ctype in the types tbl */
|
|
lua_getuservalue(L, -1);
|
|
lua_replace(L, -2);
|
|
}
|
|
|
|
while (next_token(L, P, &tok)) {
|
|
if (tok.type != TOK_TOKEN) {
|
|
put_back(P);
|
|
break;
|
|
|
|
} else if (IS_CONST(tok) || IS_VOLATILE(tok)) {
|
|
/* ignore for now */
|
|
|
|
} else {
|
|
put_back(P);
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1)));
|
|
return 0;
|
|
}
|
|
|
|
enum name_type {
|
|
BOTH,
|
|
FRONT,
|
|
BACK,
|
|
};
|
|
|
|
static void append_type_name(luaL_Buffer* B, int usr, const struct ctype* ct, enum name_type type)
|
|
{
|
|
size_t i;
|
|
lua_State* L = B->L;
|
|
|
|
usr = lua_absindex(L, usr);
|
|
|
|
if (type == FRONT || type == BOTH) {
|
|
if (ct->type != FUNCTION_PTR_TYPE && (ct->const_mask & (1 << ct->pointers))) {
|
|
luaL_addstring(B, "const ");
|
|
}
|
|
|
|
if (ct->is_unsigned) {
|
|
luaL_addstring(B, "unsigned ");
|
|
}
|
|
|
|
switch (ct->type) {
|
|
case ENUM_TYPE:
|
|
luaL_addstring(B, "enum ");
|
|
goto get_name;
|
|
|
|
case STRUCT_TYPE:
|
|
luaL_addstring(B, "struct ");
|
|
goto get_name;
|
|
|
|
case UNION_TYPE:
|
|
luaL_addstring(B, "union ");
|
|
goto get_name;
|
|
|
|
get_name:
|
|
lua_pushlightuserdata(L, &g_name_key);
|
|
lua_rawget(L, usr);
|
|
luaL_addvalue(B);
|
|
break;
|
|
|
|
case FUNCTION_TYPE:
|
|
case FUNCTION_PTR_TYPE:
|
|
lua_pushlightuserdata(L, &g_front_name_key);
|
|
lua_rawget(L, usr);
|
|
luaL_addvalue(B);
|
|
break;
|
|
|
|
case VOID_TYPE:
|
|
luaL_addstring(B, "void");
|
|
break;
|
|
case BOOL_TYPE:
|
|
luaL_addstring(B, "bool");
|
|
break;
|
|
case DOUBLE_TYPE:
|
|
luaL_addstring(B, "double");
|
|
break;
|
|
case LONG_DOUBLE_TYPE:
|
|
luaL_addstring(B, "long double");
|
|
break;
|
|
case FLOAT_TYPE:
|
|
luaL_addstring(B, "float");
|
|
break;
|
|
case COMPLEX_LONG_DOUBLE_TYPE:
|
|
luaL_addstring(B, "long complex double");
|
|
break;
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
luaL_addstring(B, "complex double");
|
|
break;
|
|
case COMPLEX_FLOAT_TYPE:
|
|
luaL_addstring(B, "complex float");
|
|
break;
|
|
case INT8_TYPE:
|
|
luaL_addstring(B, "char");
|
|
break;
|
|
case INT16_TYPE:
|
|
luaL_addstring(B, "short");
|
|
break;
|
|
case INT32_TYPE:
|
|
luaL_addstring(B, "int");
|
|
break;
|
|
case INT64_TYPE:
|
|
luaL_addstring(B, "long long");
|
|
break;
|
|
|
|
case INTPTR_TYPE:
|
|
if (sizeof(intptr_t) == sizeof(int32_t)) {
|
|
luaL_addstring(B, "long");
|
|
} else if (sizeof(intptr_t) == sizeof(int64_t)) {
|
|
luaL_addstring(B, "long long");
|
|
} else {
|
|
luaL_error(L, "internal error - bad type");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "internal error - bad type %d", ct->type);
|
|
}
|
|
|
|
for (i = 0; i < ct->pointers - ct->is_array; i++) {
|
|
luaL_addchar(B, '*');
|
|
if (ct->const_mask & (1 << (ct->pointers - i - 1))) {
|
|
luaL_addstring(B, " const");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == BOTH || type == BACK) {
|
|
if (ct->is_reference) {
|
|
luaL_addstring(B, "(&)");
|
|
}
|
|
|
|
if (ct->is_variable_array && !ct->variable_size_known) {
|
|
luaL_addstring(B, "[?]");
|
|
} else if (ct->is_array) {
|
|
lua_pushfstring(L, "[%d]", (int) ct->array_size);
|
|
luaL_addvalue(B);
|
|
}
|
|
|
|
if (ct->type == FUNCTION_PTR_TYPE || ct->type == FUNCTION_TYPE) {
|
|
lua_pushlightuserdata(L, &g_back_name_key);
|
|
lua_rawget(L, usr);
|
|
luaL_addvalue(B);
|
|
}
|
|
|
|
if (ct->is_bitfield) {
|
|
lua_pushfstring(L, " : %d", (int) ct->bit_size);
|
|
luaL_addvalue(B);
|
|
}
|
|
}
|
|
}
|
|
|
|
void push_type_name(lua_State* L, int usr, const struct ctype* ct)
|
|
{
|
|
luaL_Buffer B;
|
|
usr = lua_absindex(L, usr);
|
|
luaL_buffinit(L, &B);
|
|
append_type_name(&B, usr, ct, BOTH);
|
|
luaL_pushresult(&B);
|
|
}
|
|
|
|
static void push_function_type_strings(lua_State* L, int usr, const struct ctype* ct)
|
|
{
|
|
size_t i, args;
|
|
luaL_Buffer B;
|
|
int top = lua_gettop(L);
|
|
const struct ctype* ret_ct;
|
|
|
|
int arg_ct = top+3;
|
|
int arg_usr = top+4;
|
|
int ret_usr = top+6;
|
|
|
|
usr = lua_absindex(L, usr);
|
|
|
|
/* return type */
|
|
lua_settop(L, top+4); /* room for two returns and two temp positions */
|
|
lua_rawgeti(L, usr, 0);
|
|
lua_getuservalue(L, -1);
|
|
ret_ct = (const struct ctype*) lua_touserdata(L, -2);
|
|
|
|
luaL_buffinit(L, &B);
|
|
append_type_name(&B, ret_usr, ret_ct, FRONT);
|
|
|
|
if (ret_ct->type != FUNCTION_TYPE && ret_ct->type != FUNCTION_PTR_TYPE) {
|
|
luaL_addchar(&B, ' ');
|
|
}
|
|
|
|
switch (ct->calling_convention) {
|
|
case STD_CALL:
|
|
luaL_addstring(&B, "(__stdcall *");
|
|
break;
|
|
case FAST_CALL:
|
|
luaL_addstring(&B, "(__fastcall *");
|
|
break;
|
|
case C_CALL:
|
|
luaL_addstring(&B, "(*");
|
|
break;
|
|
default:
|
|
luaL_error(L, "internal error - unknown calling convention");
|
|
}
|
|
|
|
luaL_pushresult(&B);
|
|
lua_replace(L, top+1);
|
|
|
|
luaL_buffinit(L, &B);
|
|
luaL_addstring(&B, ")(");
|
|
|
|
/* arguments */
|
|
args = lua_rawlen(L, usr);
|
|
for (i = 1; i <= args; i++) {
|
|
if (i > 1) {
|
|
luaL_addstring(&B, ", ");
|
|
}
|
|
|
|
/* note push the arg and user value below the indexes used by the buffer
|
|
* and use indexes relative to top to avoid problems due to the buffer
|
|
* system pushing a variable number of arguments onto the stack */
|
|
lua_rawgeti(L, usr, (int) i);
|
|
lua_replace(L, arg_ct);
|
|
lua_getuservalue(L, arg_ct);
|
|
lua_replace(L, arg_usr);
|
|
append_type_name(&B, arg_usr, (const struct ctype*) lua_touserdata(L, arg_ct), BOTH);
|
|
}
|
|
|
|
luaL_addstring(&B, ")");
|
|
append_type_name(&B, ret_usr, ret_ct, BACK);
|
|
luaL_pushresult(&B);
|
|
lua_replace(L, top+2);
|
|
|
|
lua_settop(L, top+2);
|
|
assert(lua_isstring(L, top+1) && lua_isstring(L, top+2));
|
|
}
|
|
|
|
/* parses from after the opening paranthesis to after the closing parenthesis */
|
|
static void parse_function_arguments(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct)
|
|
{
|
|
struct token tok;
|
|
int args = 0;
|
|
int top = lua_gettop(L);
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
for (;;) {
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_CLOSE_PAREN) {
|
|
break;
|
|
}
|
|
|
|
if (args) {
|
|
if (tok.type != TOK_COMMA) {
|
|
luaL_error(L, "unexpected token in function argument %d on line %d", args, P->line);
|
|
}
|
|
|
|
require_token(L, P, &tok);
|
|
}
|
|
|
|
if (tok.type == TOK_VA_ARG) {
|
|
ct->has_var_arg = true;
|
|
check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected token after ... in function on line %d", P->line);
|
|
break;
|
|
|
|
} else if (tok.type == TOK_TOKEN) {
|
|
struct ctype at;
|
|
|
|
put_back(P);
|
|
parse_type(L, P, &at);
|
|
parse_argument(L, P, -1, &at, NULL, NULL);
|
|
|
|
assert(lua_gettop(L) == top + 2);
|
|
|
|
/* array arguments are just treated as their base pointer type */
|
|
at.is_array = 0;
|
|
|
|
/* check for the c style int func(void) and error on other uses of arguments of type void */
|
|
if (at.type == VOID_TYPE && at.pointers == 0) {
|
|
if (args) {
|
|
luaL_error(L, "can't have argument of type void on line %d", P->line);
|
|
}
|
|
|
|
check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected void in function on line %d", P->line);
|
|
lua_pop(L, 2);
|
|
break;
|
|
}
|
|
|
|
push_ctype(L, -1, &at);
|
|
lua_rawseti(L, ct_usr, ++args);
|
|
|
|
lua_pop(L, 2); /* parse_type and parse_argument at_usr */
|
|
|
|
} else {
|
|
luaL_error(L, "unexpected token in function argument %d on line %d", args+1, P->line);
|
|
}
|
|
}
|
|
|
|
assert(lua_gettop(L) == top);
|
|
}
|
|
|
|
static int max_bitfield_size(int type)
|
|
{
|
|
switch (type) {
|
|
case BOOL_TYPE:
|
|
return 1;
|
|
case INT8_TYPE:
|
|
return 8;
|
|
case INT16_TYPE:
|
|
return 16;
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
return 32;
|
|
case INT64_TYPE:
|
|
return 64;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname);
|
|
|
|
/* parses from after the first ( in a function declaration or function pointer
|
|
* can be one of:
|
|
* void foo(...) before ...
|
|
* void (foo)(...) before foo
|
|
* void (* <>)(...) before <> which is the inner type
|
|
*/
|
|
static struct ctype* parse_function(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname)
|
|
{
|
|
/* We have a function pointer or a function. The usr table will
|
|
* get replaced by the canonical one (if there is one) in
|
|
* find_canonical_usr after all the arguments and returns have
|
|
* been parsed. */
|
|
struct token tok;
|
|
int top = lua_gettop(L);
|
|
struct ctype* ret;
|
|
|
|
lua_newtable(L);
|
|
ret = push_ctype(L, ct_usr, ct);
|
|
lua_rawseti(L, -2, 0);
|
|
ct_usr = lua_gettop(L);
|
|
|
|
memset(ct, 0, sizeof(*ct));
|
|
ct->base_size = sizeof(void (*)());
|
|
ct->align_mask = min(FUNCTION_ALIGN_MASK, P->align_mask);
|
|
ct->type = FUNCTION_TYPE;
|
|
ct->is_defined = 1;
|
|
|
|
if (name->type == TOK_NIL) {
|
|
for (;;) {
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_STAR) {
|
|
|
|
if (ct->type == FUNCTION_TYPE) {
|
|
ct->type = FUNCTION_PTR_TYPE;
|
|
} else if (ct->pointers == POINTER_MAX) {
|
|
luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line);
|
|
} else {
|
|
ct->pointers++;
|
|
ct->const_mask <<= 1;
|
|
}
|
|
|
|
} else if (parse_attribute(L, P, &tok, ct, asmname)) {
|
|
/* parse_attribute sets the appropriate fields */
|
|
|
|
} else {
|
|
/* call parse_argument to handle the inner contents
|
|
* e.g. the <> in "void (* <>) (...)". Note that the
|
|
* inner contents can itself be a function, a function
|
|
* ptr, array, etc (e.g. "void (*signal(int sig, void
|
|
* (*func)(int)))(int)" ).
|
|
*/
|
|
put_back(P);
|
|
ct = parse_argument2(L, P, ct_usr, ct, name, asmname);
|
|
break;
|
|
}
|
|
}
|
|
|
|
check_token(L, P, TOK_CLOSE_PAREN, NULL, "unexpected token in function on line %d", P->line);
|
|
check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token in function on line %d", P->line);
|
|
}
|
|
|
|
parse_function_arguments(L, P, ct_usr, ct);
|
|
|
|
/* if we have an inner function then set the outer function ptr as its
|
|
* return type and return the inner function
|
|
* e.g. for void (* <signal(int, void (*)(int))> )(int) inner is
|
|
* surrounded by <>, return type is void (*)(int)
|
|
*/
|
|
if (lua_gettop(L) == ct_usr+1) {
|
|
lua_replace(L, ct_usr);
|
|
}
|
|
|
|
assert(lua_gettop(L) == top + 1 && lua_istable(L, -1));
|
|
return ret;
|
|
}
|
|
|
|
static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname)
|
|
{
|
|
struct token tok;
|
|
int top = lua_gettop(L);
|
|
int ft_usr = 0;
|
|
|
|
luaL_checkstack(L, 10, "function too complex");
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
for (;;) {
|
|
if (!next_token(L, P, &tok)) {
|
|
/* we've reached the end of the string */
|
|
break;
|
|
|
|
} else if (tok.type == TOK_STAR) {
|
|
if (ct->pointers == POINTER_MAX) {
|
|
luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line);
|
|
}
|
|
|
|
ct->pointers++;
|
|
ct->const_mask <<= 1;
|
|
|
|
/* __declspec(align(#)) may come before the type in a member */
|
|
if (!ct->is_packed) {
|
|
ct->align_mask = max(min(PTR_ALIGN_MASK, P->align_mask), ct->align_mask);
|
|
}
|
|
|
|
} else if (tok.type == TOK_REFERENCE) {
|
|
luaL_error(L, "NYI: c++ reference types");
|
|
|
|
} else if (parse_attribute(L, P, &tok, ct, asmname)) {
|
|
/* parse attribute has filled out appropriate fields in type */
|
|
|
|
} else if (tok.type == TOK_OPEN_PAREN) {
|
|
ct = parse_function(L, P, ct_usr, ct, name, asmname);
|
|
ft_usr = lua_gettop(L);
|
|
|
|
} else if (tok.type == TOK_OPEN_SQUARE) {
|
|
/* array */
|
|
if (ct->pointers == POINTER_MAX) {
|
|
luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers");
|
|
}
|
|
ct->is_array = 1;
|
|
ct->pointers++;
|
|
ct->const_mask <<= 1;
|
|
require_token(L, P, &tok);
|
|
|
|
if (ct->pointers == 1 && !ct->is_defined) {
|
|
luaL_error(L, "array of undefined type on line %d", P->line);
|
|
}
|
|
|
|
if (ct->is_variable_struct || ct->is_variable_array) {
|
|
luaL_error(L, "can't have an array of a variably sized type on line %d", P->line);
|
|
}
|
|
|
|
if (tok.type == TOK_QUESTION) {
|
|
ct->is_variable_array = 1;
|
|
ct->variable_increment = (ct->pointers > 1) ? sizeof(void*) : ct->base_size;
|
|
check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line);
|
|
|
|
} else if (tok.type == TOK_CLOSE_SQUARE) {
|
|
ct->array_size = 0;
|
|
|
|
} else if (tok.type == TOK_TOKEN && IS_RESTRICT(tok)) {
|
|
/* odd gcc extension foo[__restrict] for arguments */
|
|
ct->array_size = 0;
|
|
check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line);
|
|
|
|
} else {
|
|
int64_t asize;
|
|
put_back(P);
|
|
asize = calculate_constant(L, P);
|
|
if (asize < 0) {
|
|
luaL_error(L, "array size can not be negative on line %d", P->line);
|
|
}
|
|
ct->array_size = (size_t) asize;
|
|
check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line);
|
|
}
|
|
|
|
} else if (tok.type == TOK_COLON) {
|
|
int64_t bsize = calculate_constant(L, P);
|
|
|
|
if (ct->pointers || bsize < 0 || bsize > max_bitfield_size(ct->type)) {
|
|
luaL_error(L, "invalid bitfield on line %d", P->line);
|
|
}
|
|
|
|
ct->is_bitfield = 1;
|
|
ct->bit_size = (unsigned) bsize;
|
|
|
|
} else if (tok.type != TOK_TOKEN) {
|
|
/* we've reached the end of the declaration */
|
|
put_back(P);
|
|
break;
|
|
|
|
} else if (IS_CONST(tok)) {
|
|
ct->const_mask |= 1;
|
|
|
|
} else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) {
|
|
/* ignored for now */
|
|
|
|
} else {
|
|
*name = tok;
|
|
}
|
|
}
|
|
|
|
assert((ft_usr == 0 && lua_gettop(L) == top) || (lua_gettop(L) == top + 1 && ft_usr == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1))));
|
|
return ct;
|
|
}
|
|
|
|
static void find_canonical_usr(lua_State* L, int ct_usr, const struct ctype *ct)
|
|
{
|
|
struct ctype rt;
|
|
int top = lua_gettop(L);
|
|
int types;
|
|
|
|
if (ct->type != FUNCTION_PTR_TYPE && ct->type != FUNCTION_TYPE) {
|
|
return;
|
|
}
|
|
|
|
luaL_checkstack(L, 10, "function too complex");
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
/* check to see if we already have the canonical usr table */
|
|
lua_pushlightuserdata(L, &g_name_key);
|
|
lua_rawget(L, ct_usr);
|
|
if (!lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
assert(top == lua_gettop(L));
|
|
return;
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
assert(top == lua_gettop(L));
|
|
|
|
/* first canonize the return type */
|
|
lua_rawgeti(L, ct_usr, 0);
|
|
rt = *(struct ctype*) lua_touserdata(L, -1);
|
|
lua_getuservalue(L, -1);
|
|
find_canonical_usr(L, -1, &rt);
|
|
push_ctype(L, -1, &rt);
|
|
lua_rawseti(L, ct_usr, 0);
|
|
lua_pop(L, 2); /* return ctype and usr */
|
|
|
|
assert(top == lua_gettop(L));
|
|
|
|
/* look up the type string in the types table */
|
|
push_upval(L, &types_key);
|
|
types = lua_gettop(L);
|
|
|
|
push_function_type_strings(L, ct_usr, ct);
|
|
lua_pushvalue(L, -2);
|
|
lua_pushvalue(L, -2);
|
|
lua_concat(L, 2);
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_rawget(L, types);
|
|
|
|
assert(lua_gettop(L) == types + 4 && types == top + 1);
|
|
/* stack: types, front, back, both, looked up value */
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
|
|
lua_pushlightuserdata(L, &g_front_name_key);
|
|
lua_pushvalue(L, -4);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
lua_pushlightuserdata(L, &g_back_name_key);
|
|
lua_pushvalue(L, -3);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
lua_pushlightuserdata(L, &g_name_key);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, ct_usr);
|
|
|
|
lua_pushvalue(L, -1);
|
|
push_ctype(L, ct_usr, ct);
|
|
lua_rawset(L, types);
|
|
} else {
|
|
lua_getuservalue(L, -1);
|
|
lua_replace(L, ct_usr);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
lua_pop(L, 4);
|
|
assert(top == lua_gettop(L) && types == top + 1);
|
|
}
|
|
|
|
|
|
/* parses after the main base type of a typedef, function argument or
|
|
* struct/union member
|
|
* eg for const void* bar[3] the base type is void with the subtype so far of
|
|
* const, this parses the "* bar[3]" and updates the type argument
|
|
*
|
|
* ct_usr and type must be as filled out by parse_type
|
|
*
|
|
* pushes the updated user value on the top of the stack
|
|
*/
|
|
void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* pname, struct parser* asmname)
|
|
{
|
|
struct token tok, name;
|
|
int top = lua_gettop(L);
|
|
|
|
memset(&name, 0, sizeof(name));
|
|
parse_argument2(L, P, ct_usr, ct, &name, asmname);
|
|
|
|
for (;;) {
|
|
if (!next_token(L, P, &tok)) {
|
|
break;
|
|
} else if (parse_attribute(L, P, &tok, ct, asmname)) {
|
|
/* parse_attribute sets the appropriate fields */
|
|
} else {
|
|
put_back(P);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lua_gettop(L) == top) {
|
|
lua_pushvalue(L, ct_usr);
|
|
}
|
|
|
|
find_canonical_usr(L, -1, ct);
|
|
|
|
if (pname) {
|
|
*pname = name;
|
|
}
|
|
}
|
|
|
|
static void parse_typedef(lua_State* L, struct parser* P)
|
|
{
|
|
struct token tok;
|
|
struct ctype base_type;
|
|
int top = lua_gettop(L);
|
|
|
|
parse_type(L, P, &base_type);
|
|
|
|
for (;;) {
|
|
struct ctype arg_type = base_type;
|
|
struct token name;
|
|
|
|
memset(&name, 0, sizeof(name));
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
parse_argument(L, P, -1, &arg_type, &name, NULL);
|
|
assert(lua_gettop(L) == top + 2);
|
|
|
|
if (!name.size) {
|
|
luaL_error(L, "Can't have a typedef without a name on line %d", P->line);
|
|
} else if (arg_type.is_variable_array) {
|
|
luaL_error(L, "Can't typedef a variable length array on line %d", P->line);
|
|
}
|
|
|
|
push_upval(L, &types_key);
|
|
lua_pushlstring(L, name.str, name.size);
|
|
push_ctype(L, -3, &arg_type);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 2); /* types and parse_argument usr tbl */
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_SEMICOLON) {
|
|
break;
|
|
} else if (tok.type != TOK_COMMA) {
|
|
luaL_error(L, "Unexpected character in typedef on line %d", P->line);
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1); /* parse_type usr tbl */
|
|
assert(lua_gettop(L) == top);
|
|
}
|
|
|
|
static bool is_hex(char ch)
|
|
{ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'); }
|
|
|
|
static bool is_digit(char ch)
|
|
{ return '0' <= ch && ch <= '9'; }
|
|
|
|
static int from_hex(char ch)
|
|
{
|
|
if (ch >= 'a') {
|
|
return ch - 'a' + 10;
|
|
} else if (ch >= 'A') {
|
|
return ch - 'A' + 10;
|
|
} else {
|
|
return ch - '0';
|
|
}
|
|
}
|
|
|
|
static void push_strings(lua_State* L, struct parser* P)
|
|
{
|
|
luaL_Buffer B;
|
|
luaL_buffinit(L, &B);
|
|
|
|
for (;;) {
|
|
const char *p, *e;
|
|
char *t, *s;
|
|
struct token tok;
|
|
|
|
require_token(L, P, &tok);
|
|
if (tok.type != TOK_STRING) {
|
|
break;
|
|
}
|
|
|
|
p = tok.str;
|
|
e = p + tok.size;
|
|
|
|
t = luaL_prepbuffsize(&B, tok.size);
|
|
s = t;
|
|
|
|
while (p < e) {
|
|
if (*p == '\\') {
|
|
if (++p == e) {
|
|
luaL_error(L, "parse error in string");
|
|
}
|
|
switch (*p) {
|
|
case '\\': *(t++) = '\\'; p++; break;
|
|
case '\"': *(t++) = '\"'; p++; break;
|
|
case '\'': *(t++) = '\''; p++; break;
|
|
case 'n': *(t++) = '\n'; p++; break;
|
|
case 'r': *(t++) = '\r'; p++; break;
|
|
case 'b': *(t++) = '\b'; p++; break;
|
|
case 't': *(t++) = '\t'; p++; break;
|
|
case 'f': *(t++) = '\f'; p++; break;
|
|
case 'a': *(t++) = '\a'; p++; break;
|
|
case 'v': *(t++) = '\v'; p++; break;
|
|
case 'e': *(t++) = 0x1B; p++; break;
|
|
case 'x':
|
|
{
|
|
uint8_t u;
|
|
p++;
|
|
if (p + 2 > e || !is_hex(p[0]) || !is_hex(p[1])) {
|
|
luaL_error(L, "parse error in string");
|
|
}
|
|
u = (from_hex(p[0]) << 4) | from_hex(p[1]);
|
|
*(t++) = *(char*) &u;
|
|
p += 2;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
uint8_t u;
|
|
const char* e2 = min(p + 3, e);
|
|
if (!is_digit(*p)) {
|
|
luaL_error(L, "parse error in string");
|
|
}
|
|
u = *p - '0';
|
|
p++;
|
|
while (is_digit(*p) && p < e2) {
|
|
u = 10*u + *p-'0';
|
|
p++;
|
|
}
|
|
*(t++) = *(char*) &u;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
*(t++) = *(p++);
|
|
}
|
|
}
|
|
|
|
luaL_addsize(&B, t-s);
|
|
}
|
|
|
|
luaL_pushresult(&B);
|
|
}
|
|
|
|
#define END 0
|
|
#define PRAGMA_POP 1
|
|
|
|
static int parse_root(lua_State* L, struct parser* P)
|
|
{
|
|
int top = lua_gettop(L);
|
|
struct token tok;
|
|
|
|
while (next_token(L, P, &tok)) {
|
|
/* we can have:
|
|
* struct definition
|
|
* enum definition
|
|
* union definition
|
|
* struct/enum/union declaration
|
|
* typedef
|
|
* function declaration
|
|
* pragma pack
|
|
*/
|
|
|
|
assert(lua_gettop(L) == top);
|
|
|
|
if (tok.type == TOK_SEMICOLON) {
|
|
/* empty semicolon in root continue on */
|
|
|
|
} else if (tok.type == TOK_POUND) {
|
|
|
|
check_token(L, P, TOK_TOKEN, "pragma", "unexpected pre processor directive on line %d", P->line);
|
|
check_token(L, P, TOK_TOKEN, "pack", "unexpected pre processor directive on line %d", P->line);
|
|
check_token(L, P, TOK_OPEN_PAREN, "", "invalid pack directive on line %d", P->line);
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_NUMBER) {
|
|
if (tok.integer != 1 && tok.integer != 2 && tok.integer != 4 && tok.integer != 8 && tok.integer != 16) {
|
|
luaL_error(L, "pack directive with invalid pack size on line %d", P->line);
|
|
}
|
|
|
|
P->align_mask = (unsigned) (tok.integer - 1);
|
|
check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line);
|
|
|
|
} else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "push")) {
|
|
int line = P->line;
|
|
unsigned previous_alignment = P->align_mask;
|
|
|
|
check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line);
|
|
|
|
if (parse_root(L, P) != PRAGMA_POP) {
|
|
luaL_error(L, "reached end of string without a pragma pop to match the push on line %d", line);
|
|
}
|
|
|
|
P->align_mask = previous_alignment;
|
|
|
|
} else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "pop")) {
|
|
check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line);
|
|
return PRAGMA_POP;
|
|
|
|
} else {
|
|
luaL_error(L, "invalid pack directive on line %d", P->line);
|
|
}
|
|
|
|
|
|
} else if (tok.type != TOK_TOKEN) {
|
|
return luaL_error(L, "unexpected character on line %d", P->line);
|
|
|
|
} else if (IS_LITERAL(tok, "__extension__")) {
|
|
/* ignore */
|
|
continue;
|
|
|
|
} else if (IS_LITERAL(tok, "extern")) {
|
|
/* ignore extern as data and functions can only be extern */
|
|
continue;
|
|
|
|
} else if (IS_LITERAL(tok, "typedef")) {
|
|
parse_typedef(L, P);
|
|
|
|
} else if (IS_LITERAL(tok, "static")) {
|
|
struct ctype at;
|
|
|
|
int64_t val;
|
|
require_token(L, P, &tok);
|
|
if (!IS_CONST(tok)) {
|
|
luaL_error(L, "expected 'static const int' on line %d", P->line);
|
|
}
|
|
|
|
parse_type(L, P, &at);
|
|
|
|
require_token(L, P, &tok);
|
|
if (tok.type != TOK_TOKEN) {
|
|
luaL_error(L, "expected constant name after 'static const int' on line %d", P->line);
|
|
}
|
|
|
|
check_token(L, P, TOK_ASSIGN, "", "expected = after 'static const int <name>' on line %d", P->line);
|
|
|
|
val = calculate_constant(L, P);
|
|
|
|
check_token(L, P, TOK_SEMICOLON, "", "expected ; after 'static const int' definition on line %d", P->line);
|
|
|
|
push_upval(L, &constants_key);
|
|
lua_pushlstring(L, tok.str, tok.size);
|
|
|
|
switch (at.type) {
|
|
case INT8_TYPE:
|
|
case INT16_TYPE:
|
|
case INT32_TYPE:
|
|
if (at.is_unsigned)
|
|
lua_pushnumber(L, (unsigned int) val);
|
|
else
|
|
lua_pushnumber(L, (int) val);
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, "expected a valid 8-, 16-, or 32-bit signed or unsigned integer type after 'static const' on line %d", P->line);
|
|
}
|
|
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 2); /*constants and type*/
|
|
|
|
} else {
|
|
/* type declaration, type definition, or function declaration */
|
|
struct ctype type;
|
|
struct token name;
|
|
struct parser asmname;
|
|
|
|
memset(&name, 0, sizeof(name));
|
|
memset(&asmname, 0, sizeof(asmname));
|
|
|
|
put_back(P);
|
|
parse_type(L, P, &type);
|
|
|
|
for (;;) {
|
|
parse_argument(L, P, -1, &type, &name, &asmname);
|
|
|
|
if (name.size) {
|
|
/* global/function declaration */
|
|
|
|
/* set asmname_tbl[name] = asmname */
|
|
if (asmname.next) {
|
|
push_upval(L, &asmname_key);
|
|
lua_pushlstring(L, name.str, name.size);
|
|
push_strings(L, &asmname);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1); /* asmname upval */
|
|
}
|
|
|
|
push_upval(L, &functions_key);
|
|
lua_pushlstring(L, name.str, name.size);
|
|
push_ctype(L, -3, &type);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1); /* functions upval */
|
|
} else {
|
|
/* type declaration/definition - already been processed */
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
require_token(L, P, &tok);
|
|
|
|
if (tok.type == TOK_SEMICOLON) {
|
|
break;
|
|
} else if (tok.type != TOK_COMMA) {
|
|
luaL_error(L, "missing semicolon on line %d", P->line);
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
return END;
|
|
}
|
|
|
|
int ffi_cdef(lua_State* L)
|
|
{
|
|
struct parser P;
|
|
|
|
P.line = 1;
|
|
P.prev = P.next = luaL_checkstring(L, 1);
|
|
P.align_mask = DEFAULT_ALIGN_MASK;
|
|
|
|
if (parse_root(L, &P) == PRAGMA_POP) {
|
|
luaL_error(L, "pragma pop without an associated push on line %d", P.line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* calculate_constant handles operator precedence by having a number of
|
|
* recursive commands each of which computes the result at that level of
|
|
* precedence and above. calculate_constant1 is the highest precedence
|
|
*/
|
|
|
|
static int try_cast(lua_State* L)
|
|
{
|
|
struct parser* P = (struct parser*) lua_touserdata(L, 1);
|
|
struct ctype ct;
|
|
struct token name, tok;
|
|
memset(&name, 0, sizeof(name));
|
|
|
|
parse_type(L, P, &ct);
|
|
parse_argument(L, P, -1, &ct, &name, NULL);
|
|
|
|
require_token(L, P, &tok);
|
|
if (tok.type != TOK_CLOSE_PAREN || name.size) {
|
|
return luaL_error(L, "invalid cast");
|
|
}
|
|
|
|
if (ct.pointers || ct.type != INT32_TYPE) {
|
|
return luaL_error(L, "unsupported cast on line %d", P->line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok);
|
|
|
|
/* () */
|
|
static int64_t calculate_constant1(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t ret;
|
|
|
|
if (tok->type == TOK_NUMBER) {
|
|
ret = tok->integer;
|
|
next_token(L, P, tok);
|
|
return ret;
|
|
|
|
} else if (tok->type == TOK_TOKEN) {
|
|
/* look up name in constants table */
|
|
push_upval(L, &constants_key);
|
|
lua_pushlstring(L, tok->str, tok->size);
|
|
lua_rawget(L, -2);
|
|
lua_remove(L, -2); /* constants table */
|
|
|
|
if (!lua_isnumber(L, -1)) {
|
|
lua_pushlstring(L, tok->str, tok->size);
|
|
luaL_error(L, "use of undefined constant %s on line %d", lua_tostring(L, -1), P->line);
|
|
}
|
|
|
|
ret = (int64_t) lua_tonumber(L, -1);
|
|
lua_pop(L, 1);
|
|
next_token(L, P, tok);
|
|
return ret;
|
|
|
|
} else if (tok->type == TOK_OPEN_PAREN) {
|
|
struct parser before_cast = *P;
|
|
int top = lua_gettop(L);
|
|
|
|
/* see if this is a numeric cast, which we ignore */
|
|
lua_pushcfunction(L, &try_cast);
|
|
lua_pushlightuserdata(L, P);
|
|
if (!lua_pcall(L, 1, 0, 0)) {
|
|
next_token(L, P, tok);
|
|
return calculate_constant2(L, P, tok);
|
|
}
|
|
lua_settop(L, top);
|
|
|
|
*P = before_cast;
|
|
ret = calculate_constant(L, P);
|
|
|
|
require_token(L, P, tok);
|
|
if (tok->type != TOK_CLOSE_PAREN) {
|
|
luaL_error(L, "error whilst parsing constant at line %d", P->line);
|
|
}
|
|
|
|
next_token(L, P, tok);
|
|
return ret;
|
|
|
|
} else {
|
|
return luaL_error(L, "unexpected token whilst parsing constant at line %d", P->line);
|
|
}
|
|
}
|
|
|
|
/* ! and ~, unary + and -, and sizeof */
|
|
static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
if (tok->type == TOK_LOGICAL_NOT) {
|
|
require_token(L, P, tok);
|
|
return !calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_BITWISE_NOT) {
|
|
require_token(L, P, tok);
|
|
return ~calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_PLUS) {
|
|
require_token(L, P, tok);
|
|
return calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_MINUS) {
|
|
require_token(L, P, tok);
|
|
return -calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_TOKEN &&
|
|
(IS_LITERAL(*tok, "sizeof")
|
|
|| IS_LITERAL(*tok, "alignof")
|
|
|| IS_LITERAL(*tok, "__alignof__")
|
|
|| IS_LITERAL(*tok, "__alignof"))) {
|
|
|
|
bool issize = IS_LITERAL(*tok, "sizeof");
|
|
struct ctype type;
|
|
|
|
require_token(L, P, tok);
|
|
if (tok->type != TOK_OPEN_PAREN) {
|
|
luaL_error(L, "invalid sizeof at line %d", P->line);
|
|
}
|
|
|
|
parse_type(L, P, &type);
|
|
parse_argument(L, P, -1, &type, NULL, NULL);
|
|
lua_pop(L, 2);
|
|
|
|
require_token(L, P, tok);
|
|
if (tok->type != TOK_CLOSE_PAREN) {
|
|
luaL_error(L, "invalid sizeof at line %d", P->line);
|
|
}
|
|
|
|
next_token(L, P, tok);
|
|
|
|
return issize ? ctype_size(L, &type) : type.align_mask + 1;
|
|
|
|
} else {
|
|
return calculate_constant1(L, P, tok);
|
|
}
|
|
}
|
|
|
|
/* binary * / and % (left associative) */
|
|
static int64_t calculate_constant3(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant2(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_MULTIPLY) {
|
|
require_token(L, P, tok);
|
|
left *= calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_DIVIDE) {
|
|
require_token(L, P, tok);
|
|
left /= calculate_constant2(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_MODULUS) {
|
|
require_token(L, P, tok);
|
|
left %= calculate_constant2(L, P, tok);
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary + and - (left associative) */
|
|
static int64_t calculate_constant4(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant3(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_PLUS) {
|
|
require_token(L, P, tok);
|
|
left += calculate_constant3(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_MINUS) {
|
|
require_token(L, P, tok);
|
|
left -= calculate_constant3(L, P, tok);
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary << and >> (left associative) */
|
|
static int64_t calculate_constant5(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant4(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_LEFT_SHIFT) {
|
|
require_token(L, P, tok);
|
|
left <<= calculate_constant4(L, P, tok);
|
|
|
|
} else if (tok->type == TOK_RIGHT_SHIFT) {
|
|
require_token(L, P, tok);
|
|
left >>= calculate_constant4(L, P, tok);
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary <, <=, >, and >= (left associative) */
|
|
static int64_t calculate_constant6(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant5(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_LESS) {
|
|
require_token(L, P, tok);
|
|
left = (left < calculate_constant5(L, P, tok));
|
|
|
|
} else if (tok->type == TOK_LESS_EQUAL) {
|
|
require_token(L, P, tok);
|
|
left = (left <= calculate_constant5(L, P, tok));
|
|
|
|
} else if (tok->type == TOK_GREATER) {
|
|
require_token(L, P, tok);
|
|
left = (left > calculate_constant5(L, P, tok));
|
|
|
|
} else if (tok->type == TOK_GREATER_EQUAL) {
|
|
require_token(L, P, tok);
|
|
left = (left >= calculate_constant5(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary ==, != (left associative) */
|
|
static int64_t calculate_constant7(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant6(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_EQUAL) {
|
|
require_token(L, P, tok);
|
|
left = (left == calculate_constant6(L, P, tok));
|
|
|
|
} else if (tok->type == TOK_NOT_EQUAL) {
|
|
require_token(L, P, tok);
|
|
left = (left != calculate_constant6(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary & (left associative) */
|
|
static int64_t calculate_constant8(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant7(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_BITWISE_AND) {
|
|
require_token(L, P, tok);
|
|
left = (left & calculate_constant7(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary ^ (left associative) */
|
|
static int64_t calculate_constant9(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant8(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_BITWISE_XOR) {
|
|
require_token(L, P, tok);
|
|
left = (left ^ calculate_constant8(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary | (left associative) */
|
|
static int64_t calculate_constant10(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant9(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_BITWISE_OR) {
|
|
require_token(L, P, tok);
|
|
left = (left | calculate_constant9(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary && (left associative) */
|
|
static int64_t calculate_constant11(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant10(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_LOGICAL_AND) {
|
|
require_token(L, P, tok);
|
|
left = (left && calculate_constant10(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* binary || (left associative) */
|
|
static int64_t calculate_constant12(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant11(L, P, tok);
|
|
|
|
for (;;) {
|
|
if (tok->type == TOK_LOGICAL_OR) {
|
|
require_token(L, P, tok);
|
|
left = (left || calculate_constant11(L, P, tok));
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ternary ?: (right associative) */
|
|
static int64_t calculate_constant13(lua_State* L, struct parser* P, struct token* tok)
|
|
{
|
|
int64_t left = calculate_constant12(L, P, tok);
|
|
|
|
if (tok->type == TOK_QUESTION) {
|
|
int64_t middle, right;
|
|
require_token(L, P, tok);
|
|
middle = calculate_constant13(L, P, tok);
|
|
if (tok->type != TOK_COLON) {
|
|
luaL_error(L, "invalid ternery (? :) in constant on line %d", P->line);
|
|
}
|
|
require_token(L, P, tok);
|
|
right = calculate_constant13(L, P, tok);
|
|
return left ? middle : right;
|
|
|
|
} else {
|
|
return left;
|
|
}
|
|
}
|
|
|
|
int64_t calculate_constant(lua_State* L, struct parser* P)
|
|
{
|
|
struct token tok;
|
|
int64_t ret;
|
|
require_token(L, P, &tok);
|
|
ret = calculate_constant13(L, P, &tok);
|
|
|
|
if (tok.type != TOK_NIL) {
|
|
put_back(P);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* vim: ts=4 sw=4 sts=4 et tw=78
|
|
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
|
|
*/
|
|
//#include "ffi.h"
|
|
#include <math.h>
|
|
#include <inttypes.h>
|
|
|
|
/* Set to 1 to get extra debugging on print */
|
|
#define DEBUG_TOSTRING 0
|
|
|
|
int jit_key;
|
|
int ctype_mt_key;
|
|
int cdata_mt_key;
|
|
int callback_mt_key;
|
|
int cmodule_mt_key;
|
|
int constants_key;
|
|
int types_key;
|
|
int gc_key;
|
|
int callbacks_key;
|
|
int functions_key;
|
|
int abi_key;
|
|
int next_unnamed_key;
|
|
int niluv_key;
|
|
int asmname_key;
|
|
|
|
void push_upval(lua_State* L, int* key)
|
|
{
|
|
lua_pushlightuserdata(L, key);
|
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
}
|
|
|
|
void set_upval(lua_State* L, int* key)
|
|
{
|
|
lua_pushlightuserdata(L, key);
|
|
lua_insert(L, -2);
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
}
|
|
|
|
int equals_upval(lua_State* L, int idx, int* key)
|
|
{
|
|
int ret;
|
|
lua_pushvalue(L, idx);
|
|
push_upval(L, key);
|
|
ret = lua_rawequal(L, -2, -1);
|
|
lua_pop(L, 2);
|
|
return ret;
|
|
}
|
|
|
|
struct jit* get_jit(lua_State* L)
|
|
{
|
|
struct jit* jit;
|
|
push_upval(L, &jit_key);
|
|
jit = (struct jit*) lua_touserdata(L, -1);
|
|
jit->L = L;
|
|
lua_pop(L, 1); /* still in registry */
|
|
return jit;
|
|
}
|
|
|
|
static int type_error(lua_State* L, int idx, const char* to_type, int to_usr, const struct ctype* to_ct)
|
|
{
|
|
luaL_Buffer B;
|
|
struct ctype ft;
|
|
|
|
assert(to_type || (to_usr && to_ct));
|
|
if (to_usr) {
|
|
to_usr = lua_absindex(L, to_usr);
|
|
}
|
|
|
|
idx = lua_absindex(L, idx);
|
|
|
|
luaL_buffinit(L, &B);
|
|
to_cdata(L, idx, &ft);
|
|
|
|
if (ft.type != INVALID_TYPE) {
|
|
push_type_name(L, -1, &ft);
|
|
lua_pushfstring(L, "unable to convert argument %d from cdata<%s> to cdata<", idx, lua_tostring(L, -1));
|
|
lua_remove(L, -2);
|
|
luaL_addvalue(&B);
|
|
} else {
|
|
lua_pushfstring(L, "unable to convert argument %d from lua<%s> to cdata<", idx, luaL_typename(L, idx));
|
|
luaL_addvalue(&B);
|
|
}
|
|
|
|
if (to_ct) {
|
|
push_type_name(L, to_usr, to_ct);
|
|
luaL_addvalue(&B);
|
|
} else {
|
|
luaL_addstring(&B, to_type);
|
|
}
|
|
|
|
luaL_addchar(&B, '>');
|
|
|
|
luaL_pushresult(&B);
|
|
return lua_error(L);
|
|
}
|
|
|
|
static int64_t check_intptr(lua_State* L, int idx, void* p, struct ctype* ct)
|
|
{
|
|
if (ct->type == INVALID_TYPE) {
|
|
int64_t ret;
|
|
memset(ct, 0, sizeof(*ct));
|
|
ct->base_size = 8;
|
|
ct->type = INT64_TYPE;
|
|
ct->is_defined = 1;
|
|
ret = luaL_checknumber(L, idx);
|
|
return ret;
|
|
|
|
} else if (ct->pointers) {
|
|
return (intptr_t) p;
|
|
}
|
|
|
|
switch (ct->type) {
|
|
case INTPTR_TYPE:
|
|
case FUNCTION_PTR_TYPE:
|
|
return *(intptr_t*) p;
|
|
|
|
case INT64_TYPE:
|
|
return *(int64_t*) p;
|
|
|
|
case INT32_TYPE:
|
|
return ct->is_unsigned ? (int64_t) *(uint32_t*) p : (int64_t) *(int32_t*) p;
|
|
|
|
case INT16_TYPE:
|
|
return ct->is_unsigned ? (int64_t) *(uint16_t*) p : (int64_t) *(int16_t*) p;
|
|
|
|
case INT8_TYPE:
|
|
return ct->is_unsigned ? (int64_t) *(uint8_t*) p : (int64_t) *(int8_t*) p;
|
|
|
|
default:
|
|
type_error(L, idx, "intptr_t", 0, NULL);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define TO_NUMBER(TYPE, ALLOW_POINTERS) \
|
|
TYPE real = 0, imag = 0; \
|
|
void* p; \
|
|
struct ctype ct; \
|
|
\
|
|
switch (lua_type(L, idx)) { \
|
|
case LUA_TBOOLEAN: \
|
|
real = (TYPE) lua_toboolean(L, idx); \
|
|
break; \
|
|
\
|
|
case LUA_TNUMBER: \
|
|
real = (TYPE) lua_tonumber(L, idx); \
|
|
break; \
|
|
\
|
|
case LUA_TSTRING: \
|
|
if (!ALLOW_POINTERS) { \
|
|
type_error(L, idx, #TYPE, 0, NULL); \
|
|
} \
|
|
real = (TYPE) (intptr_t) lua_tostring(L, idx); \
|
|
break; \
|
|
\
|
|
case LUA_TLIGHTUSERDATA: \
|
|
if (!ALLOW_POINTERS) { \
|
|
type_error(L, idx, #TYPE, 0, NULL); \
|
|
} \
|
|
real = (TYPE) (intptr_t) lua_topointer(L, idx); \
|
|
break; \
|
|
\
|
|
case LUA_TUSERDATA: \
|
|
p = to_cdata(L, idx, &ct); \
|
|
\
|
|
if (ct.type == INVALID_TYPE) { \
|
|
if (!ALLOW_POINTERS) { \
|
|
type_error(L, idx, #TYPE, 0, NULL); \
|
|
} \
|
|
real = (TYPE) (intptr_t) p; \
|
|
} else if (ct.pointers || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {\
|
|
if (!ALLOW_POINTERS) { \
|
|
type_error(L, idx, #TYPE, 0, NULL); \
|
|
} \
|
|
real = (TYPE) (intptr_t) p; \
|
|
} else if (ct.type == COMPLEX_DOUBLE_TYPE) { \
|
|
real = (TYPE) creal(*(complex_double*) p); \
|
|
imag = (TYPE) cimag(*(complex_double*) p); \
|
|
} else if (ct.type == COMPLEX_FLOAT_TYPE) { \
|
|
real = (TYPE) crealf(*(complex_float*) p); \
|
|
imag = (TYPE) cimagf(*(complex_float*) p); \
|
|
} else if (ct.type == DOUBLE_TYPE) { \
|
|
real = (TYPE) *(double*) p; \
|
|
} else if (ct.type == FLOAT_TYPE) { \
|
|
real = (TYPE) *(float*) p; \
|
|
} else { \
|
|
real = check_intptr(L, idx, p, &ct); \
|
|
} \
|
|
lua_pop(L, 1); \
|
|
break; \
|
|
\
|
|
case LUA_TNIL: \
|
|
real = (TYPE) 0; \
|
|
break; \
|
|
\
|
|
default: \
|
|
type_error(L, idx, #TYPE, 0, NULL); \
|
|
} \
|
|
|
|
static int64_t cast_int64(lua_State* L, int idx, int is_cast)
|
|
{ TO_NUMBER(int64_t, is_cast); (void) imag; return real; }
|
|
|
|
static uint64_t cast_uint64(lua_State* L, int idx, int is_cast)
|
|
{ TO_NUMBER(uint64_t, is_cast); (void) imag; return real; }
|
|
|
|
int32_t check_int32(lua_State* L, int idx)
|
|
{ return (int32_t) cast_int64(L, idx, 0); }
|
|
|
|
uint32_t check_uint32(lua_State* L, int idx)
|
|
{ return (uint32_t) cast_uint64(L, idx, 0); }
|
|
|
|
int64_t check_int64(lua_State* L, int idx)
|
|
{ return cast_int64(L, idx, 0); }
|
|
|
|
uint64_t check_uint64(lua_State* L, int idx)
|
|
{ return cast_uint64(L, idx, 0); }
|
|
|
|
static void do_check_double(lua_State* L, int idx, double* preal, double* pimag)
|
|
{
|
|
TO_NUMBER(double, 0);
|
|
if (preal) *preal = real;
|
|
if (pimag) *pimag = imag;
|
|
}
|
|
|
|
double check_double(lua_State* L, int idx)
|
|
{ double ret; do_check_double(L, idx, &ret, NULL); return ret; }
|
|
|
|
float check_float(lua_State* L, int idx)
|
|
{ double ret; do_check_double(L, idx, &ret, NULL); return ret; }
|
|
|
|
uintptr_t check_uintptr(lua_State* L, int idx)
|
|
{ TO_NUMBER(uintptr_t, 1); (void) imag; return real; }
|
|
|
|
#ifdef HAVE_COMPLEX
|
|
complex_double check_complex_double(lua_State* L, int idx)
|
|
{ double real, imag; do_check_double(L, idx, &real, &imag); return real + imag * 1i; }
|
|
|
|
complex_float check_complex_float(lua_State* L, int idx)
|
|
{ double real, imag; do_check_double(L, idx, &real, &imag); return real + imag * 1i; }
|
|
|
|
#else
|
|
complex_double check_complex_double(lua_State* L, int idx)
|
|
{
|
|
complex_double c;
|
|
do_check_double(L, idx, &c.real, &c.imag);
|
|
return c;
|
|
}
|
|
|
|
complex_float check_complex_float(lua_State* L, int idx)
|
|
{
|
|
complex_double d;
|
|
complex_float f;
|
|
do_check_double(L, idx, &d.real, &d.imag);
|
|
f.real = d.real;
|
|
f.imag = d.imag;
|
|
return f;
|
|
}
|
|
#endif
|
|
|
|
static size_t unpack_vararg(lua_State* L, int i, char* to)
|
|
{
|
|
void* p;
|
|
struct ctype ct;
|
|
|
|
switch (lua_type(L, i)) {
|
|
case LUA_TBOOLEAN:
|
|
*(int*) to = lua_toboolean(L, i);
|
|
return sizeof(int);
|
|
|
|
case LUA_TNUMBER:
|
|
*(double*) to = lua_tonumber(L, i);
|
|
return sizeof(double);
|
|
|
|
case LUA_TSTRING:
|
|
*(const char**) to = lua_tostring(L, i);
|
|
return sizeof(const char*);
|
|
|
|
case LUA_TLIGHTUSERDATA:
|
|
*(void**) to = lua_touserdata(L, i);
|
|
return sizeof(void*);
|
|
|
|
case LUA_TUSERDATA:
|
|
p = to_cdata(L, i, &ct);
|
|
|
|
if (ct.type == INVALID_TYPE) {
|
|
*(void**) to = p;
|
|
return sizeof(void*);
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
if (ct.pointers || ct.type == INTPTR_TYPE) {
|
|
*(void**) to = p;
|
|
return sizeof(void*);
|
|
|
|
} else if (ct.type == INT32_TYPE) {
|
|
*(int32_t*) to = *(int32_t*) p;
|
|
return sizeof(int32_t);
|
|
|
|
} else if (ct.type == INT64_TYPE) {
|
|
*(int64_t*) to = *(int64_t*) p;
|
|
return sizeof(int64_t);
|
|
}
|
|
goto err;
|
|
|
|
case LUA_TNIL:
|
|
*(void**) to = NULL;
|
|
return sizeof(void*);
|
|
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
return type_error(L, i, "vararg", 0, NULL);
|
|
}
|
|
|
|
void unpack_varargs_stack(lua_State* L, int first, int last, char* to)
|
|
{
|
|
int i;
|
|
|
|
for (i = first; i <= last; i++) {
|
|
to += unpack_vararg(L, i, to);
|
|
}
|
|
}
|
|
|
|
void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to)
|
|
{
|
|
int i;
|
|
|
|
for (i = first; i <= last; i++) {
|
|
int type = lua_type(L, i);
|
|
|
|
if (type == LUA_TNUMBER && --floats_to_skip >= 0) {
|
|
continue;
|
|
} else if (type != LUA_TNUMBER && --ints_to_skip >= 0) {
|
|
continue;
|
|
}
|
|
|
|
to += unpack_vararg(L, i, to);
|
|
}
|
|
}
|
|
|
|
void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to)
|
|
{
|
|
int i;
|
|
|
|
for (i = first; i <= last && max > 0; i++) {
|
|
if (lua_type(L, i) == LUA_TNUMBER) {
|
|
unpack_vararg(L, i, to);
|
|
to += sizeof(double);
|
|
max--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to)
|
|
{
|
|
int i;
|
|
|
|
for (i = first; i <= last && max > 0; i++) {
|
|
if (lua_type(L, i) != LUA_TNUMBER) {
|
|
unpack_vararg(L, i, to);
|
|
to += sizeof(void*);
|
|
max--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void unpack_varargs_reg(lua_State* L, int first, int last, char* to)
|
|
{
|
|
int i;
|
|
|
|
for (i = first; i <= last; i++) {
|
|
unpack_vararg(L, i, to);
|
|
to += sizeof(double);
|
|
}
|
|
}
|
|
|
|
/* to_enum tries to convert a value at idx to the enum type indicated by to_ct
|
|
* and uv to_usr. For strings this means it will do a string lookup for the
|
|
* enum type. It leaves the stack unchanged. Will throw an error if the type
|
|
* at idx can't be conerted.
|
|
*/
|
|
int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* to_ct)
|
|
{
|
|
int32_t ret;
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TSTRING:
|
|
/* lookup string in to_usr to find value */
|
|
to_usr = lua_absindex(L, to_usr);
|
|
lua_pushvalue(L, idx);
|
|
lua_rawget(L, to_usr);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
goto err;
|
|
}
|
|
|
|
ret = (int32_t) lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
return ret;
|
|
|
|
case LUA_TUSERDATA:
|
|
return check_int32(L, idx);
|
|
|
|
case LUA_TNIL:
|
|
return (int32_t) 0;
|
|
|
|
case LUA_TNUMBER:
|
|
return (int32_t) lua_tointeger(L, idx);
|
|
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
return type_error(L, idx, NULL, to_usr, to_ct);
|
|
}
|
|
|
|
/* to_pointer tries converts a value at idx to a pointer. It fills out ct and
|
|
* pushes the uv of the found type. It will throw a lua error if it can not
|
|
* convert the value to a pointer. */
|
|
static void* check_pointer(lua_State* L, int idx, struct ctype* ct)
|
|
{
|
|
void* p;
|
|
memset(ct, 0, sizeof(*ct));
|
|
ct->pointers = 1;
|
|
idx = lua_absindex(L, idx);
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TNIL:
|
|
ct->type = VOID_TYPE;
|
|
ct->is_null = 1;
|
|
lua_pushnil(L);
|
|
return NULL;
|
|
|
|
case LUA_TNUMBER:
|
|
ct->type = INTPTR_TYPE;
|
|
ct->is_unsigned = 1;
|
|
ct->pointers = 0;
|
|
lua_pushnil(L);
|
|
return (void*) (uintptr_t) lua_tonumber(L, idx);
|
|
|
|
case LUA_TLIGHTUSERDATA:
|
|
ct->type = VOID_TYPE;
|
|
lua_pushnil(L);
|
|
return lua_touserdata(L, idx);
|
|
|
|
case LUA_TSTRING:
|
|
ct->type = INT8_TYPE;
|
|
ct->is_unsigned = IS_CHAR_UNSIGNED;
|
|
ct->is_array = 1;
|
|
ct->base_size = 1;
|
|
ct->const_mask = 2;
|
|
lua_pushnil(L);
|
|
return (void*) lua_tolstring(L, idx, &ct->array_size);
|
|
|
|
case LUA_TUSERDATA:
|
|
p = to_cdata(L, idx, ct);
|
|
|
|
if (ct->type == INVALID_TYPE) {
|
|
/* some other type of user data */
|
|
ct->type = VOID_TYPE;
|
|
return lua_touserdata(L, idx);
|
|
} else if (ct->type == STRUCT_TYPE || ct->type == UNION_TYPE) {
|
|
return p;
|
|
} else {
|
|
return (void*) (intptr_t) check_intptr(L, idx, p, ct);
|
|
}
|
|
break;
|
|
}
|
|
|
|
type_error(L, idx, "pointer", 0, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static int is_void_ptr(const struct ctype* ct)
|
|
{
|
|
return ct->type == VOID_TYPE
|
|
&& ct->pointers == 1;
|
|
}
|
|
|
|
static int is_same_type(lua_State* L, int usr1, int usr2, const struct ctype* t1, const struct ctype* t2)
|
|
{
|
|
if (t1->type != t2->type) {
|
|
return 0;
|
|
}
|
|
|
|
#if LUA_VERSION_NUM == 501
|
|
if (lua_isnil(L, usr1) != lua_isnil(L, usr2)) {
|
|
int ret;
|
|
usr1 = lua_absindex(L, usr1);
|
|
usr2 = lua_absindex(L, usr2);
|
|
push_upval(L, &niluv_key);
|
|
|
|
ret = lua_rawequal(L, usr1, -1)
|
|
|| lua_rawequal(L, usr2, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
if (ret) {
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return lua_rawequal(L, usr1, usr2);
|
|
}
|
|
|
|
static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers);
|
|
|
|
/* to_typed_pointer converts a value at idx to a type tt with target uv to_usr
|
|
* checking all types. May push a temporary value so that it can create
|
|
* structs on the fly. */
|
|
void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt)
|
|
{
|
|
struct ctype ft;
|
|
void* p;
|
|
|
|
to_usr = lua_absindex(L, to_usr);
|
|
idx = lua_absindex(L, idx);
|
|
|
|
if (tt->pointers == 1 && (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) && lua_type(L, idx) == LUA_TTABLE) {
|
|
/* need to construct a struct of the target type */
|
|
struct ctype ct = *tt;
|
|
ct.pointers = ct.is_array = 0;
|
|
p = push_cdata(L, to_usr, &ct);
|
|
set_struct(L, idx, p, to_usr, &ct, 1);
|
|
return p;
|
|
}
|
|
|
|
p = check_pointer(L, idx, &ft);
|
|
|
|
if (tt->pointers == 1 && ft.pointers == 0 && (ft.type == STRUCT_TYPE || ft.type == UNION_TYPE)) {
|
|
/* auto dereference structs */
|
|
ft.pointers = 1;
|
|
ft.const_mask <<= 1;
|
|
}
|
|
|
|
if (is_void_ptr(tt)) {
|
|
/* any pointer can convert to void* */
|
|
goto suc;
|
|
|
|
} else if (ft.is_null) {
|
|
/* NULL can convert to any pointer */
|
|
goto suc;
|
|
|
|
} else if (!is_same_type(L, to_usr, -1, tt, &ft)) {
|
|
/* the base type is different */
|
|
goto err;
|
|
|
|
} else if (tt->pointers != ft.pointers) {
|
|
goto err;
|
|
|
|
} else if (ft.const_mask & ~tt->const_mask) {
|
|
/* for every const in from it must be in to, there are further rules
|
|
* for const casting (see the c++ spec), but they are hard to test
|
|
* quickly */
|
|
goto err;
|
|
}
|
|
|
|
suc:
|
|
return p;
|
|
|
|
err:
|
|
type_error(L, idx, NULL, to_usr, tt);
|
|
return NULL;
|
|
}
|
|
|
|
/* to_cfunction converts a value at idx with usr table at to_usr and type tt
|
|
* into a function. Leaves the stack unchanged. */
|
|
static cfunction check_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt, int check_pointers)
|
|
{
|
|
void* p;
|
|
struct ctype ft;
|
|
cfunction f;
|
|
int top = lua_gettop(L);
|
|
|
|
idx = lua_absindex(L, idx);
|
|
to_usr = lua_absindex(L, to_usr);
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TFUNCTION:
|
|
/* Function cdatas are pinned and must be manually cleaned up by
|
|
* calling func:free(). */
|
|
push_upval(L, &callbacks_key);
|
|
f = compile_callback(L, idx, to_usr, tt);
|
|
lua_pushboolean(L, 1);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1); /* callbacks tbl */
|
|
return f;
|
|
|
|
case LUA_TNIL:
|
|
return NULL;
|
|
|
|
case LUA_TLIGHTUSERDATA:
|
|
if (check_pointers) {
|
|
goto err;
|
|
} else {
|
|
return (cfunction) lua_touserdata(L, idx);
|
|
}
|
|
|
|
case LUA_TUSERDATA:
|
|
p = to_cdata(L, idx, &ft);
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
if (ft.type == INVALID_TYPE) {
|
|
if (check_pointers) {
|
|
goto err;
|
|
} else {
|
|
lua_pop(L, 1);
|
|
return (cfunction) lua_touserdata(L, idx);
|
|
}
|
|
|
|
} else if (ft.is_null) {
|
|
lua_pop(L, 1);
|
|
return NULL;
|
|
|
|
} else if (!check_pointers && (ft.pointers || ft.type == INTPTR_TYPE)) {
|
|
lua_pop(L, 1);
|
|
return (cfunction) *(void**) p;
|
|
|
|
} else if (ft.type != FUNCTION_PTR_TYPE) {
|
|
goto err;
|
|
|
|
} else if (!check_pointers) {
|
|
lua_pop(L, 1);
|
|
return *(cfunction*) p;
|
|
|
|
} else if (ft.calling_convention != tt->calling_convention) {
|
|
goto err;
|
|
|
|
} else if (!is_same_type(L, -1, to_usr, &ft, tt)) {
|
|
goto err;
|
|
|
|
} else {
|
|
lua_pop(L, 1);
|
|
return *(cfunction*) p;
|
|
}
|
|
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
type_error(L, idx, NULL, to_usr, tt);
|
|
return NULL;
|
|
}
|
|
|
|
/* to_type_cfunction converts a value at idx with uv at to_usr and type tt to
|
|
* a cfunction. Leaves the stack unchanged. */
|
|
cfunction check_typed_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt)
|
|
{ return check_cfunction(L, idx, to_usr, tt, 1); }
|
|
|
|
static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers);
|
|
|
|
static void set_array(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers)
|
|
{
|
|
size_t i, sz, esz;
|
|
struct ctype et;
|
|
|
|
idx = lua_absindex(L, idx);
|
|
to_usr = lua_absindex(L, to_usr);
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TSTRING:
|
|
if (tt->pointers == 1 && tt->type == INT8_TYPE) {
|
|
const char* str = lua_tolstring(L, idx, &sz);
|
|
|
|
if (!tt->is_variable_array && sz >= tt->array_size) {
|
|
memcpy(to, str, tt->array_size);
|
|
} else {
|
|
/* include nul terminator */
|
|
memcpy(to, str, sz+1);
|
|
}
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
|
|
case LUA_TTABLE:
|
|
et = *tt;
|
|
et.pointers--;
|
|
et.const_mask >>= 1;
|
|
et.is_array = 0;
|
|
esz = et.pointers ? sizeof(void*) : et.base_size;
|
|
|
|
lua_rawgeti(L, idx, 2);
|
|
|
|
if (tt->is_variable_array) {
|
|
/* we have no idea how big the array is, so set values based off
|
|
* how many items were given to us */
|
|
lua_pop(L, 1);
|
|
for (i = 0; i < lua_rawlen(L, idx); i++) {
|
|
lua_rawgeti(L, idx, (int) i + 1);
|
|
set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
} else if (lua_isnil(L, -1)) {
|
|
/* there is no second element, so we set the whole array to the
|
|
* first element (or nil - ie 0) if there is no first element) */
|
|
lua_pop(L, 1);
|
|
lua_rawgeti(L, idx, 1);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
memset(to, 0, ctype_size(L, tt));
|
|
} else {
|
|
/* if its still variable we have no idea how many values to set */
|
|
for (i = 0; i < tt->array_size; i++) {
|
|
set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers);
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
} else {
|
|
/* there is a second element, so we set each element using the
|
|
* equiv index in the table initializer */
|
|
lua_pop(L, 1);
|
|
for (i = 0; i < tt->array_size; i++) {
|
|
lua_rawgeti(L, idx, (int) (i+1));
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
/* we've hit the end of the values provided in the
|
|
* initializer, so memset the rest to zero */
|
|
lua_pop(L, 1);
|
|
memset((char*) to + esz * i, 0, (tt->array_size - i) * esz);
|
|
break;
|
|
|
|
} else {
|
|
set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
return;
|
|
|
|
err:
|
|
type_error(L, idx, NULL, to_usr, tt);
|
|
}
|
|
|
|
/* pops the member key from the stack, leaves the member user value on the
|
|
* stack. Returns the member offset. Returns -ve if the member can not be
|
|
* found. */
|
|
static ptrdiff_t get_member(lua_State* L, int usr, const struct ctype* ct, struct ctype* mt)
|
|
{
|
|
ptrdiff_t off;
|
|
lua_rawget(L, usr);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
return -1;
|
|
}
|
|
|
|
*mt = *(const struct ctype*) lua_touserdata(L, -1);
|
|
lua_getuservalue(L, -1);
|
|
lua_replace(L, -2);
|
|
|
|
if (mt->is_variable_array && ct->variable_size_known) {
|
|
/* eg char mbr[?] */
|
|
size_t sz = (mt->pointers > 1) ? sizeof(void*) : mt->base_size;
|
|
assert(ct->is_variable_struct && mt->is_array);
|
|
mt->array_size = ct->variable_increment / sz;
|
|
mt->is_variable_array = 0;
|
|
|
|
} else if (mt->is_variable_struct && ct->variable_size_known) {
|
|
/* eg struct {char a; char b[?]} mbr; */
|
|
assert(ct->is_variable_struct);
|
|
mt->variable_size_known = 1;
|
|
mt->variable_increment = ct->variable_increment;
|
|
}
|
|
|
|
off = mt->offset;
|
|
mt->offset = 0;
|
|
return off;
|
|
}
|
|
|
|
static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers)
|
|
{
|
|
int have_first = 0;
|
|
int have_other = 0;
|
|
struct ctype mt;
|
|
void* p;
|
|
|
|
to_usr = lua_absindex(L, to_usr);
|
|
idx = lua_absindex(L, idx);
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TTABLE:
|
|
/* match up to the members based off the table initializers key - this
|
|
* will match both numbered and named members in the user table
|
|
* we need a special case for when no entries in the initializer -
|
|
* zero initialize the c struct, and only one entry in the initializer
|
|
* - set all members to this value */
|
|
memset(to, 0, ctype_size(L, tt));
|
|
lua_pushnil(L);
|
|
while (lua_next(L, idx)) {
|
|
ptrdiff_t off;
|
|
|
|
if (!have_first && lua_tonumber(L, -2) == 1 && lua_tonumber(L, -1) != 0) {
|
|
have_first = 1;
|
|
} else if (!have_other && (lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != 1)) {
|
|
have_other = 1;
|
|
}
|
|
|
|
lua_pushvalue(L, -2);
|
|
off = get_member(L, to_usr, tt, &mt);
|
|
assert(off >= 0);
|
|
set_value(L, -2, (char*) to + off, -1, &mt, check_pointers);
|
|
|
|
/* initializer value, mt usr */
|
|
lua_pop(L, 2);
|
|
}
|
|
|
|
/* if we only had a single non zero value then initialize all members to that value */
|
|
if (!have_other && have_first && tt->type != UNION_TYPE) {
|
|
size_t i, sz;
|
|
ptrdiff_t off;
|
|
lua_rawgeti(L, idx, 1);
|
|
sz = lua_rawlen(L, to_usr);
|
|
|
|
for (i = 2; i < sz; i++) {
|
|
lua_pushnumber(L, i);
|
|
off = get_member(L, to_usr, tt, &mt);
|
|
assert(off >= 0);
|
|
set_value(L, -2, (char*) to + off, -1, &mt, check_pointers);
|
|
lua_pop(L, 1); /* mt usr */
|
|
}
|
|
|
|
lua_pop(L, 1); /* initializer table */
|
|
}
|
|
break;
|
|
|
|
case LUA_TUSERDATA:
|
|
if (check_pointers) {
|
|
p = check_typed_pointer(L, idx, to_usr, tt);
|
|
} else {
|
|
struct ctype ct;
|
|
p = check_pointer(L, idx, &ct);
|
|
}
|
|
memcpy(to, p, tt->base_size);
|
|
lua_pop(L, 1);
|
|
break;
|
|
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
return;
|
|
|
|
err:
|
|
type_error(L, idx, NULL, to_usr, tt);
|
|
}
|
|
|
|
static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers)
|
|
{
|
|
int top = lua_gettop(L);
|
|
|
|
if (tt->is_array) {
|
|
set_array(L, idx, to, to_usr, tt, check_pointers);
|
|
|
|
} else if (tt->pointers) {
|
|
union {
|
|
uint8_t c[sizeof(void*)];
|
|
void* p;
|
|
} u;
|
|
|
|
if (lua_istable(L, idx)) {
|
|
luaL_error(L, "Can't set a pointer member to a struct that's about to be freed");
|
|
}
|
|
|
|
if (check_pointers) {
|
|
u.p = check_typed_pointer(L, idx, to_usr, tt);
|
|
} else {
|
|
struct ctype ct;
|
|
u.p = check_pointer(L, idx, &ct);
|
|
}
|
|
|
|
#ifndef ALLOW_MISALIGNED_ACCESS
|
|
if ((uintptr_t) to & PTR_ALIGN_MASK) {
|
|
memcpy(to, u.c, sizeof(void*));
|
|
} else
|
|
#endif
|
|
{
|
|
*(void**) to = u.p;
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
} else if (tt->is_bitfield) {
|
|
|
|
uint64_t hi_mask = UINT64_C(0) - (UINT64_C(1) << (tt->bit_offset + tt->bit_size));
|
|
uint64_t low_mask = (UINT64_C(1) << tt->bit_offset) - UINT64_C(1);
|
|
uint64_t val = check_uint64(L, idx);
|
|
val &= (UINT64_C(1) << tt->bit_size) - 1;
|
|
val <<= tt->bit_offset;
|
|
*(uint64_t*) to = val | (*(uint64_t*) to & (hi_mask | low_mask));
|
|
|
|
} else if (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) {
|
|
set_struct(L, idx, to, to_usr, tt, check_pointers);
|
|
|
|
} else {
|
|
|
|
#ifndef ALLOW_MISALIGNED_ACCESS
|
|
union {
|
|
uint8_t c[8];
|
|
_Bool b;
|
|
uint64_t u64;
|
|
float f;
|
|
double d;
|
|
cfunction func;
|
|
} misalign;
|
|
|
|
void* origto = to;
|
|
|
|
if ((uintptr_t) origto & (tt->base_size - 1)) {
|
|
to = misalign.c;
|
|
}
|
|
#endif
|
|
|
|
switch (tt->type) {
|
|
case BOOL_TYPE:
|
|
*(_Bool*) to = (cast_int64(L, idx, !check_pointers) != 0);
|
|
break;
|
|
case INT8_TYPE:
|
|
if (tt->is_unsigned) {
|
|
*(uint8_t*) to = (uint8_t) cast_uint64(L, idx, !check_pointers);
|
|
} else {
|
|
*(int8_t*) to = (int8_t) cast_int64(L, idx, !check_pointers);
|
|
}
|
|
break;
|
|
case INT16_TYPE:
|
|
if (tt->is_unsigned) {
|
|
*(uint16_t*) to = (uint16_t) cast_uint64(L, idx, !check_pointers);
|
|
} else {
|
|
*(int16_t*) to = (int16_t) cast_int64(L, idx, !check_pointers);
|
|
}
|
|
break;
|
|
case INT32_TYPE:
|
|
if (tt->is_unsigned) {
|
|
*(uint32_t*) to = (uint32_t) cast_uint64(L, idx, !check_pointers);
|
|
} else {
|
|
*(int32_t*) to = (int32_t) cast_int64(L, idx, !check_pointers);
|
|
}
|
|
break;
|
|
case INT64_TYPE:
|
|
if (tt->is_unsigned) {
|
|
*(uint64_t*) to = cast_uint64(L, idx, !check_pointers);
|
|
} else {
|
|
*(int64_t*) to = cast_int64(L, idx, !check_pointers);
|
|
}
|
|
break;
|
|
case FLOAT_TYPE:
|
|
*(float*) to = (float) check_double(L, idx);
|
|
break;
|
|
case DOUBLE_TYPE:
|
|
*(double*) to = check_double(L, idx);
|
|
break;
|
|
case COMPLEX_FLOAT_TYPE:
|
|
*(complex_float*) to = check_complex_float(L, idx);
|
|
break;
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
*(complex_double*) to = check_complex_double(L, idx);
|
|
break;
|
|
case INTPTR_TYPE:
|
|
*(uintptr_t*) to = check_uintptr(L, idx);
|
|
break;
|
|
case ENUM_TYPE:
|
|
*(int32_t*) to = check_enum(L, idx, to_usr, tt);
|
|
break;
|
|
case FUNCTION_PTR_TYPE:
|
|
*(cfunction*) to = check_cfunction(L, idx, to_usr, tt, check_pointers);
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
#ifndef ALLOW_MISALIGNED_ACCESS
|
|
if ((uintptr_t) origto & (tt->base_size - 1)) {
|
|
memcpy(origto, misalign.c, tt->base_size);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
assert(lua_gettop(L) == top);
|
|
return;
|
|
err:
|
|
type_error(L, idx, NULL, to_usr, tt);
|
|
}
|
|
|
|
static int ffi_typeof(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
check_ctype(L, 1, &ct);
|
|
push_ctype(L, -1, &ct);
|
|
return 1;
|
|
}
|
|
|
|
static void setmintop(lua_State* L, int idx)
|
|
{
|
|
if (lua_gettop(L) < idx) {
|
|
lua_settop(L, idx);
|
|
}
|
|
}
|
|
|
|
/* warning: in the case that it finds an array size, it removes that index */
|
|
static void get_variable_array_size(lua_State* L, int idx, struct ctype* ct)
|
|
{
|
|
/* we only care about the variable buisness for the variable array
|
|
* directly ie ffi.new('char[?]') or the struct that contains the variable
|
|
* array ffi.new('struct {char v[?]}'). A pointer to the struct doesn't
|
|
* care about the variable size (it treats it as a zero sized array). */
|
|
|
|
if (ct->is_variable_array) {
|
|
assert(ct->is_array);
|
|
ct->array_size = (size_t) luaL_checknumber(L, idx);
|
|
ct->is_variable_array = 0;
|
|
lua_remove(L, idx);
|
|
|
|
} else if (ct->is_variable_struct && !ct->variable_size_known) {
|
|
assert(ct->type == STRUCT_TYPE && !ct->is_array);
|
|
ct->variable_increment *= (size_t) luaL_checknumber(L, idx);
|
|
ct->variable_size_known = 1;
|
|
lua_remove(L, idx);
|
|
}
|
|
}
|
|
|
|
static int try_set_value(lua_State* L)
|
|
{
|
|
void* p = lua_touserdata(L, 2);
|
|
struct ctype* ct = (struct ctype*) lua_touserdata(L, 4);
|
|
int check_ptrs = lua_toboolean(L, 5);
|
|
set_value(L, 1, p, 3, ct, check_ptrs);
|
|
return 0;
|
|
}
|
|
|
|
static int do_new(lua_State* L, int is_cast)
|
|
{
|
|
int cargs, i;
|
|
void* p;
|
|
struct ctype ct;
|
|
int check_ptrs = !is_cast;
|
|
|
|
check_ctype(L, 1, &ct);
|
|
|
|
/* don't push a callback when we have a c function, as cb:set needs a
|
|
* compiled callback from a lua function to work */
|
|
if (!ct.pointers && ct.type == FUNCTION_PTR_TYPE && (lua_isnil(L, 2) || lua_isfunction(L, 2))) {
|
|
/* Function cdatas are pinned and must be manually cleaned up by
|
|
* calling func:free(). */
|
|
compile_callback(L, 2, -1, &ct);
|
|
push_upval(L, &callbacks_key);
|
|
lua_pushvalue(L, -2);
|
|
lua_pushboolean(L, 1);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1); /* callbacks tbl */
|
|
return 1;
|
|
}
|
|
|
|
/* this removes the vararg argument if its needed, and errors if its invalid */
|
|
if (!is_cast) {
|
|
get_variable_array_size(L, 2, &ct);
|
|
}
|
|
|
|
p = push_cdata(L, -1, &ct);
|
|
|
|
/* if the user mt has a __gc function then call ffi.gc on this value */
|
|
if (push_user_mt(L, -2, &ct)) {
|
|
push_upval(L, &gc_key);
|
|
lua_pushvalue(L, -3);
|
|
|
|
/* user_mt.__gc */
|
|
lua_pushliteral(L, "__gc");
|
|
lua_rawget(L, -4);
|
|
|
|
lua_rawset(L, -3); /* gc_upval[cdata] = user_mt.__gc */
|
|
lua_pop(L, 2); /* user_mt and gc_upval */
|
|
}
|
|
|
|
/* stack is:
|
|
* ctype arg
|
|
* ctor args ... 0+
|
|
* ctype usr
|
|
* cdata
|
|
*/
|
|
|
|
cargs = lua_gettop(L) - 3;
|
|
|
|
if (cargs == 0) {
|
|
return 1;
|
|
}
|
|
|
|
if (cargs == 1) {
|
|
/* try packed form first
|
|
* packed: ffi.new('int[3]', {1})
|
|
* unpacked: ffi.new('int[3]', 1)
|
|
*/
|
|
lua_pushcfunction(L, &try_set_value);
|
|
lua_pushvalue(L, 2); /* ctor arg */
|
|
lua_pushlightuserdata(L, p);
|
|
lua_pushvalue(L, -5); /* ctype usr */
|
|
lua_pushlightuserdata(L, &ct);
|
|
lua_pushboolean(L, check_ptrs);
|
|
|
|
if (!lua_pcall(L, 5, 0, 0)) {
|
|
return 1;
|
|
}
|
|
|
|
/* remove any errors */
|
|
lua_settop(L, 4);
|
|
}
|
|
|
|
/* if we have more than 2 ctor arguments then they must be unpacked, e.g.
|
|
* ffi.new('int[3]', 1, 2, 3) */
|
|
lua_createtable(L, cargs, 0);
|
|
lua_replace(L, 1);
|
|
for (i = 1; i <= cargs; i++) {
|
|
lua_pushvalue(L, i + 1);
|
|
lua_rawseti(L, 1, i);
|
|
}
|
|
assert(lua_gettop(L) == cargs + 3);
|
|
set_value(L, 1, p, -2, &ct, check_ptrs);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_new(lua_State* L)
|
|
{ return do_new(L, 0); }
|
|
|
|
static int ffi_cast(lua_State* L)
|
|
{ return do_new(L, 1); }
|
|
|
|
static int ctype_new(lua_State* L)
|
|
{ return do_new(L, 0); }
|
|
|
|
static int ctype_call(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
int top = lua_gettop(L);
|
|
|
|
check_ctype(L, 1, &ct);
|
|
|
|
if (push_user_mt(L, -1, &ct)) {
|
|
lua_pushstring(L, "__new");
|
|
lua_rawget(L, -2);
|
|
if (!lua_isnil(L, -1)) {
|
|
lua_insert(L, 1); // function at bottom of stack under args
|
|
lua_pop(L, 2);
|
|
lua_call(L, top, 1);
|
|
return 1;
|
|
}
|
|
lua_pop(L, 2);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
assert(lua_gettop(L) == top);
|
|
return do_new(L, 0);
|
|
}
|
|
|
|
static int ffi_sizeof(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
check_ctype(L, 1, &ct);
|
|
get_variable_array_size(L, 2, &ct);
|
|
lua_pushnumber(L, ctype_size(L, &ct));
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_alignof(lua_State* L)
|
|
{
|
|
struct ctype ct, mt;
|
|
lua_settop(L, 2);
|
|
check_ctype(L, 1, &ct);
|
|
|
|
/* if no member is specified then we return the alignment of the type */
|
|
if (lua_isnil(L, 2)) {
|
|
lua_pushnumber(L, ct.align_mask + 1);
|
|
return 1;
|
|
}
|
|
|
|
/* get the alignment of the member */
|
|
lua_pushvalue(L, 2);
|
|
if (get_member(L, -2, &ct, &mt) < 0) {
|
|
push_type_name(L, 3, &ct);
|
|
return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2));
|
|
}
|
|
|
|
lua_pushnumber(L, mt.align_mask + 1);
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_offsetof(lua_State* L)
|
|
{
|
|
ptrdiff_t off;
|
|
struct ctype ct, mt;
|
|
lua_settop(L, 2);
|
|
check_ctype(L, 1, &ct);
|
|
|
|
lua_pushvalue(L, 2);
|
|
off = get_member(L, -2, &ct, &mt); /* this replaces the member key at -1 with the mbr usr value */
|
|
if (off < 0) {
|
|
push_type_name(L, 3, &ct);
|
|
return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2));
|
|
}
|
|
|
|
lua_pushnumber(L, off);
|
|
|
|
if (!mt.is_bitfield) {
|
|
return 1;
|
|
}
|
|
|
|
lua_pushnumber(L, mt.bit_offset);
|
|
lua_pushnumber(L, mt.bit_size);
|
|
return 3;
|
|
}
|
|
|
|
static int ffi_istype(lua_State* L)
|
|
{
|
|
struct ctype tt, ft;
|
|
check_ctype(L, 1, &tt);
|
|
to_cdata(L, 2, &ft);
|
|
|
|
if (ft.type == INVALID_TYPE) {
|
|
goto fail;
|
|
}
|
|
|
|
if (!is_same_type(L, 3, 4, &tt, &ft)) {
|
|
goto fail;
|
|
}
|
|
|
|
if (tt.pointers != ft.pointers) {
|
|
goto fail;
|
|
}
|
|
|
|
if (tt.is_array != ft.is_array) {
|
|
goto fail;
|
|
}
|
|
|
|
if (tt.is_array && tt.array_size != ft.array_size) {
|
|
goto fail;
|
|
}
|
|
|
|
if (tt.calling_convention != ft.calling_convention) {
|
|
goto fail;
|
|
}
|
|
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
|
|
fail:
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
static int cdata_gc(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
check_cdata(L, 1, &ct);
|
|
lua_settop(L, 1);
|
|
|
|
/* call the gc func if there is any registered */
|
|
lua_pushvalue(L, 1);
|
|
lua_rawget(L, lua_upvalueindex(2));
|
|
if (!lua_isnil(L, -1)) {
|
|
lua_pushvalue(L, 1);
|
|
lua_pcall(L, 1, 0, 0);
|
|
}
|
|
|
|
/* unset the closure */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushnil(L);
|
|
lua_rawset(L, lua_upvalueindex(1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int callback_free(lua_State* L)
|
|
{
|
|
cfunction* p = (cfunction*) lua_touserdata(L, 1);
|
|
free_code(get_jit(L), L, *p);
|
|
return 0;
|
|
}
|
|
|
|
static int cdata_free(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
cfunction* p = (cfunction*) check_cdata(L, 1, &ct);
|
|
lua_settop(L, 1);
|
|
|
|
/* unset the closure */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushnil(L);
|
|
lua_rawset(L, lua_upvalueindex(1));
|
|
|
|
if (ct.is_jitted) {
|
|
free_code(get_jit(L), L, *p);
|
|
*p = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdata_set(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
cfunction* p = (cfunction*) check_cdata(L, 1, &ct);
|
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
|
|
|
if (!ct.is_jitted) {
|
|
luaL_error(L, "can't set the function for a non-lua callback");
|
|
}
|
|
|
|
if (*p == NULL) {
|
|
luaL_error(L, "can't set the function for a free'd callback");
|
|
}
|
|
|
|
push_func_ref(L, *p);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawseti(L, -2, CALLBACK_FUNC_USR_IDX);
|
|
|
|
/* remove the closure for this callback as it embeds the function pointer
|
|
* value */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushboolean(L, 1);
|
|
lua_rawset(L, lua_upvalueindex(1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdata_call(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
int top = lua_gettop(L);
|
|
cfunction* p = (cfunction*) check_cdata(L, 1, &ct);
|
|
|
|
if (push_user_mt(L, -1, &ct)) {
|
|
lua_pushliteral(L, "__call");
|
|
lua_rawget(L, -2);
|
|
|
|
if (!lua_isnil(L, -1)) {
|
|
lua_insert(L, 1);
|
|
lua_pop(L, 2); /* ct_usr, user_mt */
|
|
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
|
return lua_gettop(L);
|
|
}
|
|
}
|
|
if (ct.pointers || ct.type != FUNCTION_PTR_TYPE) {
|
|
return luaL_error(L, "only function callbacks are callable");
|
|
}
|
|
|
|
lua_pushvalue(L, 1);
|
|
lua_rawget(L, lua_upvalueindex(1));
|
|
|
|
if (!lua_isfunction(L, -1)) {
|
|
lua_pop(L, 1);
|
|
compile_function(L, *p, -1, &ct);
|
|
|
|
assert(lua_gettop(L) == top + 2); /* uv, closure */
|
|
|
|
/* closures[func] = closure */
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, lua_upvalueindex(1));
|
|
|
|
lua_replace(L, 1);
|
|
} else {
|
|
lua_replace(L, 1);
|
|
}
|
|
|
|
lua_pop(L, 1); /* uv */
|
|
assert(lua_gettop(L) == top);
|
|
|
|
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
|
return lua_gettop(L);
|
|
}
|
|
|
|
static int user_mt_key;
|
|
|
|
static int ffi_metatype(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
lua_settop(L, 2);
|
|
|
|
check_ctype(L, 1, &ct);
|
|
if (lua_type(L, 2) != LUA_TTABLE && lua_type(L, 2) != LUA_TNIL) {
|
|
return luaL_argerror(L, 2, "metatable must be a table or nil");
|
|
}
|
|
|
|
lua_pushlightuserdata(L, &user_mt_key);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawset(L, 3); /* user[user_mt_key] = mt */
|
|
|
|
/* return the passed in ctype */
|
|
push_ctype(L, 3, &ct);
|
|
return 1;
|
|
}
|
|
|
|
/* push_user_mt returns 1 if the type has a user metatable and pushes it onto
|
|
* the stack, otherwise it returns 0 and pushes nothing */
|
|
int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct)
|
|
{
|
|
if (ct->type != STRUCT_TYPE && ct->type != UNION_TYPE) {
|
|
return 0;
|
|
}
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
lua_pushlightuserdata(L, &user_mt_key);
|
|
lua_rawget(L, ct_usr);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_gc(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
lua_settop(L, 2);
|
|
check_cdata(L, 1, &ct);
|
|
|
|
push_upval(L, &gc_key);
|
|
lua_pushvalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawset(L, -3);
|
|
|
|
/* return the cdata back */
|
|
lua_settop(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
/* lookup_cdata_index returns the offset of the found type and user value on
|
|
* the stack if valid. Otherwise returns -ve and doesn't touch the stack.
|
|
*/
|
|
static ptrdiff_t lookup_cdata_index(lua_State* L, int idx, int ct_usr, struct ctype* ct)
|
|
{
|
|
struct ctype mt;
|
|
ptrdiff_t off;
|
|
|
|
ct_usr = lua_absindex(L, ct_usr);
|
|
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TNUMBER:
|
|
/* possibilities are array, pointer */
|
|
|
|
if (!ct->pointers || is_void_ptr(ct)) {
|
|
return -1;
|
|
}
|
|
|
|
ct->is_array = 0;
|
|
ct->pointers--;
|
|
ct->const_mask >>= 1;
|
|
|
|
lua_pushvalue(L, ct_usr);
|
|
|
|
return (ct->pointers ? sizeof(void*) : ct->base_size) * lua_tonumber(L, 2);
|
|
|
|
case LUA_TSTRING:
|
|
/* possibilities are struct/union, pointer to struct/union */
|
|
|
|
if ((ct->type != STRUCT_TYPE && ct->type != UNION_TYPE) || ct->is_array || ct->pointers > 1) {
|
|
return -1;
|
|
}
|
|
|
|
lua_pushvalue(L, idx);
|
|
off = get_member(L, ct_usr, ct, &mt);
|
|
if (off < 0) {
|
|
return -1;
|
|
}
|
|
|
|
*ct = mt;
|
|
return off;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int cdata_newindex(lua_State* L)
|
|
{
|
|
struct ctype tt;
|
|
char* to;
|
|
ptrdiff_t off;
|
|
|
|
lua_settop(L, 3);
|
|
|
|
to = (char*) check_cdata(L, 1, &tt);
|
|
off = lookup_cdata_index(L, 2, -1, &tt);
|
|
|
|
if (off < 0) {
|
|
if (!push_user_mt(L, -1, &tt)) {
|
|
goto err;
|
|
}
|
|
|
|
lua_pushliteral(L, "__newindex");
|
|
lua_rawget(L, -2);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
goto err;
|
|
}
|
|
|
|
lua_insert(L, 1);
|
|
lua_settop(L, 4);
|
|
lua_call(L, 3, LUA_MULTRET);
|
|
return lua_gettop(L);
|
|
}
|
|
|
|
if (tt.const_mask & 1) {
|
|
return luaL_error(L, "can't set const data");
|
|
}
|
|
|
|
set_value(L, 3, to + off, -1, &tt, 1);
|
|
return 0;
|
|
|
|
err:
|
|
push_type_name(L, 4, &tt);
|
|
return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2));
|
|
}
|
|
|
|
static int cdata_index(lua_State* L)
|
|
{
|
|
void* to;
|
|
struct ctype ct;
|
|
char* data;
|
|
ptrdiff_t off;
|
|
|
|
lua_settop(L, 2);
|
|
data = (char*) check_cdata(L, 1, &ct);
|
|
assert(lua_gettop(L) == 3);
|
|
|
|
if (!ct.pointers) {
|
|
switch (ct.type) {
|
|
case FUNCTION_PTR_TYPE:
|
|
/* Callbacks use the same metatable as standard cdata values, but have set
|
|
* and free members. So instead of mt.__index = mt, we do the equiv here. */
|
|
lua_getmetatable(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawget(L, -2);
|
|
return 1;
|
|
|
|
/* This provides the .re and .im virtual members */
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
case COMPLEX_FLOAT_TYPE:
|
|
if (!lua_isstring(L, 2)) {
|
|
luaL_error(L, "invalid member for complex number");
|
|
|
|
} else if (strcmp(lua_tostring(L, 2), "re") == 0) {
|
|
lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? creal(*(complex_double*) data) : crealf(*(complex_float*) data));
|
|
|
|
} else if (strcmp(lua_tostring(L, 2), "im") == 0) {
|
|
lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? cimag(*(complex_double*) data) : cimagf(*(complex_float*) data));
|
|
|
|
} else {
|
|
luaL_error(L, "invalid member for complex number");
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
off = lookup_cdata_index(L, 2, -1, &ct);
|
|
|
|
if (off < 0) {
|
|
assert(lua_gettop(L) == 3);
|
|
if (!push_user_mt(L, -1, &ct)) {
|
|
goto err;
|
|
}
|
|
|
|
lua_pushliteral(L, "__index");
|
|
lua_rawget(L, -2);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
goto err;
|
|
}
|
|
|
|
if (lua_istable(L, -1)) {
|
|
lua_pushvalue(L, 2);
|
|
lua_gettable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
lua_insert(L, 1);
|
|
lua_settop(L, 3);
|
|
lua_call(L, 2, LUA_MULTRET);
|
|
return lua_gettop(L);
|
|
|
|
err:
|
|
push_type_name(L, 3, &ct);
|
|
return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2));
|
|
}
|
|
|
|
assert(lua_gettop(L) == 4); /* ct, key, ct_usr, mbr_usr */
|
|
data += off;
|
|
|
|
if (ct.is_array) {
|
|
/* push a reference to the array */
|
|
ct.is_reference = 1;
|
|
to = push_cdata(L, -1, &ct);
|
|
*(void**) to = data;
|
|
return 1;
|
|
|
|
} else if (ct.is_bitfield) {
|
|
|
|
if (ct.type == INT64_TYPE) {
|
|
struct ctype rt;
|
|
uint64_t val = *(uint64_t*) data;
|
|
val >>= ct.bit_offset;
|
|
val &= (UINT64_C(1) << ct.bit_size) - 1;
|
|
|
|
memset(&rt, 0, sizeof(rt));
|
|
rt.base_size = 8;
|
|
rt.type = INT64_TYPE;
|
|
rt.is_unsigned = 1;
|
|
rt.is_defined = 1;
|
|
|
|
to = push_cdata(L, 0, &rt);
|
|
*(uint64_t*) to = val;
|
|
|
|
return 1;
|
|
|
|
} else if (ct.type == BOOL_TYPE) {
|
|
uint64_t val = *(uint64_t*) data;
|
|
lua_pushboolean(L, (int) (val & (UINT64_C(1) << ct.bit_offset)));
|
|
return 1;
|
|
|
|
} else {
|
|
uint64_t val = *(uint64_t*) data;
|
|
val >>= ct.bit_offset;
|
|
val &= (UINT64_C(1) << ct.bit_size) - 1;
|
|
lua_pushnumber(L, val);
|
|
return 1;
|
|
}
|
|
|
|
} else if (ct.pointers) {
|
|
#ifndef ALLOW_MISALIGNED_ACCESS
|
|
union {
|
|
uint8_t c[8];
|
|
void* p;
|
|
} misalignbuf;
|
|
|
|
if ((uintptr_t) data & PTR_ALIGN_MASK) {
|
|
memcpy(misalignbuf.c, data, sizeof(void*));
|
|
data = misalignbuf.c;
|
|
}
|
|
#endif
|
|
to = push_cdata(L, -1, &ct);
|
|
*(void**) to = *(void**) data;
|
|
return 1;
|
|
|
|
} else if (ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {
|
|
/* push a reference to the member */
|
|
ct.is_reference = 1;
|
|
to = push_cdata(L, -1, &ct);
|
|
*(void**) to = data;
|
|
return 1;
|
|
|
|
} else if (ct.type == FUNCTION_PTR_TYPE) {
|
|
cfunction* pf = (cfunction*) push_cdata(L, -1, &ct);
|
|
*pf = *(cfunction*) data;
|
|
return 1;
|
|
|
|
} else {
|
|
#ifndef ALLOW_MISALIGNED_ACCESS
|
|
union {
|
|
uint8_t c[8];
|
|
double d;
|
|
float f;
|
|
uint64_t u64;
|
|
} misalignbuf;
|
|
|
|
assert(ct.base_size <= 8);
|
|
|
|
if ((uintptr_t) data & (ct.base_size - 1)) {
|
|
memcpy(misalignbuf.c, data, ct.base_size);
|
|
data = misalignbuf.c;
|
|
}
|
|
#endif
|
|
|
|
switch (ct.type) {
|
|
case BOOL_TYPE:
|
|
lua_pushboolean(L, *(_Bool*) data);
|
|
break;
|
|
case INT8_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint8_t*) data : (lua_Number) *(int8_t*) data);
|
|
break;
|
|
case INT16_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint16_t*) data : (lua_Number) *(int16_t*) data);
|
|
break;
|
|
case ENUM_TYPE:
|
|
case INT32_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint32_t*) data : (lua_Number) *(int32_t*) data);
|
|
break;
|
|
case INT64_TYPE:
|
|
to = push_cdata(L, -1, &ct);
|
|
*(int64_t*) to = *(int64_t*) data;
|
|
break;
|
|
case INTPTR_TYPE:
|
|
to = push_cdata(L, -1, &ct);
|
|
*(intptr_t*) to = *(intptr_t*) data;
|
|
break;
|
|
case FLOAT_TYPE:
|
|
lua_pushnumber(L, *(float*) data);
|
|
break;
|
|
case DOUBLE_TYPE:
|
|
lua_pushnumber(L, *(double*) data);
|
|
break;
|
|
default:
|
|
luaL_error(L, "internal error: invalid member type");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static complex_double check_complex(lua_State* L, int idx, void* p, struct ctype* ct)
|
|
{
|
|
if (ct->type == INVALID_TYPE) {
|
|
double d = luaL_checknumber(L, idx);
|
|
#ifdef HAVE_COMPLEX
|
|
return d;
|
|
#else
|
|
complex_double c;
|
|
c.real = d;
|
|
c.imag = 0;
|
|
return c;
|
|
#endif
|
|
} else if (ct->type == COMPLEX_DOUBLE_TYPE) {
|
|
return *(complex_double*) p;
|
|
} else if (ct->type == COMPLEX_FLOAT_TYPE) {
|
|
complex_float* f = (complex_float*) p;
|
|
#ifdef HAVE_COMPLEX
|
|
return *f;
|
|
#else
|
|
complex_double d;
|
|
d.real = f->real;
|
|
d.imag = f->imag;
|
|
return d;
|
|
#endif
|
|
} else {
|
|
complex_double dummy;
|
|
type_error(L, idx, "complex", 0, NULL);
|
|
memset(&dummy, 0, sizeof(dummy));
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
static int rank(const struct ctype* ct)
|
|
{
|
|
if (ct->pointers) {
|
|
return 5;
|
|
}
|
|
|
|
switch (ct->type) {
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
return 7;
|
|
case COMPLEX_FLOAT_TYPE:
|
|
return 6;
|
|
case INTPTR_TYPE:
|
|
return sizeof(intptr_t) >= sizeof(int64_t) ? 4 : 1;
|
|
case INT64_TYPE:
|
|
return ct->is_unsigned ? 3 : 2;
|
|
case INT32_TYPE:
|
|
case INT16_TYPE:
|
|
case INT8_TYPE:
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void push_complex(lua_State* L, complex_double res, int ct_usr, const struct ctype* ct)
|
|
{
|
|
if (ct->type == COMPLEX_DOUBLE_TYPE) {
|
|
complex_double* p = (complex_double*) push_cdata(L, ct_usr, ct);
|
|
*p = res;
|
|
} else {
|
|
complex_float* p = (complex_float*) push_cdata(L, ct_usr, ct);
|
|
#ifdef HAVE_COMPLEX
|
|
*p = (complex float) res;
|
|
#else
|
|
p->real = (float) res.real;
|
|
p->imag = (float) res.imag;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void push_number(lua_State* L, int64_t val, int ct_usr, const struct ctype* ct)
|
|
{
|
|
if ((ct->pointers || ct->type == INTPTR_TYPE) && sizeof(intptr_t) != sizeof(int64_t)) {
|
|
intptr_t* p = (intptr_t*) push_cdata(L, ct_usr, ct);
|
|
*p = val;
|
|
} else {
|
|
int64_t* p = (int64_t*) push_cdata(L, ct_usr, ct);
|
|
*p = val;
|
|
}
|
|
}
|
|
|
|
static int call_user_op(lua_State* L, const char* opfield, int idx, int ct_usr, const struct ctype* ct)
|
|
{
|
|
idx = lua_absindex(L, idx);
|
|
|
|
if (push_user_mt(L, ct_usr, ct)) {
|
|
lua_pushstring(L, opfield);
|
|
lua_rawget(L, -2);
|
|
if (!lua_isnil(L, -1)) {
|
|
int top = lua_gettop(L);
|
|
lua_pushvalue(L, idx);
|
|
lua_call(L, 1, LUA_MULTRET);
|
|
return lua_gettop(L) - top + 1;
|
|
}
|
|
lua_pop(L, 2);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int cdata_unm(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
void* p;
|
|
int64_t val;
|
|
int ret;
|
|
|
|
lua_settop(L, 1);
|
|
p = to_cdata(L, 1, &ct);
|
|
|
|
ret = call_user_op(L, "__unm", 1, 2, &ct);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
val = check_intptr(L, 1, p, &ct);
|
|
|
|
if (ct.pointers) {
|
|
luaL_error(L, "can't negate a pointer value");
|
|
} else {
|
|
memset(&ct, 0, sizeof(ct));
|
|
ct.type = INT64_TYPE;
|
|
ct.base_size = 8;
|
|
ct.is_defined = 1;
|
|
push_number(L, -val, 0, &ct);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* returns -ve if no binop was called otherwise returns the number of return
|
|
* arguments */
|
|
static int call_user_binop(lua_State* L, const char* opfield, int lidx, int lusr, const struct ctype* lt, int ridx, int rusr, const struct ctype* rt)
|
|
{
|
|
lidx = lua_absindex(L, lidx);
|
|
ridx = lua_absindex(L, ridx);
|
|
|
|
if (push_user_mt(L, lusr, lt)) {
|
|
lua_pushstring(L, opfield);
|
|
lua_rawget(L, -2);
|
|
|
|
if (!lua_isnil(L, -1)) {
|
|
int top = lua_gettop(L);
|
|
lua_pushvalue(L, lidx);
|
|
lua_pushvalue(L, ridx);
|
|
lua_call(L, 2, LUA_MULTRET);
|
|
return lua_gettop(L) - top + 1;
|
|
}
|
|
|
|
lua_pop(L, 2); /* user_mt and user_mt.op */
|
|
}
|
|
|
|
if (push_user_mt(L, rusr, rt)) {
|
|
lua_pushstring(L, opfield);
|
|
lua_rawget(L, -2);
|
|
|
|
if (!lua_isnil(L, -1)) {
|
|
int top = lua_gettop(L);
|
|
lua_pushvalue(L, lidx);
|
|
lua_pushvalue(L, ridx);
|
|
lua_call(L, 2, LUA_MULTRET);
|
|
return lua_gettop(L) - top + 1;
|
|
}
|
|
|
|
lua_pop(L, 2); /* user_mt and user_mt.op */
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int cdata_concat(lua_State* L)
|
|
{
|
|
struct ctype lt, rt;
|
|
int ret;
|
|
|
|
lua_settop(L, 2);
|
|
to_cdata(L, 1, <);
|
|
to_cdata(L, 2, &rt);
|
|
|
|
ret = call_user_binop(L, "__concat", 1, 3, <, 2, 4, &rt);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
return luaL_error(L, "NYI");
|
|
}
|
|
|
|
static int cdata_len(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
int ret;
|
|
|
|
lua_settop(L, 1);
|
|
to_cdata(L, 1, &ct);
|
|
|
|
ret = call_user_op(L, "__len", 1, 2, &ct);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
push_type_name(L, 2, &ct);
|
|
return luaL_error(L, "type %s does not implement the __len metamethod", lua_tostring(L, -1));
|
|
}
|
|
|
|
static int cdata_pairs(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
int ret;
|
|
|
|
lua_settop(L, 1);
|
|
to_cdata(L, 1, &ct);
|
|
|
|
ret = call_user_op(L, "__pairs", 1, 2, &ct);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
push_type_name(L, 2, &ct);
|
|
return luaL_error(L, "type %s does not implement the __pairs metamethod", lua_tostring(L, -1));
|
|
}
|
|
|
|
static int cdata_ipairs(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
int ret;
|
|
|
|
lua_settop(L, 1);
|
|
to_cdata(L, 1, &ct);
|
|
|
|
ret = call_user_op(L, "__ipairs", 1, 2, &ct);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
push_type_name(L, 2, &ct);
|
|
return luaL_error(L, "type %s does not implement the __ipairs metamethod", lua_tostring(L, -1));
|
|
}
|
|
|
|
static int cdata_add(lua_State* L)
|
|
{
|
|
struct ctype lt, rt, ct;
|
|
void *lp, *rp;
|
|
int ct_usr;
|
|
int ret;
|
|
|
|
lua_settop(L, 2);
|
|
|
|
lp = to_cdata(L, 1, <);
|
|
rp = to_cdata(L, 2, &rt);
|
|
assert(lua_gettop(L) == 4);
|
|
|
|
ret = call_user_binop(L, "__add", 1, 3, <, 2, 4, &rt);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
assert(lua_gettop(L) == 4);
|
|
|
|
ct_usr = rank(<) > rank(&rt) ? 3 : 4;
|
|
ct = rank(<) > rank(&rt) ? lt : rt;
|
|
|
|
if (IS_COMPLEX(ct.type)) {
|
|
complex_double left, right, res;
|
|
|
|
left = check_complex(L, 1, lp, <);
|
|
right = check_complex(L, 2, rp, &rt);
|
|
assert(lua_gettop(L) == 4);
|
|
|
|
#ifdef HAVE_COMPLEX
|
|
res = left + right;
|
|
#else
|
|
res.real = left.real + right.real;
|
|
res.imag = left.imag + right.imag;
|
|
#endif
|
|
|
|
push_complex(L, res, ct_usr, &ct);
|
|
return 1;
|
|
|
|
} else {
|
|
int64_t left = check_intptr(L, 1, lp, <);
|
|
int64_t right = check_intptr(L, 2, rp, &rt);
|
|
assert(lua_gettop(L) == 4);
|
|
|
|
/* note due to 2s complement it doesn't matter if we do the addition as int or uint,
|
|
* but the result needs to be uint64_t if either of the sources are */
|
|
|
|
if (lt.pointers && rt.pointers) {
|
|
luaL_error(L, "can't add two pointers");
|
|
|
|
} else if (lt.pointers) {
|
|
int64_t res = left + (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right;
|
|
lt.is_array = 0;
|
|
push_number(L, res, 3, <);
|
|
|
|
} else if (rt.pointers) {
|
|
int64_t res = right + (rt.pointers > 1 ? sizeof(void*) : rt.base_size) * left;
|
|
rt.is_array = 0;
|
|
push_number(L, res, 4, &rt);
|
|
|
|
} else {
|
|
push_number(L, left + right, ct_usr, &ct);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int cdata_sub(lua_State* L)
|
|
{
|
|
struct ctype lt, rt, ct;
|
|
void *lp, *rp;
|
|
int ct_usr;
|
|
int ret;
|
|
|
|
lua_settop(L, 2);
|
|
|
|
lp = to_cdata(L, 1, <);
|
|
rp = to_cdata(L, 2, &rt);
|
|
|
|
ret = call_user_binop(L, "__sub", 1, 3, <, 2, 4, &rt);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
ct_usr = rank(<) > rank(&rt) ? 3 : 4;
|
|
ct = rank(<) > rank(&rt) ? lt : rt;
|
|
|
|
if (IS_COMPLEX(ct.type)) {
|
|
complex_double left, right, res;
|
|
|
|
left = check_complex(L, 1, lp, <);
|
|
right = check_complex(L, 2, rp, &rt);
|
|
|
|
#ifdef HAVE_COMPLEX
|
|
res = left - right;
|
|
#else
|
|
res.real = left.real - right.real;
|
|
res.imag = left.imag - right.imag;
|
|
#endif
|
|
|
|
push_complex(L, res, ct_usr, &ct);
|
|
return 1;
|
|
|
|
} else {
|
|
int64_t left = check_intptr(L, 1, lp, <);
|
|
int64_t right = check_intptr(L, 2, rp, &rt);
|
|
|
|
if (rt.pointers) {
|
|
luaL_error(L, "NYI: can't subtract a pointer value");
|
|
|
|
} else if (lt.pointers) {
|
|
int64_t res = left - (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right;
|
|
lt.is_array = 0;
|
|
push_number(L, res, 3, <);
|
|
|
|
} else {
|
|
int64_t res = left - right;
|
|
push_number(L, res, ct_usr, &ct);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* TODO fix for unsigned */
|
|
#define NUMBER_ONLY_BINOP(OPSTR, DO_NORMAL, DO_COMPLEX) \
|
|
struct ctype lt, rt, ct; \
|
|
void *lp, *rp; \
|
|
int ct_usr; \
|
|
int ret; \
|
|
\
|
|
lua_settop(L, 2); \
|
|
\
|
|
lp = to_cdata(L, 1, <); \
|
|
rp = to_cdata(L, 2, &rt); \
|
|
\
|
|
ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \
|
|
if (ret >= 0) { \
|
|
return ret; \
|
|
} \
|
|
\
|
|
ct_usr = rank(<) > rank(&rt) ? 3 : 4; \
|
|
ct = rank(<) > rank(&rt) ? lt : rt; \
|
|
\
|
|
if (IS_COMPLEX(ct.type)) { \
|
|
complex_double res; \
|
|
complex_double left = check_complex(L, 1, lp, <); \
|
|
complex_double right = check_complex(L, 2, rp, &rt); \
|
|
\
|
|
DO_COMPLEX(left, right, res); \
|
|
push_complex(L, res, ct_usr, &ct); \
|
|
\
|
|
} else if (lt.pointers || rt.pointers) { \
|
|
luaL_error(L, "can't operate on a pointer value"); \
|
|
\
|
|
} else { \
|
|
int64_t res; \
|
|
int64_t left = check_intptr(L, 1, lp, <); \
|
|
int64_t right = check_intptr(L, 2, rp, &rt); \
|
|
\
|
|
DO_NORMAL(left, right, res); \
|
|
push_number(L, res, ct_usr, &ct); \
|
|
} \
|
|
\
|
|
return 1
|
|
|
|
#define MUL(l,r,s) s = l * r
|
|
#define DIV(l,r,s) s = l / r
|
|
#define MOD(l,r,s) s = l % r
|
|
#define POW(l,r,s) s = pow(l, r)
|
|
|
|
#ifdef HAVE_COMPLEX
|
|
#define MULC(l,r,s) s = l * r
|
|
#define DIVC(l,r,s) s = l / r
|
|
#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod")
|
|
#define POWC(l,r,s) s = cpow(l, r)
|
|
#else
|
|
#define MULC(l,r,s) s.real = l.real * r.real - l.imag * r.imag, s.imag = l.real * r.imag + l.imag * r.real
|
|
#define DIVC(l,r,s) s.real = (l.real * r.real + l.imag * r.imag) / (r.real * r.real + r.imag * r.imag), \
|
|
s.imag = (l.imag * r.real - l.real * r.imag) / (r.real * r.real + r.imag * r.imag)
|
|
#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod")
|
|
#define POWC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex pow")
|
|
#endif
|
|
|
|
static int cdata_mul(lua_State* L)
|
|
{ NUMBER_ONLY_BINOP("__mul", MUL, MULC); }
|
|
|
|
static int cdata_div(lua_State* L)
|
|
{ NUMBER_ONLY_BINOP("__div", DIV, DIVC); }
|
|
|
|
static int cdata_mod(lua_State* L)
|
|
{ NUMBER_ONLY_BINOP("__mod", MOD, MODC); }
|
|
|
|
static int cdata_pow(lua_State* L)
|
|
{ NUMBER_ONLY_BINOP("__pow", POW, POWC); }
|
|
|
|
#define COMPARE_BINOP(OPSTR, OP, OPC) \
|
|
struct ctype lt, rt; \
|
|
void *lp, *rp; \
|
|
int ret, res; \
|
|
\
|
|
lua_settop(L, 2); \
|
|
\
|
|
lp = to_cdata(L, 1, <); \
|
|
rp = to_cdata(L, 2, &rt); \
|
|
\
|
|
ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \
|
|
if (ret >= 0) { \
|
|
return ret; \
|
|
} \
|
|
\
|
|
if (IS_COMPLEX(lt.type) || IS_COMPLEX(rt.type)) { \
|
|
complex_double left = check_complex(L, 1, lp, <); \
|
|
complex_double right = check_complex(L, 2, rp, &rt); \
|
|
\
|
|
res = OPC(left, right); \
|
|
\
|
|
lua_pushboolean(L, res); \
|
|
\
|
|
} else { \
|
|
int64_t left = check_intptr(L, 1, lp, <); \
|
|
int64_t right = check_intptr(L, 2, rp, &rt); \
|
|
\
|
|
if (lt.pointers && rt.pointers) { \
|
|
if (is_void_ptr(<) || is_void_ptr(&rt) || is_same_type(L, 3, 4, <, &rt)) { \
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
} else { \
|
|
goto err; \
|
|
} \
|
|
\
|
|
} else if (lt.is_null && rt.type == FUNCTION_PTR_TYPE) { \
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
\
|
|
} else if (rt.is_null && lt.type == FUNCTION_PTR_TYPE) { \
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
\
|
|
} else if (lt.pointers && rt.type == INTPTR_TYPE && rt.is_unsigned) {\
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
\
|
|
} else if (rt.pointers && lt.type == INTPTR_TYPE && lt.is_unsigned) {\
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
\
|
|
} else if (rt.pointers || lt.pointers) { \
|
|
goto err; \
|
|
\
|
|
} else if (lt.is_unsigned && rt.is_unsigned) { \
|
|
res = OP((uint64_t) left, (uint64_t) right); \
|
|
\
|
|
} else if (lt.is_unsigned) { \
|
|
res = OP((int64_t) (uint64_t) left, right); \
|
|
\
|
|
} else if (rt.is_unsigned) { \
|
|
res = OP(left, (int64_t) (uint64_t) right); \
|
|
\
|
|
} else { \
|
|
res = OP(left, right); \
|
|
} \
|
|
\
|
|
lua_pushboolean(L, res); \
|
|
} \
|
|
return 1
|
|
|
|
#define EQ(l, r) (l) == (r)
|
|
#define LT(l, r) (l) < (r)
|
|
#define LE(l, r) (l) <= (r)
|
|
|
|
#ifdef HAVE_COMPLEX
|
|
#define EQC(l, r) (l) == (r)
|
|
#else
|
|
#define EQC(l, r) (l).real == (r).real && (l).imag == (r).imag
|
|
#endif
|
|
|
|
#define LEC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable")
|
|
#define LTC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable")
|
|
|
|
static int cdata_eq(lua_State* L)
|
|
{
|
|
COMPARE_BINOP("__eq", EQ, EQC);
|
|
err:
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
static int cdata_lt(lua_State* L)
|
|
{
|
|
COMPARE_BINOP("__lt", LT, LTC);
|
|
err:
|
|
lua_getuservalue(L, 1);
|
|
lua_getuservalue(L, 2);
|
|
push_type_name(L, -2, <);
|
|
push_type_name(L, -2, <);
|
|
return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1));
|
|
}
|
|
|
|
static int cdata_le(lua_State* L)
|
|
{
|
|
COMPARE_BINOP("__le", LE, LEC);
|
|
err:
|
|
lua_getuservalue(L, 1);
|
|
lua_getuservalue(L, 2);
|
|
push_type_name(L, -2, <);
|
|
push_type_name(L, -2, <);
|
|
return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1));
|
|
}
|
|
|
|
static const char* etype_tostring(int type)
|
|
{
|
|
switch (type) {
|
|
case VOID_TYPE: return "void";
|
|
case DOUBLE_TYPE: return "double";
|
|
case FLOAT_TYPE: return "float";
|
|
case COMPLEX_DOUBLE_TYPE: return "complex double";
|
|
case COMPLEX_FLOAT_TYPE: return "complex float";
|
|
case BOOL_TYPE: return "bool";
|
|
case INT8_TYPE: return "int8";
|
|
case INT16_TYPE: return "int16";
|
|
case INT32_TYPE: return "int32";
|
|
case INT64_TYPE: return "int64";
|
|
case INTPTR_TYPE: return "intptr";
|
|
case ENUM_TYPE: return "enum";
|
|
case UNION_TYPE: return "union";
|
|
case STRUCT_TYPE: return "struct";
|
|
case FUNCTION_PTR_TYPE: return "function ptr";
|
|
case FUNCTION_TYPE: return "function";
|
|
default: return "invalid";
|
|
}
|
|
}
|
|
|
|
static void print_type(lua_State* L, const struct ctype* ct)
|
|
{
|
|
lua_pushfstring(L, " sz %d %d %d align %d ptr %d %d %d type %s%s %d %d %d name %d call %d %d var %d %d %d bit %d %d %d %d jit %d",
|
|
/* sz */
|
|
ct->base_size,
|
|
ct->array_size,
|
|
ct->offset,
|
|
/* align */
|
|
ct->align_mask,
|
|
/* ptr */
|
|
ct->is_array,
|
|
ct->pointers,
|
|
ct->const_mask,
|
|
/* type */
|
|
ct->is_unsigned ? "u" : "",
|
|
etype_tostring(ct->type),
|
|
ct->is_reference,
|
|
ct->is_defined,
|
|
ct->is_null,
|
|
/* name */
|
|
ct->has_member_name,
|
|
/* call */
|
|
ct->calling_convention,
|
|
ct->has_var_arg,
|
|
/* var */
|
|
ct->is_variable_array,
|
|
ct->is_variable_struct,
|
|
ct->variable_size_known,
|
|
/* bit */
|
|
ct->is_bitfield,
|
|
ct->has_bitfield,
|
|
ct->bit_offset,
|
|
ct->bit_size,
|
|
/* jit */
|
|
ct->is_jitted);
|
|
}
|
|
|
|
static int ctype_tostring(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
assert(lua_type(L, 1) == LUA_TUSERDATA);
|
|
lua_settop(L, 1);
|
|
check_ctype(L, 1, &ct);
|
|
assert(lua_gettop(L) == 2);
|
|
push_type_name(L, -1, &ct);
|
|
lua_pushfstring(L, "ctype<%s> %p", lua_tostring(L, -1), lua_topointer(L, 1));
|
|
|
|
if (DEBUG_TOSTRING) {
|
|
print_type(L, &ct);
|
|
lua_concat(L, 2);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int cdata_tostring(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
char buf[64];
|
|
void* p;
|
|
int ret;
|
|
|
|
lua_settop(L, 1);
|
|
p = to_cdata(L, 1, &ct);
|
|
|
|
ret = call_user_op(L, "__tostring", 1, 2, &ct);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ct.pointers > 0 || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {
|
|
push_type_name(L, -1, &ct);
|
|
lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), p);
|
|
|
|
if (DEBUG_TOSTRING) {
|
|
print_type(L, &ct);
|
|
lua_concat(L, 2);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
switch (ct.type) {
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
{
|
|
complex_double c = *(complex_double*) p;
|
|
if (cimag(c) != 0) {
|
|
lua_pushfstring(L, "%f+%fi", creal(c), cimag(c));
|
|
} else {
|
|
lua_pushfstring(L, "%f", creal(c));
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case COMPLEX_FLOAT_TYPE:
|
|
{
|
|
complex_float c = *(complex_float*) p;
|
|
if (cimagf(c) != 0) {
|
|
lua_pushfstring(L, "%f+%fi", crealf(c), cimagf(c));
|
|
} else {
|
|
lua_pushfstring(L, "%f", crealf(c));
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case FUNCTION_PTR_TYPE:
|
|
p = *(void**) p;
|
|
push_type_name(L, -1, &ct);
|
|
lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), *(void**) p);
|
|
return 1;
|
|
|
|
case INTPTR_TYPE:
|
|
lua_pushfstring(L, "%p", *(uintptr_t*) p);
|
|
return 1;
|
|
|
|
case INT64_TYPE:
|
|
sprintf(buf, ct.is_unsigned ? "%"PRIu64 : "%"PRId64, *(uint64_t*) p);
|
|
lua_pushstring(L, buf);
|
|
return 1;
|
|
|
|
default:
|
|
sprintf(buf, ct.is_unsigned ? "%"PRId64 : "%"PRId64, (int64_t) check_intptr(L, 1, p, &ct));
|
|
lua_pushstring(L, buf);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int ffi_errno(lua_State* L)
|
|
{
|
|
struct jit* jit = get_jit(L);
|
|
|
|
if (!lua_isnoneornil(L, 1)) {
|
|
lua_pushnumber(L, jit->last_errno);
|
|
jit->last_errno = luaL_checknumber(L, 1);
|
|
} else {
|
|
lua_pushnumber(L, jit->last_errno);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_number(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
void* data = to_cdata(L, 1, &ct);
|
|
|
|
if (ct.type != INVALID_TYPE) {
|
|
lua_pushnumber(L, check_intptr(L, 1, data, &ct));
|
|
return 1;
|
|
} else {
|
|
/* call the old _G.tonumber, we use an upvalue as _G.tonumber is set
|
|
* to this function */
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
lua_insert(L, 1);
|
|
lua_call(L, lua_gettop(L)-1, LUA_MULTRET);
|
|
return lua_gettop(L);
|
|
}
|
|
}
|
|
|
|
static int ffi_string(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
char* data;
|
|
lua_settop(L, 2);
|
|
|
|
data = (char*) check_cdata(L, 1, &ct);
|
|
|
|
if (is_void_ptr(&ct)) {
|
|
lua_pushlstring(L, data, (size_t) luaL_checknumber(L, 2));
|
|
return 1;
|
|
|
|
} else if (ct.type == INT8_TYPE && ct.pointers == 1) {
|
|
size_t sz;
|
|
|
|
if (!lua_isnil(L, 2)) {
|
|
sz = (size_t) luaL_checknumber(L, 2);
|
|
|
|
} else if (ct.is_array && !ct.is_variable_array) {
|
|
char* nul = memchr(data, '\0', ct.array_size);
|
|
sz = nul ? nul - data : ct.array_size;
|
|
|
|
} else {
|
|
sz = strlen(data);
|
|
}
|
|
|
|
lua_pushlstring(L, data, sz);
|
|
return 1;
|
|
}
|
|
|
|
return luaL_error(L, "unable to convert cdata to string");
|
|
}
|
|
|
|
static int ffi_copy(lua_State* L)
|
|
{
|
|
struct ctype ft, tt;
|
|
char *to, *from;
|
|
|
|
setmintop(L, 3);
|
|
to = (char*) check_pointer(L, 1, &tt);
|
|
from = (char*) check_pointer(L, 2, &ft);
|
|
|
|
if (!lua_isnoneornil(L, 3)) {
|
|
memcpy(to, from, (size_t) luaL_checknumber(L, 3));
|
|
|
|
} else if (ft.type == INT8_TYPE && ft.pointers == 1) {
|
|
size_t sz = ft.is_array ? ft.array_size : strlen(from);
|
|
memcpy(to, from, sz);
|
|
to[sz] = '\0';
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ffi_fill(lua_State* L)
|
|
{
|
|
struct ctype ct;
|
|
void* to;
|
|
size_t sz;
|
|
int val = 0;
|
|
|
|
setmintop(L, 3);
|
|
to = check_pointer(L, 1, &ct);
|
|
sz = (size_t) luaL_checknumber(L, 2);
|
|
|
|
if (!lua_isnoneornil(L, 3)) {
|
|
val = (int) luaL_checkinteger(L, 3);
|
|
}
|
|
|
|
memset(to, val, sz);
|
|
return 0;
|
|
}
|
|
|
|
static int ffi_abi(lua_State* L)
|
|
{
|
|
luaL_checkstring(L, 1);
|
|
push_upval(L, &abi_key);
|
|
lua_pushvalue(L, 1);
|
|
lua_rawget(L, -2);
|
|
lua_pushboolean(L, lua_toboolean(L, -1));
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_load(lua_State* L)
|
|
{
|
|
const char* libname = luaL_checkstring(L, 1);
|
|
void** lib = (void**) lua_newuserdata(L, sizeof(void*));
|
|
|
|
*lib = LoadLibraryA(libname);
|
|
|
|
#ifdef LIB_FORMAT_1
|
|
if (!*lib) {
|
|
libname = lua_pushfstring(L, LIB_FORMAT_1, lua_tostring(L, 1));
|
|
*lib = LoadLibraryA(libname);
|
|
lua_pop(L, 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef LIB_FORMAT_2
|
|
if (!*lib) {
|
|
libname = lua_pushfstring(L, LIB_FORMAT_2, lua_tostring(L, 1));
|
|
*lib = LoadLibraryA(libname);
|
|
lua_pop(L, 1);
|
|
}
|
|
#endif
|
|
|
|
if (!*lib) {
|
|
return luaL_error(L, "could not load library %s", lua_tostring(L, 1));
|
|
}
|
|
|
|
lua_newtable(L);
|
|
lua_setuservalue(L, -2);
|
|
|
|
push_upval(L, &cmodule_mt_key);
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
static void* find_symbol(lua_State* L, int modidx, const char* asmname)
|
|
{
|
|
size_t i;
|
|
void** libs;
|
|
size_t num;
|
|
void* sym = NULL;
|
|
|
|
libs = (void**) lua_touserdata(L, modidx);
|
|
num = lua_rawlen(L, modidx) / sizeof(void*);
|
|
|
|
for (i = 0; i < num && sym == NULL; i++) {
|
|
if (libs[i]) {
|
|
sym = GetProcAddressA(libs[i], asmname);
|
|
}
|
|
}
|
|
|
|
return sym;
|
|
}
|
|
|
|
/* pushes the user table */
|
|
static void* lookup_global(lua_State* L, int modidx, int nameidx, const char** pname, struct ctype* ct)
|
|
{
|
|
int top = lua_gettop(L);
|
|
void* sym;
|
|
|
|
modidx = lua_absindex(L, modidx);
|
|
nameidx = lua_absindex(L, nameidx);
|
|
|
|
*pname = luaL_checkstring(L, nameidx);
|
|
|
|
/* get the ctype */
|
|
push_upval(L, &functions_key);
|
|
lua_pushvalue(L, nameidx);
|
|
lua_rawget(L, -2);
|
|
if (lua_isnil(L, -1)) {
|
|
luaL_error(L, "missing declaration for function/global %s", *pname);
|
|
return NULL;
|
|
}
|
|
|
|
/* leave just the ct_usr on the stack */
|
|
*ct = *(const struct ctype*) lua_touserdata(L, -1);
|
|
lua_getuservalue(L, -1);
|
|
lua_replace(L, top + 1);
|
|
lua_pop(L, 1);
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
|
|
/* get the assembly name */
|
|
push_upval(L, &asmname_key);
|
|
lua_pushvalue(L, nameidx);
|
|
lua_rawget(L, -2);
|
|
if (lua_isstring(L, -1)) {
|
|
*pname = lua_tostring(L, -1);
|
|
}
|
|
lua_pop(L, 2);
|
|
|
|
sym = find_symbol(L, modidx, *pname);
|
|
|
|
assert(lua_gettop(L) == top + 1);
|
|
return sym;
|
|
}
|
|
|
|
static int cmodule_index(lua_State* L)
|
|
{
|
|
const char* asmname;
|
|
struct ctype ct;
|
|
void *sym;
|
|
|
|
lua_settop(L, 2);
|
|
|
|
/* see if we have already loaded the function */
|
|
lua_getuservalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawget(L, -2);
|
|
if (!lua_isnil(L, -1)) {
|
|
return 1;
|
|
}
|
|
lua_pop(L, 2);
|
|
|
|
/* check the constants table */
|
|
push_upval(L, &constants_key);
|
|
lua_pushvalue(L, 2);
|
|
lua_rawget(L, -2);
|
|
if (!lua_isnil(L, -1)) {
|
|
return 1;
|
|
}
|
|
lua_pop(L, 2);
|
|
|
|
/* lookup_global pushes the ct_usr */
|
|
sym = lookup_global(L, 1, 2, &asmname, &ct);
|
|
|
|
#if defined _WIN32 && !defined _WIN64 && (defined __i386__ || defined _M_IX86)
|
|
if (!sym && ct.type == FUNCTION_TYPE) {
|
|
ct.calling_convention = STD_CALL;
|
|
lua_pushfstring(L, "_%s@%d", asmname, x86_return_size(L, -1, &ct));
|
|
sym = find_symbol(L, 1, lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
if (!sym && ct.type == FUNCTION_TYPE) {
|
|
ct.calling_convention = FAST_CALL;
|
|
lua_pushfstring(L, "@%s@%d", asmname, x86_return_size(L, -1, &ct));
|
|
sym = find_symbol(L, 1, lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
#endif
|
|
|
|
if (!sym) {
|
|
return luaL_error(L, "failed to find function/global %s", asmname);
|
|
}
|
|
|
|
assert(lua_gettop(L) == 3); /* module, name, ct_usr */
|
|
|
|
if (ct.type == FUNCTION_TYPE) {
|
|
compile_function(L, (cfunction) sym, -1, &ct);
|
|
assert(lua_gettop(L) == 4); /* module, name, ct_usr, function */
|
|
|
|
/* set module usr value[luaname] = function to cache for next time */
|
|
lua_getuservalue(L, 1);
|
|
lua_pushvalue(L, 2);
|
|
lua_pushvalue(L, -3);
|
|
lua_rawset(L, -3);
|
|
lua_pop(L, 1); /* module uv */
|
|
return 1;
|
|
}
|
|
|
|
/* extern const char* foo; and extern const char foo[]; */
|
|
if (ct.pointers == 1 && ct.type == INT8_TYPE) {
|
|
char* str = (char*) sym;
|
|
if (!ct.is_array) {
|
|
str = *(char**) sym;
|
|
}
|
|
lua_pushstring(L, str);
|
|
return 1;
|
|
}
|
|
|
|
/* extern struct foo foo[], extern void* foo[]; and extern struct foo foo; */
|
|
if (ct.is_array || (!ct.pointers && (ct.type == UNION_TYPE || ct.type == STRUCT_TYPE))) {
|
|
void* p;
|
|
ct.is_reference = 1;
|
|
p = push_cdata(L, -1, &ct);
|
|
*(void**) p = sym;
|
|
return 1;
|
|
}
|
|
|
|
/* extern void* foo; and extern void (*foo)(); */
|
|
if (ct.pointers || ct.type == FUNCTION_PTR_TYPE) {
|
|
void* p = push_cdata(L, -1, &ct);
|
|
*(void**) p = *(void**) sym;
|
|
return 1;
|
|
}
|
|
|
|
switch (ct.type) {
|
|
case COMPLEX_DOUBLE_TYPE:
|
|
case COMPLEX_FLOAT_TYPE:
|
|
case INTPTR_TYPE:
|
|
case INT64_TYPE:
|
|
{
|
|
/* TODO: complex float/double need to be references if .re and
|
|
* .imag are setable */
|
|
void* p = push_cdata(L, -1, &ct);
|
|
memcpy(p, sym, ct.base_size);
|
|
return 1;
|
|
}
|
|
|
|
case DOUBLE_TYPE:
|
|
lua_pushnumber(L, *(double*) sym);
|
|
return 1;
|
|
|
|
case FLOAT_TYPE:
|
|
lua_pushnumber(L, *(float*) sym);
|
|
return 1;
|
|
|
|
case BOOL_TYPE:
|
|
lua_pushboolean(L, *(bool*) sym);
|
|
return 1;
|
|
|
|
case INT8_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint8_t*) sym : (lua_Number) *(int8_t*) sym);
|
|
return 1;
|
|
|
|
case INT16_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint16_t*) sym : (lua_Number) *(int16_t*) sym);
|
|
return 1;
|
|
|
|
case INT32_TYPE:
|
|
case ENUM_TYPE:
|
|
lua_pushnumber(L, ct.is_unsigned ? (lua_Number) *(uint32_t*) sym : (lua_Number) *(int32_t*) sym);
|
|
return 1;
|
|
}
|
|
|
|
return luaL_error(L, "NYI - global value type");
|
|
}
|
|
|
|
static int cmodule_newindex(lua_State* L)
|
|
{
|
|
const char* name;
|
|
void* sym;
|
|
struct ctype ct;
|
|
|
|
lua_settop(L, 3);
|
|
|
|
/* pushes the ct_usr */
|
|
sym = lookup_global(L, 1, 2, &name, &ct);
|
|
assert(lua_gettop(L) == 4); /* module, name, value, ct_usr */
|
|
|
|
if (sym == NULL) {
|
|
return luaL_error(L, "failed to find global %s", name);
|
|
}
|
|
|
|
if (ct.type == FUNCTION_TYPE || ct.is_array || (ct.const_mask & 1)) {
|
|
return luaL_error(L, "can not set global %s", name);
|
|
}
|
|
|
|
set_value(L, 3, sym, -1, &ct, 1);
|
|
return 0;
|
|
}
|
|
|
|
static int jit_gc(lua_State* L)
|
|
{
|
|
size_t i;
|
|
struct jit* jit = get_jit(L);
|
|
dasm_free(jit);
|
|
for (i = 0; i < jit->pagenum; i++) {
|
|
FreePage(jit->pages[i], jit->pages[i]->size);
|
|
}
|
|
free(jit->globals);
|
|
return 0;
|
|
}
|
|
|
|
static int ffi_debug(lua_State* L)
|
|
{
|
|
lua_newtable(L);
|
|
push_upval(L, &ctype_mt_key);
|
|
lua_setfield(L, -2, "ctype_mt");
|
|
push_upval(L, &cdata_mt_key);
|
|
lua_setfield(L, -2, "cdata_mt");
|
|
push_upval(L, &cmodule_mt_key);
|
|
lua_setfield(L, -2, "cmodule_mt");
|
|
push_upval(L, &constants_key);
|
|
lua_setfield(L, -2, "constants");
|
|
push_upval(L, &types_key);
|
|
lua_setfield(L, -2, "types");
|
|
push_upval(L, &jit_key);
|
|
lua_setfield(L, -2, "jit");
|
|
push_upval(L, &gc_key);
|
|
lua_setfield(L, -2, "gc");
|
|
push_upval(L, &callbacks_key);
|
|
lua_setfield(L, -2, "callbacks");
|
|
push_upval(L, &functions_key);
|
|
lua_setfield(L, -2, "functions");
|
|
push_upval(L, &abi_key);
|
|
lua_setfield(L, -2, "abi");
|
|
push_upval(L, &next_unnamed_key);
|
|
lua_setfield(L, -2, "next_unnamed");
|
|
return 1;
|
|
}
|
|
|
|
static int do64(lua_State* L, int is_unsigned)
|
|
{
|
|
lua_Number low, high;
|
|
struct ctype ct;
|
|
int64_t val;
|
|
|
|
lua_settop(L, 2);
|
|
|
|
if (!lua_isnil(L, 2)) {
|
|
high = luaL_checknumber(L, 1);
|
|
low = luaL_checknumber(L, 2);
|
|
} else {
|
|
high = 0;
|
|
low = luaL_checknumber(L, 1);
|
|
}
|
|
|
|
val = ((int64_t) (uint32_t) high << 32) | (int64_t) (uint32_t) low;
|
|
|
|
if (!is_unsigned && (high < 0 || low < 0)) {
|
|
val = -val;
|
|
}
|
|
|
|
memset(&ct, 0, sizeof(ct));
|
|
ct.type = INT64_TYPE;
|
|
ct.is_unsigned = is_unsigned;
|
|
ct.is_defined = 1;
|
|
ct.base_size = sizeof(int64_t);
|
|
push_number(L, (int64_t) val, 0, &ct);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ffi_i64(lua_State* L)
|
|
{ return do64(L, 0); }
|
|
|
|
static int ffi_u64(lua_State* L)
|
|
{ return do64(L, 1); }
|
|
|
|
static const luaL_Reg cdata_mt[] = {
|
|
{"__gc", &cdata_gc},
|
|
{"__call", &cdata_call},
|
|
{"free", &cdata_free},
|
|
{"set", &cdata_set},
|
|
{"__index", &cdata_index},
|
|
{"__newindex", &cdata_newindex},
|
|
{"__add", &cdata_add},
|
|
{"__sub", &cdata_sub},
|
|
{"__mul", &cdata_mul},
|
|
{"__div", &cdata_div},
|
|
{"__mod", &cdata_mod},
|
|
{"__pow", &cdata_pow},
|
|
{"__unm", &cdata_unm},
|
|
{"__eq", &cdata_eq},
|
|
{"__lt", &cdata_lt},
|
|
{"__le", &cdata_le},
|
|
{"__tostring", &cdata_tostring},
|
|
{"__concat", &cdata_concat},
|
|
{"__len", &cdata_len},
|
|
{"__pairs", &cdata_pairs},
|
|
{"__ipairs", &cdata_ipairs},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg callback_mt[] = {
|
|
{"__gc", &callback_free},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg ctype_mt[] = {
|
|
{"__call", &ctype_call},
|
|
{"__new", &ctype_new},
|
|
{"__tostring", &ctype_tostring},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg cmodule_mt[] = {
|
|
{"__index", &cmodule_index},
|
|
{"__newindex", &cmodule_newindex},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg jit_mt[] = {
|
|
{"__gc", &jit_gc},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const luaL_Reg ffi_reg[] = {
|
|
{"cdef", &ffi_cdef},
|
|
{"load", &ffi_load},
|
|
{"new", &ffi_new},
|
|
{"typeof", &ffi_typeof},
|
|
{"cast", &ffi_cast},
|
|
{"metatype", &ffi_metatype},
|
|
{"gc", &ffi_gc},
|
|
{"sizeof", &ffi_sizeof},
|
|
{"alignof", &ffi_alignof},
|
|
{"offsetof", &ffi_offsetof},
|
|
{"istype", &ffi_istype},
|
|
{"errno", &ffi_errno},
|
|
{"string", &ffi_string},
|
|
{"copy", &ffi_copy},
|
|
{"fill", &ffi_fill},
|
|
{"abi", &ffi_abi},
|
|
{"debug", &ffi_debug},
|
|
{"i64", &ffi_i64},
|
|
{"u64", &ffi_u64},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/* leaves the usr table on the stack */
|
|
static void push_builtin(lua_State* L, struct ctype* ct, const char* name, int type, int size, int align, int is_unsigned)
|
|
{
|
|
memset(ct, 0, sizeof(*ct));
|
|
ct->type = type;
|
|
ct->base_size = size;
|
|
ct->align_mask = align;
|
|
ct->is_defined = 1;
|
|
ct->is_unsigned = is_unsigned;
|
|
|
|
push_upval(L, &types_key);
|
|
push_ctype(L, 0, ct);
|
|
lua_setfield(L, -2, name);
|
|
lua_pop(L, 1); /* types */
|
|
}
|
|
|
|
static void push_builtin_undef(lua_State* L, struct ctype* ct, const char* name, int type)
|
|
{
|
|
memset(ct, 0, sizeof(*ct));
|
|
ct->type = type;
|
|
|
|
push_upval(L, &types_key);
|
|
push_ctype(L, 0, ct);
|
|
lua_setfield(L, -2, name);
|
|
lua_pop(L, 1); /* types */
|
|
}
|
|
|
|
static void add_typedef(lua_State* L, const char* from, const char* to)
|
|
{
|
|
struct ctype ct;
|
|
struct parser P;
|
|
P.line = 1;
|
|
P.align_mask = DEFAULT_ALIGN_MASK;
|
|
P.next = P.prev = from;
|
|
|
|
push_upval(L, &types_key);
|
|
parse_type(L, &P, &ct);
|
|
parse_argument(L, &P, -1, &ct, NULL, NULL);
|
|
push_ctype(L, -1, &ct);
|
|
|
|
/* stack is at +4: types, type usr, arg usr, ctype */
|
|
|
|
lua_setfield(L, -4, to);
|
|
lua_pop(L, 3); /* types, type usr, arg usr */
|
|
}
|
|
|
|
static int setup_upvals(lua_State* L)
|
|
{
|
|
struct jit* jit = get_jit(L);
|
|
|
|
/* jit setup */
|
|
{
|
|
dasm_init(jit, 64);
|
|
#ifdef _WIN32
|
|
{
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
jit->align_page_size = si.dwAllocationGranularity - 1;
|
|
}
|
|
#else
|
|
jit->align_page_size = sysconf(_SC_PAGE_SIZE) - 1;
|
|
#endif
|
|
jit->globals = (void**) malloc(64 * sizeof(void*));
|
|
dasm_setupglobal(jit, jit->globals, 64);
|
|
compile_globals(jit, L);
|
|
}
|
|
|
|
/* ffi.C */
|
|
{
|
|
#ifdef _WIN32
|
|
size_t sz = sizeof(HMODULE) * 6;
|
|
HMODULE* libs = lua_newuserdata(L, sz);
|
|
memset(libs, 0, sz);
|
|
|
|
/* exe */
|
|
GetModuleHandle(NULL);
|
|
/* lua dll */
|
|
#ifdef LUA_DLL_NAME
|
|
#define STR2(tok) #tok
|
|
#define STR(tok) STR2(tok)
|
|
libs[1] = LoadLibraryA(STR(LUA_DLL_NAME));
|
|
#undef STR
|
|
#undef STR2
|
|
#endif
|
|
|
|
/* crt */
|
|
#ifdef UNDER_CE
|
|
libs[2] = LoadLibraryA("coredll.dll");
|
|
#else
|
|
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (char*) &_fmode, &libs[2]);
|
|
libs[3] = LoadLibraryA("kernel32.dll");
|
|
libs[4] = LoadLibraryA("user32.dll");
|
|
libs[5] = LoadLibraryA("gdi32.dll");
|
|
#endif
|
|
|
|
jit->lua_dll = libs[1];
|
|
jit->kernel32_dll = libs[3];
|
|
|
|
#else /* !_WIN32 */
|
|
size_t sz = sizeof(void*) * 5;
|
|
void** libs = lua_newuserdata(L, sz);
|
|
memset(libs, 0, sz);
|
|
|
|
libs[0] = LoadLibraryA(NULL); /* exe */
|
|
libs[1] = LoadLibraryA("libc.so");
|
|
#ifdef __GNUC__
|
|
libs[2] = LoadLibraryA("libgcc.so");
|
|
#endif
|
|
libs[3] = LoadLibraryA("libm.so");
|
|
libs[4] = LoadLibraryA("libdl.so");
|
|
#endif
|
|
|
|
lua_newtable(L);
|
|
lua_setuservalue(L, -2);
|
|
|
|
push_upval(L, &cmodule_mt_key);
|
|
lua_setmetatable(L, -2);
|
|
|
|
lua_setfield(L, 1, "C");
|
|
}
|
|
|
|
/* setup builtin types */
|
|
{
|
|
complex_double* pc;
|
|
struct {char ch; uint16_t v;} a16;
|
|
struct {char ch; uint32_t v;} a32;
|
|
struct {char ch; uint64_t v;} a64;
|
|
struct {char ch; float v;} af;
|
|
struct {char ch; double v;} ad;
|
|
#ifdef HAVE_LONG_DOUBLE
|
|
struct {char ch; long double v;} ald;
|
|
#endif
|
|
struct {char ch; uintptr_t v;} aptr;
|
|
struct ctype ct;
|
|
struct {char ch; complex_float v;} cf;
|
|
struct {char ch; complex_double v;} cd;
|
|
#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX
|
|
struct {char ch; complex long double v;} cld;
|
|
#endif
|
|
|
|
push_builtin(L, &ct, "void", VOID_TYPE, 0, 0, 0);
|
|
push_builtin(L, &ct, "bool", BOOL_TYPE, sizeof(_Bool), sizeof(_Bool) -1, 1);
|
|
push_builtin(L, &ct, "uint8_t", INT8_TYPE, sizeof(uint8_t), 0, 1);
|
|
push_builtin(L, &ct, "int8_t", INT8_TYPE, sizeof(int8_t), 0, 0);
|
|
push_builtin(L, &ct, "uint16_t", INT16_TYPE, sizeof(uint16_t), ALIGNOF(a16), 1);
|
|
push_builtin(L, &ct, "int16_t", INT16_TYPE, sizeof(int16_t), ALIGNOF(a16), 0);
|
|
push_builtin(L, &ct, "uint32_t", INT32_TYPE, sizeof(uint32_t), ALIGNOF(a32), 1);
|
|
push_builtin(L, &ct, "int32_t", INT32_TYPE, sizeof(int32_t), ALIGNOF(a32), 0);
|
|
push_builtin(L, &ct, "uint64_t", INT64_TYPE, sizeof(uint64_t), ALIGNOF(a64), 1);
|
|
push_builtin(L, &ct, "int64_t", INT64_TYPE, sizeof(int64_t), ALIGNOF(a64), 0);
|
|
push_builtin(L, &ct, "float", FLOAT_TYPE, sizeof(float), ALIGNOF(af), 0);
|
|
push_builtin(L, &ct, "double", DOUBLE_TYPE, sizeof(double), ALIGNOF(ad), 0);
|
|
#ifdef HAVE_LONG_DOUBLE
|
|
push_builtin(L, &ct, "long double", LONG_DOUBLE_TYPE, sizeof(long double), ALIGNOF(ald), 0);
|
|
#else
|
|
push_builtin_undef(L, &ct, "long double", LONG_DOUBLE_TYPE);
|
|
#endif
|
|
push_builtin(L, &ct, "uintptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 1);
|
|
push_builtin(L, &ct, "intptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 0);
|
|
push_builtin(L, &ct, "complex float", COMPLEX_FLOAT_TYPE, sizeof(complex_float), ALIGNOF(cf), 0);
|
|
push_builtin(L, &ct, "complex double", COMPLEX_DOUBLE_TYPE, sizeof(complex_double), ALIGNOF(cd), 0);
|
|
#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX
|
|
push_builtin(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE, sizeof(complex long double), ALIGNOF(cld), 0);
|
|
#else
|
|
push_builtin_undef(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE);
|
|
#endif
|
|
|
|
/* add NULL and i constants */
|
|
push_upval(L, &constants_key);
|
|
|
|
memset(&ct, 0, sizeof(ct));
|
|
ct.type = VOID_TYPE;
|
|
ct.is_defined = 1;
|
|
ct.pointers = 1;
|
|
ct.is_null = 1;
|
|
|
|
push_cdata(L, 0, &ct);
|
|
lua_setfield(L, -2, "NULL");
|
|
|
|
memset(&ct, 0, sizeof(ct));
|
|
ct.type = COMPLEX_DOUBLE_TYPE;
|
|
ct.is_defined = 1;
|
|
ct.base_size = sizeof(complex_double);
|
|
pc = (complex_double*) push_cdata(L, 0, &ct);
|
|
#ifdef HAVE_COMPLEX
|
|
*pc = 1i;
|
|
#else
|
|
pc->real = 0;
|
|
pc->imag = 1;
|
|
#endif
|
|
lua_setfield(L, -2, "i");
|
|
|
|
lua_pop(L, 1); /* constants */
|
|
}
|
|
|
|
assert(lua_gettop(L) == 1);
|
|
|
|
/* setup builtin typedefs */
|
|
{
|
|
add_typedef(L, "bool", "_Bool");
|
|
|
|
if (sizeof(uint32_t) == sizeof(size_t)) {
|
|
add_typedef(L, "uint32_t", "size_t");
|
|
add_typedef(L, "int32_t", "ssize_t");
|
|
} else if (sizeof(uint64_t) == sizeof(size_t)) {
|
|
add_typedef(L, "uint64_t", "size_t");
|
|
add_typedef(L, "int64_t", "ssize_t");
|
|
}
|
|
|
|
if (sizeof(int32_t) == sizeof(intptr_t)) {
|
|
add_typedef(L, "int32_t", "intptr_t");
|
|
add_typedef(L, "int32_t", "ptrdiff_t");
|
|
} else if (sizeof(int64_t) == sizeof(intptr_t)) {
|
|
add_typedef(L, "int64_t", "intptr_t");
|
|
add_typedef(L, "int64_t", "ptrdiff_t");
|
|
}
|
|
|
|
if (sizeof(uint8_t) == sizeof(wchar_t)) {
|
|
add_typedef(L, "uint8_t", "wchar_t");
|
|
} else if (sizeof(uint16_t) == sizeof(wchar_t)) {
|
|
add_typedef(L, "uint16_t", "wchar_t");
|
|
} else if (sizeof(uint32_t) == sizeof(wchar_t)) {
|
|
add_typedef(L, "uint32_t", "wchar_t");
|
|
}
|
|
|
|
if (sizeof(va_list) == sizeof(char*)) {
|
|
add_typedef(L, "char*", "va_list");
|
|
} else {
|
|
struct {char ch; va_list v;} av;
|
|
lua_pushfstring(L, "struct {char data[%d] __attribute__((align(%d)));}", (int) sizeof(va_list), (int) ALIGNOF(av) + 1);
|
|
add_typedef(L, lua_tostring(L, -1), "va_list");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
add_typedef(L, "va_list", "__builtin_va_list");
|
|
add_typedef(L, "va_list", "__gnuc_va_list");
|
|
}
|
|
|
|
assert(lua_gettop(L) == 1);
|
|
|
|
/* setup ABI params table */
|
|
push_upval(L, &abi_key);
|
|
|
|
#if defined ARCH_X86 || defined ARCH_ARM
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -2, "32bit");
|
|
#elif defined ARCH_X64
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -2, "64bit");
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
#if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_ARM
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -2, "le");
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
#if defined ARCH_X86 || defined ARCH_X64
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -2, "fpu");
|
|
#elif defined ARCH_ARM
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -2, "softfp");
|
|
#else
|
|
#error
|
|
#endif
|
|
lua_pop(L, 1); /* abi tbl */
|
|
|
|
|
|
/* GC table - shouldn't pin cdata values */
|
|
push_upval(L, &gc_key);
|
|
lua_newtable(L);
|
|
lua_pushliteral(L, "k");
|
|
lua_setfield(L, -2, "__mode");
|
|
lua_setmetatable(L, -2);
|
|
lua_pop(L, 1); /* gc table */
|
|
|
|
|
|
/* ffi.os */
|
|
#if defined OS_CE
|
|
lua_pushliteral(L, "WindowsCE");
|
|
#elif defined OS_WIN
|
|
lua_pushliteral(L, "Windows");
|
|
#elif defined OS_OSX
|
|
lua_pushliteral(L, "OSX");
|
|
#elif defined OS_LINUX
|
|
lua_pushliteral(L, "Linux");
|
|
#elif defined OS_BSD
|
|
lua_pushliteral(L, "BSD");
|
|
#elif defined OS_POSIX
|
|
lua_pushliteral(L, "POSIX");
|
|
#else
|
|
lua_pushliteral(L, "Other");
|
|
#endif
|
|
lua_setfield(L, 1, "os");
|
|
|
|
|
|
/* ffi.arch */
|
|
#if defined ARCH_X86
|
|
lua_pushliteral(L, "x86");
|
|
#elif defined ARCH_X64
|
|
lua_pushliteral(L, "x64");
|
|
#elif defined ARCH_ARM
|
|
lua_pushliteral(L, "arm");
|
|
#else
|
|
# error
|
|
#endif
|
|
lua_setfield(L, 1, "arch");
|
|
|
|
assert(lua_gettop(L) == 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void setup_mt(lua_State* L, const luaL_Reg* mt, int upvals)
|
|
{
|
|
lua_pushboolean(L, 1);
|
|
lua_setfield(L, -upvals-2, "__metatable");
|
|
luaL_setfuncs(L, mt, upvals);
|
|
}
|
|
|
|
int luaopen_ffi(lua_State* L)
|
|
{
|
|
lua_settop(L, 0);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &niluv_key);
|
|
|
|
lua_newtable(L);
|
|
setup_mt(L, ctype_mt, 0);
|
|
set_upval(L, &ctype_mt_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &callbacks_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &gc_key);
|
|
|
|
lua_newtable(L);
|
|
push_upval(L, &callbacks_key);
|
|
push_upval(L, &gc_key);
|
|
setup_mt(L, cdata_mt, 2);
|
|
set_upval(L, &cdata_mt_key);
|
|
|
|
lua_newtable(L);
|
|
setup_mt(L, callback_mt, 0);
|
|
set_upval(L, &callback_mt_key);
|
|
|
|
lua_newtable(L);
|
|
setup_mt(L, cmodule_mt, 0);
|
|
set_upval(L, &cmodule_mt_key);
|
|
|
|
memset(lua_newuserdata(L, sizeof(struct jit)), 0, sizeof(struct jit));
|
|
lua_newtable(L);
|
|
setup_mt(L, jit_mt, 0);
|
|
lua_setmetatable(L, -2);
|
|
set_upval(L, &jit_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &constants_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &types_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &functions_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &asmname_key);
|
|
|
|
lua_newtable(L);
|
|
set_upval(L, &abi_key);
|
|
|
|
lua_pushinteger(L, 1);
|
|
set_upval(L, &next_unnamed_key);
|
|
|
|
assert(lua_gettop(L) == 0);
|
|
|
|
/* ffi table */
|
|
lua_newtable(L);
|
|
luaL_setfuncs(L, ffi_reg, 0);
|
|
|
|
/* setup_upvals(ffi tbl) */
|
|
lua_pushcfunction(L, &setup_upvals);
|
|
lua_pushvalue(L, 1);
|
|
lua_call(L, 1, 0);
|
|
|
|
assert(lua_gettop(L) == 1);
|
|
|
|
lua_getglobal(L, "tonumber");
|
|
lua_pushcclosure(L, &ffi_number, 1);
|
|
lua_pushvalue(L, -1);
|
|
lua_setglobal(L, "tonumber");
|
|
lua_setfield(L, -2, "number"); /* ffi.number */
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|