From fd5f56e53ec38d750aee1fed30c91c649506bdac Mon Sep 17 00:00:00 2001 From: Michael Noser Date: Tue, 25 Nov 2025 06:04:36 +0100 Subject: [PATCH 1/5] Add libffi integration --- .clangd | 2 +- Makefile | 7 ++- src/umka_common.c | 1 - src/umka_common.h | 1 + src/umka_compiler.c | 2 +- src/umka_decl.c | 33 +++++++++++ src/umka_expr.c | 7 ++- src/umka_gen.c | 6 ++ src/umka_gen.h | 9 +++ src/umka_ident.c | 9 +++ src/umka_ident.h | 4 +- src/umka_lexer.c | 3 +- src/umka_lexer.h | 1 + src/umka_stmt.c | 141 ++++++++++++++++++++++++++++++++++++++++++-- src/umka_stmt.h | 1 + src/umka_types.c | 2 + src/umka_types.h | 3 +- src/umka_vm.c | 47 +++++++++++++++ src/umka_vm.h | 1 + 19 files changed, 266 insertions(+), 14 deletions(-) diff --git a/.clangd b/.clangd index ce76e8276..28e9d9a87 100644 --- a/.clangd +++ b/.clangd @@ -1,4 +1,4 @@ CompileFlags: - Add: [-DUMKA_BUILD -DUMKA_EXT_LIBS] + Add: [-DUMKA_BUILD -DUMKA_EXT_LIBS -I../libffi/build/include] InlayHints: Enabled: No \ No newline at end of file diff --git a/Makefile b/Makefile index 5d0c411b1..234de2b13 100644 --- a/Makefile +++ b/Makefile @@ -29,12 +29,17 @@ else ifneq ($(findstring MINGW64_NT,$(PLATFORM)),) DYNAMIC_CFLAGS_EXTRA = -shared -fvisibility=hidden endif +LIBS = -L../libffi/build/.libs \ + -lffi +INCLUDE = -I../libffi/build/include + # identical for all platforms: UMKA_LIB_STATIC = $(BUILD_PATH)/libumka.a UMKA_LIB_DYNAMIC = $(BUILD_PATH)/libumka.$(LIBEXT) UMKA_EXE = $(BUILD_PATH)/umka -CFLAGS = -s -fPIC -O3 -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS +#CFLAGS = -s -fPIC -O3 -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS +CFLAGS = -fPIC -ggdb -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS $(LIBS) $(INCLUDE) STATIC_CFLAGS = $(CFLAGS) -DUMKA_STATIC DYNAMIC_CFLAGS = $(CFLAGS) -DUMKA_BUILD $(DYNAMIC_CFLAGS_EXTRA) diff --git a/src/umka_common.c b/src/umka_common.c index 321330f99..60eec4eff 100644 --- a/src/umka_common.c +++ b/src/umka_common.c @@ -376,7 +376,6 @@ void moduleAddSource(Modules *modules, const char *path, const char *source, boo modules->moduleSource[modules->numModuleSources++] = moduleSource; } - void *moduleGetImplLibFunc(const Module *module, const char *name) { if (module->implLib) diff --git a/src/umka_common.h b/src/umka_common.h index 1a979b502..b09265053 100644 --- a/src/umka_common.h +++ b/src/umka_common.h @@ -210,6 +210,7 @@ int moduleAdd (Modules *modules, const char *path); const ModuleSource *moduleFindSource(const Modules *modules, const char *path); void moduleAddSource (Modules *modules, const char *path, const char *source, bool trusted); void *moduleGetImplLibFunc (const Module *module, const char *name); +void *getExternalDynamicFunc (const char* name); char *moduleCurFolder (char *buf, int size); bool modulePathIsAbsolute (const char *path); bool moduleRegularizePath (const Modules *modules, const char *path, const char *curFolder, char *regularizedPath, int size); diff --git a/src/umka_compiler.c b/src/umka_compiler.c index 23b8c7875..486346275 100644 --- a/src/umka_compiler.c +++ b/src/umka_compiler.c @@ -384,7 +384,7 @@ bool compilerGetFunc(Umka *umka, const char *moduleName, const char *funcName, U } const Ident *fnIdent = identFind(&umka->idents, &umka->modules, &umka->blocks, module, funcName, NULL, false); - if (!fnIdent || fnIdent->kind != IDENT_CONST || fnIdent->type->kind != TYPE_FN) + if (!fnIdent || fnIdent->kind != IDENT_CONST || fnIdent->type->kind != TYPE_FN || fnIdent->kind != IDENT_EXTERN_FN) return false; identSetUsed(fnIdent); diff --git a/src/umka_decl.c b/src/umka_decl.c index cd694a495..d74776337 100644 --- a/src/umka_decl.c +++ b/src/umka_decl.c @@ -1,3 +1,4 @@ +#include "umka_lexer.h" #define __USE_MINGW_ANSI_STDIO 1 #include @@ -742,6 +743,37 @@ void parseShortVarDecl(Umka *umka) } +// fnDecl = "fn" [rcvSignature] ident exportMark signature [block]. +static void parseExternFnDecl(Umka *umka) +{ + if (umka->blocks.top != 0) + umka->error.handler(umka->error.context, "Extern functions can only be declared at top-level"); + + lexEat(&umka->lex, TOK_EXTERN); + lexEat(&umka->lex, TOK_FN); + Type *fnType = typeAdd(&umka->types, &umka->blocks, TYPE_EXTERNFN); + + if (umka->lex.tok.kind == TOK_LPAR) + parseRcvSignature(umka, &fnType->sig); + + lexCheck(&umka->lex, TOK_IDENT); + IdentName name; + strcpy(name, umka->lex.tok.name); + + lexNext(&umka->lex); + bool exported = parseExportMark(umka); + + parseSignature(umka, &fnType->sig); + + Const constant = {.intVal = umka->gen.ip}; + Ident *fn = identAddExternFunc(&umka->idents, &umka->modules, &umka->blocks, name, fnType, exported, constant); + + if (umka->lex.tok.kind == TOK_LBRACE) + umka->error.handler(umka->error.context, "Extern functions can only be declared as prototype"); + else + parseFnPrototype(umka, fn); +} + // fnDecl = "fn" [rcvSignature] ident exportMark signature [block]. static void parseFnDecl(Umka *umka) { @@ -791,6 +823,7 @@ void parseDecl(Umka *umka) case TOK_CONST: parseConstDecl(umka); break; case TOK_VAR: parseFullVarDecl(umka); break; case TOK_IDENT: parseShortVarDecl(umka); break; + case TOK_EXTERN: parseExternFnDecl(umka); break; case TOK_FN: parseFnDecl(umka); break; case TOK_EOF: if (umka->blocks.top == 0) diff --git a/src/umka_expr.c b/src/umka_expr.c index d95626c01..5814521f1 100644 --- a/src/umka_expr.c +++ b/src/umka_expr.c @@ -19,7 +19,7 @@ void doPushConst(Umka *umka, const Type *type, const Const *constant) { if (type->kind == TYPE_UINT) genPushUIntConst(&umka->gen, constant->uintVal); - else if (typeOrdinal(type) || type->kind == TYPE_FN) + else if (typeOrdinal(type) || type->kind == TYPE_FN || type->kind == TYPE_EXTERNFN) genPushIntConst(&umka->gen, constant->intVal); else if (typeReal(type)) genPushRealConst(&umka->gen, constant->realVal); @@ -1814,6 +1814,7 @@ static void parsePrimary(Umka *umka, const Ident *ident, const Type **type, Cons switch (ident->kind) { case IDENT_CONST: + case IDENT_EXTERN_FN: { if (constant) *constant = ident->constant; @@ -2548,13 +2549,13 @@ static void parseCallSelector(Umka *umka, const Type **type, bool *isVar, bool * // Implicit dereferencing: f^(x) == f(x) doTryImplicitDeref(umka, type); - if ((*type)->kind == TYPE_PTR && ((*type)->base->kind == TYPE_FN || (*type)->base->kind == TYPE_CLOSURE)) + if ((*type)->kind == TYPE_PTR && ((*type)->base->kind == TYPE_FN || (*type)->base->kind == TYPE_EXTERNFN ||(*type)->base->kind == TYPE_CLOSURE)) { genDeref(&umka->gen, (*type)->base->kind); *type = (*type)->base; } - if ((*type)->kind != TYPE_FN && (*type)->kind != TYPE_CLOSURE) + if ((*type)->kind != TYPE_FN && (*type)->kind != TYPE_EXTERNFN && (*type)->kind != TYPE_CLOSURE) umka->error.handler(umka->error.context, "Function or closure expected"); parseCall(umka, type); diff --git a/src/umka_gen.c b/src/umka_gen.c index 4ccc7c5f7..9b4e26cdc 100644 --- a/src/umka_gen.c +++ b/src/umka_gen.c @@ -806,6 +806,12 @@ void genCallExtern(CodeGen *gen, void *entry) genAddInstr(gen, &instr); } +void genCallExternDynamic(CodeGen *gen, DynamicCall *dynamicCall) +{ + const Instruction instr = {.opcode = OP_CALL_EXTERN_DYNAMIC, .tokKind = TOK_NONE, .typeKind = TYPE_NONE, .operand.ptrVal = dynamicCall}; + genAddInstr(gen, &instr); +} + void genCallBuiltin(CodeGen *gen, TypeKind typeKind, BuiltinFunc builtin) { diff --git a/src/umka_gen.h b/src/umka_gen.h index fc98387b9..5c854d3bb 100644 --- a/src/umka_gen.h +++ b/src/umka_gen.h @@ -1,6 +1,8 @@ #ifndef UMKA_GEN_H_INCLUDED #define UMKA_GEN_H_INCLUDED +#include + #include "umka_common.h" #include "umka_vm.h" @@ -42,6 +44,12 @@ typedef struct Error *error; } CodeGen; +typedef struct +{ + void *entry; + ffi_cif cif; +} DynamicCall; + void genInit(CodeGen *gen, Storage *storage, DebugInfo *debug, Error *error); @@ -100,6 +108,7 @@ void genGotoIfNot (CodeGen *gen, int dest); void genCall (CodeGen *gen, int entry); void genCallIndirect (CodeGen *gen, int paramSlots); void genCallExtern (CodeGen *gen, void *entry); +void genCallExternDynamic (CodeGen *gen, DynamicCall *dynamicCall); void genCallBuiltin (CodeGen *gen, TypeKind typeKind, BuiltinFunc builtin); void genCallTypedBuiltin (CodeGen *gen, const Type *type, BuiltinFunc builtin); void genReturn (CodeGen *gen, int paramSlots); diff --git a/src/umka_ident.c b/src/umka_ident.c index b96898887..f44735a14 100644 --- a/src/umka_ident.c +++ b/src/umka_ident.c @@ -260,6 +260,15 @@ Ident *identAddBuiltinFunc(Idents *idents, const Modules *modules, const Blocks return ident; } +Ident *identAddExternFunc(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, Const constant) +{ + Ident *ident = identAdd(idents, modules, blocks, IDENT_EXTERN_FN, name, type, false); + ident->exported = exported; + ident->_extern = true; + ident->constant = constant; + return ident; +} + Ident *identAddModule(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, int moduleVal) { diff --git a/src/umka_ident.h b/src/umka_ident.h index ddcf94c86..1abbcfc75 100644 --- a/src/umka_ident.h +++ b/src/umka_ident.h @@ -12,6 +12,7 @@ typedef enum IDENT_VAR, IDENT_TYPE, IDENT_BUILTIN_FN, + IDENT_EXTERN_FN, IDENT_MODULE } IdentKind; @@ -23,7 +24,7 @@ typedef struct tagIdent unsigned int hash; const Type *type; int module, block; // Place of definition (global identifiers are in block 0) - bool exported, globallyAllocated, used, temporary; + bool exported, globallyAllocated, used, temporary, _extern; int prototypeOffset; // For function prototypes union { @@ -65,6 +66,7 @@ Ident *identAddGlobalVar (Idents *idents, const Modules *modules, const Blocks Ident *identAddLocalVar (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, int offset); Ident *identAddType (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported); Ident *identAddBuiltinFunc(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, BuiltinFunc builtin); +Ident *identAddExternFunc (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, Const constant); Ident *identAddModule (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, int moduleVal); int identAllocStack (Idents *idents, const Types *types, Blocks *blocks, const Type *type); diff --git a/src/umka_lexer.c b/src/umka_lexer.c index 0c7e71273..d91aada2b 100644 --- a/src/umka_lexer.c +++ b/src/umka_lexer.c @@ -37,6 +37,7 @@ static const char *spelling [] = "type", "var", "weak", + "extern", // Operators "+", @@ -102,7 +103,7 @@ static const char *spelling [] = enum { - NUM_KEYWORDS = TOK_WEAK - TOK_BREAK + 1 + NUM_KEYWORDS = TOK_EXTERN - TOK_BREAK + 1 }; diff --git a/src/umka_lexer.h b/src/umka_lexer.h index 373b60c9a..3df9087ce 100644 --- a/src/umka_lexer.h +++ b/src/umka_lexer.h @@ -30,6 +30,7 @@ typedef enum TOK_TYPE, TOK_VAR, TOK_WEAK, + TOK_EXTERN, // Operators TOK_PLUS, diff --git a/src/umka_stmt.c b/src/umka_stmt.c index b603dab4b..ca6fba03e 100644 --- a/src/umka_stmt.c +++ b/src/umka_stmt.c @@ -1,11 +1,18 @@ +#include "umka_common.h" +#include "umka_gen.h" +#include "umka_lexer.h" +#include "umka_types.h" #define __USE_MINGW_ANSI_STDIO 1 #include #include +#include +#include #include "umka_stmt.h" #include "umka_expr.h" #include "umka_decl.h" +#include static void parseStmtList(Umka *umka); @@ -65,6 +72,113 @@ void doZeroVar(Umka *umka, const Ident *ident) } } +ffi_type *mapToFfiType(Umka *umka,const struct tagType *type); + +ffi_type *mapToFfiStruct(Umka *umka, const Type *type) { + const int MAX_STRUCT_FIELDS = 64; + + // todo: free? + ffi_type *ffi_t = malloc(sizeof(ffi_type)); + ffi_t->type = FFI_TYPE_STRUCT; + ffi_t->alignment = type->alignment; + ffi_t->size = type->size; + + if (type->numItems > MAX_STRUCT_FIELDS) { + umka->error.handler(umka->error.context, "Struct passed to dynamic fn cannot have more than %d items", type->numItems); + } + + size_t fieldsSize = sizeof(ffi_type)*(type->numItems+1); + ffi_type **structFields = malloc(fieldsSize); + memset(structFields, 0, fieldsSize); + ffi_t->elements = structFields; + + for (int i = 0; i < type->numItems && i < MAX_STRUCT_FIELDS; i++) { + structFields[i] = mapToFfiType(umka, type->field[i]->type); + } + + return ffi_t; +} + +ffi_type *mapToFfiType(Umka *umka,const struct tagType *type) { + switch (type->kind) { + // int types + case TYPE_INT8: + return &ffi_type_sint8; + case TYPE_INT16: + return &ffi_type_sint16; + case TYPE_INT32: + return &ffi_type_sint32; + case TYPE_INT: + return &ffi_type_sint64; + case TYPE_UINT8: + return &ffi_type_uint8; + case TYPE_UINT16: + return &ffi_type_uint16; + case TYPE_UINT32: + return &ffi_type_uint32; + case TYPE_UINT: + return &ffi_type_uint64; + + // ptr types + case TYPE_STR: + case TYPE_NULL: + case TYPE_ARRAY: + case TYPE_PTR: + return &ffi_type_uint64; + case TYPE_BOOL: + return &ffi_type_uint8; + case TYPE_CHAR: + return &ffi_type_uchar; + + case TYPE_STRUCT: + // this approach creates a new struct, every time a parameter of a struct type appears + // probabbly should be only created once for each struct, then looked up here + return mapToFfiStruct(umka, type->typeIdent->type); + + // float types + case TYPE_REAL32: + return &ffi_type_float; + break; + case TYPE_REAL: + return &ffi_type_double; + break; + + // skip + case TYPE_INTERFACE: + return NULL; + + case TYPE_VOID: + return &ffi_type_void; + + // not supported + case TYPE_WEAKPTR: + case TYPE_DYNARRAY: + case TYPE_MAP: + case TYPE_NONE: + case TYPE_FORWARD: + case TYPE_CLOSURE: + case TYPE_FIBER: + case TYPE_FN: + case TYPE_EXTERNFN: + umka->error.handler(umka->error.context, "Cannot convert type `%s` to ffi_type", type->typeIdent->name); + } + return NULL; +} + +int assignFfiTypes(Umka *umka, ffi_type **types, const Signature *sig) +{ + int numArgs = 0; + for(int i = 0; i < sig->numParams && i < 16; i++) { + const Param *param = sig->param[i]; + ffi_type *type = mapToFfiType(umka, param->type); + + if (strcmp(param->name, "#upvalues") == 0) continue; + if (strcmp(param->name, "#result") == 0) continue; + + types[numArgs++] = type; + } + return numArgs; +} void doResolveExtern(Umka *umka) { @@ -87,9 +201,9 @@ void doResolveExtern(Umka *umka) fn = external->entry; external->resolved = true; - } - else + } else { fn = moduleGetImplLibFunc(umka->modules.module[umka->blocks.module], ident->name); + } if (!fn) umka->error.handler(umka->error.context, "Unresolved prototype of %s", ident->name); @@ -102,7 +216,27 @@ void doResolveExtern(Umka *umka) for (int i = 0; i < ident->type->sig.numParams; i++) identAllocParam(&umka->idents, &umka->types, &umka->modules, &umka->blocks, &ident->type->sig, i); - genCallExtern(&umka->gen, fn); + if (ident->_extern) { + // todo: free? + DynamicCall *dynamicCall = malloc(sizeof(DynamicCall)); + dynamicCall->entry = fn; + + // todo: free? + ffi_type **types = malloc(sizeof(ffi_type)*16); + ffi_type *retType = mapToFfiType(umka, ident->type->sig.resultType); + int numArgs = assignFfiTypes(umka, types, &ident->type->sig); + + if (retType == NULL) + umka->error.handler(umka->error.context, "Unsupported return type for ffi function: %d", ident->type->sig.resultType->typeIdent->name); + + ffi_status status = ffi_prep_cif(&dynamicCall->cif, FFI_DEFAULT_ABI, numArgs, retType, types); + if (status != FFI_OK) + umka->error.handler(umka->error.context, "Error creating ffi_cif: %d", status); + + genCallExternDynamic(&umka->gen, dynamicCall); + } else { + genCallExtern(&umka->gen, fn); + } doGarbageCollection(umka); identWarnIfUnusedAll(&umka->idents, blocksCurrent(&umka->blocks)); @@ -120,7 +254,6 @@ void doResolveExtern(Umka *umka) } } - static bool doShortVarDeclLookahead(Umka *umka) { // ident {"," ident} ":=" diff --git a/src/umka_stmt.h b/src/umka_stmt.h index 23904902f..8f9b2e545 100644 --- a/src/umka_stmt.h +++ b/src/umka_stmt.h @@ -8,6 +8,7 @@ void doGarbageCollectionDownToBlock(Umka *umka, int block); void doZeroVar(Umka *umka, const Ident *ident); void doResolveExtern(Umka *umka); +void* externalLoadFfi(Umka *umka, const Ident* ident); void parseAssignmentStmt(Umka *umka, const Type *type, Const *varPtrConstList); void parseDeclAssignmentStmt(Umka *umka, IdentName *names, const bool *exported, int num, bool constExpr); diff --git a/src/umka_types.c b/src/umka_types.c index dce4c050a..23bd29184 100644 --- a/src/umka_types.c +++ b/src/umka_types.c @@ -165,6 +165,7 @@ static int typeSizeRecompute(const Type *type) } case TYPE_FIBER: return sizeof(void *); case TYPE_FN: return sizeof(int64_t); + case TYPE_EXTERNFN: return sizeof(int64_t); default: return -1; } } @@ -219,6 +220,7 @@ static int typeAlignmentRecompute(const Type *type) } case TYPE_FIBER: return typeSizeRecompute(type); case TYPE_FN: return sizeof(int64_t); + case TYPE_EXTERNFN: return sizeof(int64_t); default: return 0; } } diff --git a/src/umka_types.h b/src/umka_types.h index 8ca22979c..a72fdafbb 100644 --- a/src/umka_types.h +++ b/src/umka_types.h @@ -37,7 +37,8 @@ typedef enum TYPE_INTERFACE, TYPE_CLOSURE, TYPE_FIBER, // Pointer of a special kind - TYPE_FN + TYPE_FN, + TYPE_EXTERNFN } TypeKind; diff --git a/src/umka_vm.c b/src/umka_vm.c index 9ad988d1e..404529e2b 100644 --- a/src/umka_vm.c +++ b/src/umka_vm.c @@ -1,3 +1,8 @@ +#include "umka_api.h" +#include "umka_compiler.h" +#include "umka_gen.h" +#include "umka_ident.h" +#include "umka_types.h" #define __USE_MINGW_ANSI_STDIO 1 //#define UMKA_VM_DEBUG @@ -28,6 +33,8 @@ #include #include +#include + #include "umka_vm.h" @@ -804,6 +811,7 @@ static FORCE_INLINE void doDerefImpl(Slot *slot, TypeKind typeKind, Error *error case TYPE_CLOSURE: break; // Always represented by pointer, not dereferenced case TYPE_FIBER: slot->ptrVal = *(void * *)slot->ptrVal; break; case TYPE_FN: slot->intVal = *(int64_t *)slot->ptrVal; break; + case TYPE_EXTERNFN: slot->intVal = *(int64_t *)slot->ptrVal; break; default: error->runtimeHandler(error->context, ERR_RUNTIME, "Illegal type"); return; } @@ -855,6 +863,7 @@ static FORCE_INLINE void doAssignImpl(void *lhs, Slot rhs, TypeKind typeKind, in } case TYPE_FIBER: *(void * *)lhs = rhs.ptrVal; break; case TYPE_FN: *(int64_t *)lhs = rhs.intVal; break; + case TYPE_EXTERNFN: *(int64_t *)lhs = rhs.intVal; break; default: error->runtimeHandler(error->context, ERR_RUNTIME, "Illegal type"); return; } @@ -1694,6 +1703,7 @@ static int doFillReprBuf(const Slot *slot, const Type *type, char *buf, int maxL case TYPE_FIBER: len += snprintf(buf + len, maxLen, "fiber @ %p", slot->ptrVal); break; case TYPE_FN: len += snprintf(buf + len, maxLen, "fn @ %lld", (long long int)slot->intVal); break; + case TYPE_EXTERNFN: len += snprintf(buf + len, maxLen, "extern fn @ %lld", (long long int)slot->intVal); break; default: break; } @@ -2699,6 +2709,7 @@ static FORCE_INLINE void doBuiltinValid(Fiber *fiber, Error *error) break; } case TYPE_FN: + case TYPE_EXTERNFN: { const int entryOffset = fiber->top->intVal; isValid = entryOffset > 0; @@ -3558,6 +3569,41 @@ static FORCE_INLINE void doCallExtern(Fiber *fiber, Error *error) fiber->ip++; } +static FORCE_INLINE void ffiEntry(UmkaStackSlot *params, UmkaStackSlot *result, const DynamicCall *dynamicCall) +{ + void* args[16] = {0}; + for (unsigned int i = 0; i < dynamicCall->cif.nargs; i++) { + args[i] = &umkaGetParam(params, i)->ptrVal; + } + + UmkaStackSlot *resultSlot = umkaGetResult(params, result); + void* retPtr = resultSlot; + if (dynamicCall->cif.rtype->type == FFI_TYPE_STRUCT) { + retPtr = resultSlot->ptrVal; + } + + ffi_call((ffi_cif *)&dynamicCall->cif, dynamicCall->entry, retPtr, args); + // workaround for real32 -> cast float to double + if (dynamicCall->cif.rtype->type == FFI_TYPE_FLOAT) { + resultSlot->realVal = resultSlot->real32Val; + } + +} + + +static FORCE_INLINE void doCallExternDynamic(Fiber *fiber, Error *error) +{ + const DynamicCall *dynamicCall = fiber->code[fiber->ip].operand.ptrVal; + + fiber->reg[REG_RESULT].ptrVal = error->context; // Upon entry, the result slot stores the Umka instance + + const int ip = fiber->ip; + ffiEntry(&fiber->base[2].apiSlot, &fiber->reg[REG_RESULT].apiSlot, dynamicCall); + fiber->ip = ip; + + fiber->ip++; +} + static FORCE_INLINE void doCallBuiltin(Fiber *fiber, Fiber **newFiber, HeapPages *pages, Error *error) { @@ -3796,6 +3842,7 @@ static void vmLoop(VM *vm) case OP_CALL: doCall(fiber, error); break; case OP_CALL_INDIRECT: doCallIndirect(fiber, error); break; case OP_CALL_EXTERN: doCallExtern(fiber, error); break; + case OP_CALL_EXTERN_DYNAMIC: doCallExternDynamic(fiber, error); break; case OP_CALL_BUILTIN: { Fiber *newFiber = NULL; diff --git a/src/umka_vm.h b/src/umka_vm.h index 94999c9e5..cdeb5bf82 100644 --- a/src/umka_vm.h +++ b/src/umka_vm.h @@ -85,6 +85,7 @@ typedef enum OP_CALL, OP_CALL_INDIRECT, OP_CALL_EXTERN, + OP_CALL_EXTERN_DYNAMIC, OP_CALL_BUILTIN, OP_RETURN, OP_ENTER_FRAME, From 34d739ad9e78f360adeb0ae7bf47423b08a8eb3a Mon Sep 17 00:00:00 2001 From: Michael Noser Date: Fri, 28 Nov 2025 02:26:49 +0100 Subject: [PATCH 2/5] Added UMKA_FFI feature flag & refactored ffi implementation Use umka `Storage` for allocating types Cache ffi_type structs for reuse --- .clangd | 2 +- Makefile | 2 +- src/umka_common.h | 1 - src/umka_compiler.c | 2 +- src/umka_decl.c | 221 +++++++++++++++++++++++--------------------- src/umka_expr.c | 10 +- src/umka_ffi.c | 138 +++++++++++++++++++++++++++ src/umka_ffi.h | 49 ++++++++++ src/umka_gen.c | 15 ++- src/umka_gen.h | 15 ++- src/umka_ident.c | 7 +- src/umka_ident.h | 13 ++- src/umka_lexer.c | 6 +- src/umka_lexer.h | 4 +- src/umka_stmt.c | 133 +++----------------------- src/umka_stmt.h | 1 - src/umka_types.c | 2 - src/umka_types.h | 1 - src/umka_vm.c | 52 +++++------ src/umka_vm.h | 4 +- 20 files changed, 388 insertions(+), 290 deletions(-) create mode 100644 src/umka_ffi.c create mode 100644 src/umka_ffi.h diff --git a/.clangd b/.clangd index 28e9d9a87..b8e698ade 100644 --- a/.clangd +++ b/.clangd @@ -1,4 +1,4 @@ CompileFlags: - Add: [-DUMKA_BUILD -DUMKA_EXT_LIBS -I../libffi/build/include] + Add: [-DUMKA_BUILD -DUMKA_EXT_LIBS -DUMKA_FFI -I../libffi/build/include] InlayHints: Enabled: No \ No newline at end of file diff --git a/Makefile b/Makefile index 234de2b13..e48cf1d03 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ UMKA_LIB_DYNAMIC = $(BUILD_PATH)/libumka.$(LIBEXT) UMKA_EXE = $(BUILD_PATH)/umka #CFLAGS = -s -fPIC -O3 -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS -CFLAGS = -fPIC -ggdb -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS $(LIBS) $(INCLUDE) +CFLAGS = -fPIC -ggdb -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS -DUMKA_FFI $(LIBS) $(INCLUDE) STATIC_CFLAGS = $(CFLAGS) -DUMKA_STATIC DYNAMIC_CFLAGS = $(CFLAGS) -DUMKA_BUILD $(DYNAMIC_CFLAGS_EXTRA) diff --git a/src/umka_common.h b/src/umka_common.h index b09265053..1a979b502 100644 --- a/src/umka_common.h +++ b/src/umka_common.h @@ -210,7 +210,6 @@ int moduleAdd (Modules *modules, const char *path); const ModuleSource *moduleFindSource(const Modules *modules, const char *path); void moduleAddSource (Modules *modules, const char *path, const char *source, bool trusted); void *moduleGetImplLibFunc (const Module *module, const char *name); -void *getExternalDynamicFunc (const char* name); char *moduleCurFolder (char *buf, int size); bool modulePathIsAbsolute (const char *path); bool moduleRegularizePath (const Modules *modules, const char *path, const char *curFolder, char *regularizedPath, int size); diff --git a/src/umka_compiler.c b/src/umka_compiler.c index 486346275..23b8c7875 100644 --- a/src/umka_compiler.c +++ b/src/umka_compiler.c @@ -384,7 +384,7 @@ bool compilerGetFunc(Umka *umka, const char *moduleName, const char *funcName, U } const Ident *fnIdent = identFind(&umka->idents, &umka->modules, &umka->blocks, module, funcName, NULL, false); - if (!fnIdent || fnIdent->kind != IDENT_CONST || fnIdent->type->kind != TYPE_FN || fnIdent->kind != IDENT_EXTERN_FN) + if (!fnIdent || fnIdent->kind != IDENT_CONST || fnIdent->type->kind != TYPE_FN) return false; identSetUsed(fnIdent); diff --git a/src/umka_decl.c b/src/umka_decl.c index d74776337..087ea0f32 100644 --- a/src/umka_decl.c +++ b/src/umka_decl.c @@ -1,4 +1,3 @@ -#include "umka_lexer.h" #define __USE_MINGW_ANSI_STDIO 1 #include @@ -10,6 +9,9 @@ #include "umka_stmt.h" #include "umka_decl.h" +#ifdef UMKA_FFI +#include "umka_ffi.h" +#endif // UMKA_FFI static int parseModule(Umka *umka); @@ -743,15 +745,16 @@ void parseShortVarDecl(Umka *umka) } -// fnDecl = "fn" [rcvSignature] ident exportMark signature [block]. +#ifdef UMKA_FFI +// fnDecl = "extern fn" [rcvSignature] ident exportMark signature. static void parseExternFnDecl(Umka *umka) { if (umka->blocks.top != 0) umka->error.handler(umka->error.context, "Extern functions can only be declared at top-level"); - lexEat(&umka->lex, TOK_EXTERN); + lexEat(&umka->lex, TOK_FFI); lexEat(&umka->lex, TOK_FN); - Type *fnType = typeAdd(&umka->types, &umka->blocks, TYPE_EXTERNFN); + Type *fnType = typeAdd(&umka->types, &umka->blocks, TYPE_FN); if (umka->lex.tok.kind == TOK_LPAR) parseRcvSignature(umka, &fnType->sig); @@ -773,6 +776,8 @@ static void parseExternFnDecl(Umka *umka) else parseFnPrototype(umka, fn); } +#endif // UMKA_FFI + // fnDecl = "fn" [rcvSignature] ident exportMark signature [block]. static void parseFnDecl(Umka *umka) @@ -823,168 +828,170 @@ void parseDecl(Umka *umka) case TOK_CONST: parseConstDecl(umka); break; case TOK_VAR: parseFullVarDecl(umka); break; case TOK_IDENT: parseShortVarDecl(umka); break; - case TOK_EXTERN: parseExternFnDecl(umka); break; +#ifdef UMKA_FFI + case TOK_FFI: parseExternFnDecl(umka); break; +#endif // UMKA_FFI case TOK_FN: parseFnDecl(umka); break; case TOK_EOF: if (umka->blocks.top == 0) - break; + break; - default: umka->error.handler(umka->error.context, "Declaration expected but %s found", lexSpelling(umka->lex.tok.kind)); break; - } + default: umka->error.handler(umka->error.context, "Declaration expected but %s found", lexSpelling(umka->lex.tok.kind)); break; +} } // decls = decl {";" decl}. static void parseDecls(Umka *umka) { - while (1) - { - parseDecl(umka); - if (umka->lex.tok.kind == TOK_EOF) - break; - lexEat(&umka->lex, TOK_SEMICOLON); - } +while (1) +{ + parseDecl(umka); + if (umka->lex.tok.kind == TOK_EOF) + break; + lexEat(&umka->lex, TOK_SEMICOLON); +} } // importItem = [ident "="] stringLiteral. static void parseImportItem(Umka *umka) { - char *alias = NULL; - if (umka->lex.tok.kind == TOK_IDENT) - { - alias = storageAdd(&umka->storage, DEFAULT_STR_LEN + 1); - strcpy(alias, umka->lex.tok.name); - lexNext(&umka->lex); - lexEat(&umka->lex, TOK_EQ); - } +char *alias = NULL; +if (umka->lex.tok.kind == TOK_IDENT) +{ + alias = storageAdd(&umka->storage, DEFAULT_STR_LEN + 1); + strcpy(alias, umka->lex.tok.name); + lexNext(&umka->lex); + lexEat(&umka->lex, TOK_EQ); +} - lexCheck(&umka->lex, TOK_STRLITERAL); +lexCheck(&umka->lex, TOK_STRLITERAL); - char path[DEFAULT_STR_LEN + 1] = ""; +char path[DEFAULT_STR_LEN + 1] = ""; - // Module source strings, if any, have precedence over files - const char *sourceString = NULL; - bool sourceTrusted = false; +// Module source strings, if any, have precedence over files +const char *sourceString = NULL; +bool sourceTrusted = false; - if (moduleRegularizePath(&umka->modules, umka->lex.tok.strVal, umka->modules.curFolder, path, DEFAULT_STR_LEN + 1)) +if (moduleRegularizePath(&umka->modules, umka->lex.tok.strVal, umka->modules.curFolder, path, DEFAULT_STR_LEN + 1)) +{ + const ModuleSource *sourceDesc = moduleFindSource(&umka->modules, path); + if (sourceDesc) { - const ModuleSource *sourceDesc = moduleFindSource(&umka->modules, path); - if (sourceDesc) - { - sourceString = sourceDesc->source; - sourceTrusted = sourceDesc->trusted; - } + sourceString = sourceDesc->source; + sourceTrusted = sourceDesc->trusted; } +} - if (!sourceString) - moduleAssertRegularizePath(&umka->modules, umka->lex.tok.strVal, umka->modules.module[umka->blocks.module]->folder, path, DEFAULT_STR_LEN + 1); +if (!sourceString) + moduleAssertRegularizePath(&umka->modules, umka->lex.tok.strVal, umka->modules.module[umka->blocks.module]->folder, path, DEFAULT_STR_LEN + 1); - char folder[DEFAULT_STR_LEN + 1] = ""; - char name [DEFAULT_STR_LEN + 1] = ""; +char folder[DEFAULT_STR_LEN + 1] = ""; +char name [DEFAULT_STR_LEN + 1] = ""; - moduleNameFromPath(&umka->modules, path, folder, name, DEFAULT_STR_LEN + 1); +moduleNameFromPath(&umka->modules, path, folder, name, DEFAULT_STR_LEN + 1); - if (!alias) - { - alias = storageAdd(&umka->storage, DEFAULT_STR_LEN + 1); - strcpy(alias, name); - } +if (!alias) +{ + alias = storageAdd(&umka->storage, DEFAULT_STR_LEN + 1); + strcpy(alias, name); +} - if (moduleFindImported(&umka->modules, &umka->blocks, alias) >= 0) - umka->modules.error->handler(umka->modules.error->context, "Duplicate imported module %s", alias); +if (moduleFindImported(&umka->modules, &umka->blocks, alias) >= 0) + umka->modules.error->handler(umka->modules.error->context, "Duplicate imported module %s", alias); - int importedModule = moduleFind(&umka->modules, path); - if (importedModule < 0) - { - // Save context - int currentModule = umka->blocks.module; - DebugInfo currentDebug = umka->debug; - Lexer currentLex = umka->lex; - lexInit(&umka->lex, &umka->storage, &umka->debug, path, sourceString, sourceTrusted, &umka->error); +int importedModule = moduleFind(&umka->modules, path); +if (importedModule < 0) +{ + // Save context + int currentModule = umka->blocks.module; + DebugInfo currentDebug = umka->debug; + Lexer currentLex = umka->lex; + lexInit(&umka->lex, &umka->storage, &umka->debug, path, sourceString, sourceTrusted, &umka->error); - lexNext(&umka->lex); - importedModule = parseModule(umka); + lexNext(&umka->lex); + importedModule = parseModule(umka); - // Restore context - lexFree(&umka->lex); - umka->lex = currentLex; - umka->debug = currentDebug; - umka->blocks.module = currentModule; - } + // Restore context + lexFree(&umka->lex); + umka->lex = currentLex; + umka->debug = currentDebug; + umka->blocks.module = currentModule; +} - // Imported module is registered but its body has not been compiled yet - this is only possible if it's imported in a cycle - if (!umka->modules.module[importedModule]->isCompiled) - umka->modules.error->handler(umka->modules.error->context, "Cyclic import of module %s", alias); +// Imported module is registered but its body has not been compiled yet - this is only possible if it's imported in a cycle +if (!umka->modules.module[importedModule]->isCompiled) + umka->modules.error->handler(umka->modules.error->context, "Cyclic import of module %s", alias); - // Module is imported iff it has an import alias (which may coincide with the module name if not specified explicitly) - char **importAlias = &umka->modules.module[umka->blocks.module]->importAlias[importedModule]; - if (*importAlias) - umka->modules.error->handler(umka->modules.error->context, "Duplicate imported module %s", path); - *importAlias = alias; +// Module is imported iff it has an import alias (which may coincide with the module name if not specified explicitly) +char **importAlias = &umka->modules.module[umka->blocks.module]->importAlias[importedModule]; +if (*importAlias) + umka->modules.error->handler(umka->modules.error->context, "Duplicate imported module %s", path); +*importAlias = alias; - identAddModule(&umka->idents, &umka->modules, &umka->blocks, alias, umka->voidType, importedModule); +identAddModule(&umka->idents, &umka->modules, &umka->blocks, alias, umka->voidType, importedModule); - lexNext(&umka->lex); +lexNext(&umka->lex); } // import = "import" (importItem | "(" {importItem ";"} ")"). static void parseImport(Umka *umka) { - lexEat(&umka->lex, TOK_IMPORT); +lexEat(&umka->lex, TOK_IMPORT); - if (umka->lex.tok.kind == TOK_LPAR) +if (umka->lex.tok.kind == TOK_LPAR) +{ + lexNext(&umka->lex); + while (umka->lex.tok.kind == TOK_STRLITERAL || umka->lex.tok.kind == TOK_IDENT) { - lexNext(&umka->lex); - while (umka->lex.tok.kind == TOK_STRLITERAL || umka->lex.tok.kind == TOK_IDENT) - { - parseImportItem(umka); - lexEat(&umka->lex, TOK_SEMICOLON); - } - lexEat(&umka->lex, TOK_RPAR); - } - else parseImportItem(umka); + lexEat(&umka->lex, TOK_SEMICOLON); + } + lexEat(&umka->lex, TOK_RPAR); +} +else + parseImportItem(umka); } // module = [import ";"] decls. static int parseModule(Umka *umka) { - umka->blocks.module = moduleAdd(&umka->modules, umka->lex.fileName); +umka->blocks.module = moduleAdd(&umka->modules, umka->lex.fileName); - if (umka->lex.tok.kind == TOK_IMPORT) - { - parseImport(umka); - lexEat(&umka->lex, TOK_SEMICOLON); - } - parseDecls(umka); - doResolveExtern(umka); +if (umka->lex.tok.kind == TOK_IMPORT) +{ + parseImport(umka); + lexEat(&umka->lex, TOK_SEMICOLON); +} +parseDecls(umka); +doResolveExtern(umka); - umka->modules.module[umka->blocks.module]->isCompiled = true; - return umka->blocks.module; +umka->modules.module[umka->blocks.module]->isCompiled = true; +return umka->blocks.module; } // program = module. void parseProgram(Umka *umka) { - genNop(&umka->gen); // Cleanup code jump stub +genNop(&umka->gen); // Cleanup code jump stub - lexNext(&umka->lex); - const int mainModule = parseModule(umka); +lexNext(&umka->lex); +const int mainModule = parseModule(umka); - const Ident *mainIdent = identFind(&umka->idents, &umka->modules, &umka->blocks, mainModule, "main", NULL, false); - if (mainIdent) - { - if (!identIsMain(mainIdent)) - umka->error.handler(umka->error.context, "Identifier main must be fn main()"); +const Ident *mainIdent = identFind(&umka->idents, &umka->modules, &umka->blocks, mainModule, "main", NULL, false); +if (mainIdent) +{ + if (!identIsMain(mainIdent)) + umka->error.handler(umka->error.context, "Identifier main must be fn main()"); - compilerMakeFuncContext(umka, mainIdent->type, mainIdent->offset, &umka->mainFn); - } + compilerMakeFuncContext(umka, mainIdent->type, mainIdent->offset, &umka->mainFn); +} - genEntryPoint(&umka->gen, JUMP_TO_CLEANUP); // Cleanup code jump - doGarbageCollection(umka); - genHalt(&umka->gen); +genEntryPoint(&umka->gen, JUMP_TO_CLEANUP); // Cleanup code jump +doGarbageCollection(umka); +genHalt(&umka->gen); } diff --git a/src/umka_expr.c b/src/umka_expr.c index 5814521f1..62c5907d1 100644 --- a/src/umka_expr.c +++ b/src/umka_expr.c @@ -19,7 +19,7 @@ void doPushConst(Umka *umka, const Type *type, const Const *constant) { if (type->kind == TYPE_UINT) genPushUIntConst(&umka->gen, constant->uintVal); - else if (typeOrdinal(type) || type->kind == TYPE_FN || type->kind == TYPE_EXTERNFN) + else if (typeOrdinal(type) || type->kind == TYPE_FN) genPushIntConst(&umka->gen, constant->intVal); else if (typeReal(type)) genPushRealConst(&umka->gen, constant->realVal); @@ -1814,7 +1814,9 @@ static void parsePrimary(Umka *umka, const Ident *ident, const Type **type, Cons switch (ident->kind) { case IDENT_CONST: - case IDENT_EXTERN_FN: +#ifdef UMKA_FFI + case IDENT_FFI_FN: +#endif { if (constant) *constant = ident->constant; @@ -2549,13 +2551,13 @@ static void parseCallSelector(Umka *umka, const Type **type, bool *isVar, bool * // Implicit dereferencing: f^(x) == f(x) doTryImplicitDeref(umka, type); - if ((*type)->kind == TYPE_PTR && ((*type)->base->kind == TYPE_FN || (*type)->base->kind == TYPE_EXTERNFN ||(*type)->base->kind == TYPE_CLOSURE)) + if ((*type)->kind == TYPE_PTR && ((*type)->base->kind == TYPE_FN ||(*type)->base->kind == TYPE_CLOSURE)) { genDeref(&umka->gen, (*type)->base->kind); *type = (*type)->base; } - if ((*type)->kind != TYPE_FN && (*type)->kind != TYPE_EXTERNFN && (*type)->kind != TYPE_CLOSURE) + if ((*type)->kind != TYPE_FN && (*type)->kind != TYPE_CLOSURE) umka->error.handler(umka->error.context, "Function or closure expected"); parseCall(umka, type); diff --git a/src/umka_ffi.c b/src/umka_ffi.c new file mode 100644 index 000000000..d6eba710f --- /dev/null +++ b/src/umka_ffi.c @@ -0,0 +1,138 @@ +#include "umka_ffi.h" +#include "umka_common.h" +#include "umka_compiler.h" + + +static FfiStructs ffiStructs = {0}; + +ffi_type* appendFfiStructs(Umka *umka, const Type *type) { + if (ffiStructs.size >= ffiStructs.capacity) { + if (ffiStructs.capacity == 0) { + ffiStructs.capacity = sizeof(FfiStructs) * 16; + ffiStructs.items = storageAdd(&umka->storage, ffiStructs.capacity); + } else { + storageRealloc(&umka->storage, ffiStructs.items, ffiStructs.capacity*2); + } + } + + ffi_type *struct_type = storageAdd(&umka->storage, sizeof(ffi_type)); + struct_type->type = FFI_TYPE_STRUCT; + struct_type->alignment = type->alignment; + struct_type->size = type->size; + + ffiStructs.items[ffiStructs.size++] = (FfiStruct){ .hash = type->typeIdent->hash, .type = struct_type }; + return struct_type; +} + +ffi_type* findFfiStruct(int hash) { + for (int i = 0; i < ffiStructs.size; i++) + if (ffiStructs.items[i].hash == hash) return ffiStructs.items[i].type; + return NULL; +} + +ffi_type *mapToFfiStruct(Umka *umka, const Type *type) { + ffi_type *struct_type = findFfiStruct(type->typeIdent->hash); + if (struct_type != NULL) + return struct_type; + + struct_type = appendFfiStructs(umka, type); + if (type->numItems > MAX_STRUCT_FIELDS) { + // todo make this a flag? + umka->error.handler(umka->error.context, + "Structs passed to dynamic fn cannot have more than " + "%d direct members.\n" + "You can increase this by setting `MAX_STRUCT_FIELDS` compiler definition." + , MAX_STRUCT_FIELDS); + } + + size_t fieldsSize = sizeof(ffi_type)*(type->numItems+1); // +1 for null termination + ffi_type **structFields = storageAdd(&umka->storage, fieldsSize); + memset(structFields, 0, fieldsSize); + struct_type->elements = structFields; + + for (int i = 0; i < type->numItems && i < MAX_STRUCT_FIELDS; i++) { + structFields[i] = mapToFfiType(umka, type->field[i]->type); + } + + return struct_type; +} + +ffi_type *mapToFfiType(Umka *umka,const struct tagType *type) { + switch (type->kind) { + // int types + case TYPE_INT8: + return &ffi_type_sint8; + case TYPE_INT16: + return &ffi_type_sint16; + case TYPE_INT32: + return &ffi_type_sint32; + case TYPE_INT: + return &ffi_type_sint64; + case TYPE_UINT8: + return &ffi_type_uint8; + case TYPE_UINT16: + return &ffi_type_uint16; + case TYPE_UINT32: + return &ffi_type_uint32; + case TYPE_UINT: + return &ffi_type_uint64; + + // ptr types + case TYPE_STR: + case TYPE_NULL: + case TYPE_ARRAY: + case TYPE_PTR: + return &ffi_type_uint64; + case TYPE_BOOL: + return &ffi_type_uint8; + case TYPE_CHAR: + return &ffi_type_uchar; + + case TYPE_STRUCT: + // this approach creates a new struct, every time a parameter of a struct type appears + // probabbly should be only created once for each struct, then looked up here + return mapToFfiStruct(umka, type->typeIdent->type); + + // float types + case TYPE_REAL32: + return &ffi_type_float; + break; + case TYPE_REAL: + return &ffi_type_double; + break; + + // skip + case TYPE_INTERFACE: + return NULL; + + case TYPE_VOID: + return &ffi_type_void; + + // not supported + case TYPE_WEAKPTR: + case TYPE_DYNARRAY: + case TYPE_MAP: + case TYPE_NONE: + case TYPE_FORWARD: + case TYPE_CLOSURE: + case TYPE_FIBER: + case TYPE_FN: + umka->error.handler(umka->error.context, "Cannot convert type `%s` to ffi_type", type->typeIdent->name); + } + return NULL; +} + +int assignFfiTypes(Umka *umka, ffi_type **types, const Signature *sig) +{ + int numArgs = 0; + for(int i = 0; i < sig->numParams && i < 16; i++) { + const Param *param = sig->param[i]; + ffi_type *type = mapToFfiType(umka, param->type); + + if (strcmp(param->name, "#upvalues") == 0) continue; + if (strcmp(param->name, "#result") == 0) continue; + + types[numArgs++] = type; + } + return numArgs; +} diff --git a/src/umka_ffi.h b/src/umka_ffi.h new file mode 100644 index 000000000..339aec5b0 --- /dev/null +++ b/src/umka_ffi.h @@ -0,0 +1,49 @@ +#ifndef UMKA_FFI_H_ +#define UMKA_FFI_H_ + +#include + +#include "umka_common.h" +#include "umka_types.h" + +#ifndef MAX_STRUCT_FIELDS +#define MAX_STRUCT_FIELDS (64) +#endif + +#ifdef UMKA_VM_DEBUG + #define FORCE_INLINE + #define UNLIKELY(x) (x) +#else + #ifdef _MSC_VER // MSVC++ only + #define FORCE_INLINE __forceinline + #define UNLIKELY(x) (x) + #else + #define FORCE_INLINE __attribute__((always_inline)) inline + #define UNLIKELY(x) __builtin_expect(!!(x), 0) + #endif +#endif + + +typedef struct { + int hash; + ffi_type *type; +} FfiStruct; + + +typedef struct { + FfiStruct* items; + size_t size; + size_t capacity; +} FfiStructs; + +typedef struct +{ + void *entry; + ffi_cif cif; +} DynamicCall; + + +ffi_type* mapToFfiType (Umka *umka,const struct tagType *type); +int assignFfiTypes (Umka *umka, ffi_type **types, const Signature *sig); + +#endif // UMKA_FFI_H_ diff --git a/src/umka_gen.c b/src/umka_gen.c index 9b4e26cdc..99fc54cb6 100644 --- a/src/umka_gen.c +++ b/src/umka_gen.c @@ -6,6 +6,10 @@ #include "umka_gen.h" #include "umka_const.h" +#ifdef UMKA_FFI +#include "umka_ffi.h" +#endif // UMKA_FFI + // Common functions @@ -800,15 +804,18 @@ void genCallIndirect(CodeGen *gen, int paramSlots) } -void genCallExtern(CodeGen *gen, void *entry) +#ifdef UMKA_FFI +void genCallExternFfi(CodeGen *gen, DynamicCall *dynamicCall) { - const Instruction instr = {.opcode = OP_CALL_EXTERN, .tokKind = TOK_NONE, .typeKind = TYPE_NONE, .operand.ptrVal = entry}; + const Instruction instr = {.opcode = OP_CALL_EXTERN_FFI, .tokKind = TOK_NONE, .typeKind = TYPE_NONE, .operand.ptrVal = dynamicCall}; genAddInstr(gen, &instr); } +#endif // UMKA_FFI -void genCallExternDynamic(CodeGen *gen, DynamicCall *dynamicCall) + +void genCallExtern(CodeGen *gen, void *entry) { - const Instruction instr = {.opcode = OP_CALL_EXTERN_DYNAMIC, .tokKind = TOK_NONE, .typeKind = TYPE_NONE, .operand.ptrVal = dynamicCall}; + const Instruction instr = {.opcode = OP_CALL_EXTERN, .tokKind = TOK_NONE, .typeKind = TYPE_NONE, .operand.ptrVal = entry}; genAddInstr(gen, &instr); } diff --git a/src/umka_gen.h b/src/umka_gen.h index 5c854d3bb..e21403687 100644 --- a/src/umka_gen.h +++ b/src/umka_gen.h @@ -1,11 +1,12 @@ #ifndef UMKA_GEN_H_INCLUDED #define UMKA_GEN_H_INCLUDED -#include - #include "umka_common.h" #include "umka_vm.h" +#ifdef UMKA_FFI +#include "umka_ffi.h" +#endif typedef struct { @@ -44,12 +45,6 @@ typedef struct Error *error; } CodeGen; -typedef struct -{ - void *entry; - ffi_cif cif; -} DynamicCall; - void genInit(CodeGen *gen, Storage *storage, DebugInfo *debug, Error *error); @@ -107,8 +102,10 @@ void genGotoIfNot (CodeGen *gen, int dest); void genCall (CodeGen *gen, int entry); void genCallIndirect (CodeGen *gen, int paramSlots); +#ifdef UMKA_FFI +void genCallExternFfi (CodeGen *gen, DynamicCall *dynamicCall); +#endif void genCallExtern (CodeGen *gen, void *entry); -void genCallExternDynamic (CodeGen *gen, DynamicCall *dynamicCall); void genCallBuiltin (CodeGen *gen, TypeKind typeKind, BuiltinFunc builtin); void genCallTypedBuiltin (CodeGen *gen, const Type *type, BuiltinFunc builtin); void genReturn (CodeGen *gen, int paramSlots); diff --git a/src/umka_ident.c b/src/umka_ident.c index f44735a14..34ab0f8e7 100644 --- a/src/umka_ident.c +++ b/src/umka_ident.c @@ -260,14 +260,17 @@ Ident *identAddBuiltinFunc(Idents *idents, const Modules *modules, const Blocks return ident; } + +#ifdef UMKA_FFI Ident *identAddExternFunc(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, Const constant) { - Ident *ident = identAdd(idents, modules, blocks, IDENT_EXTERN_FN, name, type, false); + Ident *ident = identAdd(idents, modules, blocks, IDENT_FFI_FN, name, type, false); ident->exported = exported; - ident->_extern = true; + ident->ffi = true; ident->constant = constant; return ident; } +#endif // UMKA_FFI Ident *identAddModule(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, int moduleVal) diff --git a/src/umka_ident.h b/src/umka_ident.h index 1abbcfc75..8e34647a2 100644 --- a/src/umka_ident.h +++ b/src/umka_ident.h @@ -12,7 +12,9 @@ typedef enum IDENT_VAR, IDENT_TYPE, IDENT_BUILTIN_FN, - IDENT_EXTERN_FN, +#ifdef UMKA_FFI + IDENT_FFI_FN, +#endif IDENT_MODULE } IdentKind; @@ -24,7 +26,10 @@ typedef struct tagIdent unsigned int hash; const Type *type; int module, block; // Place of definition (global identifiers are in block 0) - bool exported, globallyAllocated, used, temporary, _extern; + bool exported, globallyAllocated, used, temporary; +#ifdef UMKA_FFI + bool ffi; +#endif int prototypeOffset; // For function prototypes union { @@ -65,8 +70,10 @@ Ident *identAddTempConst (Idents *idents, const Modules *modules, const Blocks Ident *identAddGlobalVar (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, void *ptr); Ident *identAddLocalVar (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, int offset); Ident *identAddType (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported); +#ifdef UMKA_FFI +Ident *identAddExternFunc(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, Const constant); +#endif Ident *identAddBuiltinFunc(Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, BuiltinFunc builtin); -Ident *identAddExternFunc (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, bool exported, Const constant); Ident *identAddModule (Idents *idents, const Modules *modules, const Blocks *blocks, const char *name, const Type *type, int moduleVal); int identAllocStack (Idents *idents, const Types *types, Blocks *blocks, const Type *type); diff --git a/src/umka_lexer.c b/src/umka_lexer.c index d91aada2b..f7658f9da 100644 --- a/src/umka_lexer.c +++ b/src/umka_lexer.c @@ -36,8 +36,10 @@ static const char *spelling [] = "switch", "type", "var", +#ifdef UMKA_FFI + "ffi", +#endif "weak", - "extern", // Operators "+", @@ -103,7 +105,7 @@ static const char *spelling [] = enum { - NUM_KEYWORDS = TOK_EXTERN - TOK_BREAK + 1 + NUM_KEYWORDS = TOK_WEAK - TOK_BREAK + 1 }; diff --git a/src/umka_lexer.h b/src/umka_lexer.h index 3df9087ce..6bda2c186 100644 --- a/src/umka_lexer.h +++ b/src/umka_lexer.h @@ -29,8 +29,10 @@ typedef enum TOK_SWITCH, TOK_TYPE, TOK_VAR, +#ifdef UMKA_FFI + TOK_FFI, +#endif TOK_WEAK, - TOK_EXTERN, // Operators TOK_PLUS, diff --git a/src/umka_stmt.c b/src/umka_stmt.c index ca6fba03e..920e43152 100644 --- a/src/umka_stmt.c +++ b/src/umka_stmt.c @@ -1,18 +1,14 @@ -#include "umka_common.h" -#include "umka_gen.h" -#include "umka_lexer.h" -#include "umka_types.h" #define __USE_MINGW_ANSI_STDIO 1 -#include #include -#include -#include #include "umka_stmt.h" #include "umka_expr.h" #include "umka_decl.h" -#include + +#ifdef UMKA_FFI +#include "umka_ffi.h" +#endif static void parseStmtList(Umka *umka); @@ -72,113 +68,6 @@ void doZeroVar(Umka *umka, const Ident *ident) } } -ffi_type *mapToFfiType(Umka *umka,const struct tagType *type); - -ffi_type *mapToFfiStruct(Umka *umka, const Type *type) { - const int MAX_STRUCT_FIELDS = 64; - - // todo: free? - ffi_type *ffi_t = malloc(sizeof(ffi_type)); - ffi_t->type = FFI_TYPE_STRUCT; - ffi_t->alignment = type->alignment; - ffi_t->size = type->size; - - if (type->numItems > MAX_STRUCT_FIELDS) { - umka->error.handler(umka->error.context, "Struct passed to dynamic fn cannot have more than %d items", type->numItems); - } - - size_t fieldsSize = sizeof(ffi_type)*(type->numItems+1); - ffi_type **structFields = malloc(fieldsSize); - memset(structFields, 0, fieldsSize); - ffi_t->elements = structFields; - - for (int i = 0; i < type->numItems && i < MAX_STRUCT_FIELDS; i++) { - structFields[i] = mapToFfiType(umka, type->field[i]->type); - } - - return ffi_t; -} - -ffi_type *mapToFfiType(Umka *umka,const struct tagType *type) { - switch (type->kind) { - // int types - case TYPE_INT8: - return &ffi_type_sint8; - case TYPE_INT16: - return &ffi_type_sint16; - case TYPE_INT32: - return &ffi_type_sint32; - case TYPE_INT: - return &ffi_type_sint64; - case TYPE_UINT8: - return &ffi_type_uint8; - case TYPE_UINT16: - return &ffi_type_uint16; - case TYPE_UINT32: - return &ffi_type_uint32; - case TYPE_UINT: - return &ffi_type_uint64; - - // ptr types - case TYPE_STR: - case TYPE_NULL: - case TYPE_ARRAY: - case TYPE_PTR: - return &ffi_type_uint64; - case TYPE_BOOL: - return &ffi_type_uint8; - case TYPE_CHAR: - return &ffi_type_uchar; - - case TYPE_STRUCT: - // this approach creates a new struct, every time a parameter of a struct type appears - // probabbly should be only created once for each struct, then looked up here - return mapToFfiStruct(umka, type->typeIdent->type); - - // float types - case TYPE_REAL32: - return &ffi_type_float; - break; - case TYPE_REAL: - return &ffi_type_double; - break; - - // skip - case TYPE_INTERFACE: - return NULL; - - case TYPE_VOID: - return &ffi_type_void; - - // not supported - case TYPE_WEAKPTR: - case TYPE_DYNARRAY: - case TYPE_MAP: - case TYPE_NONE: - case TYPE_FORWARD: - case TYPE_CLOSURE: - case TYPE_FIBER: - case TYPE_FN: - case TYPE_EXTERNFN: - umka->error.handler(umka->error.context, "Cannot convert type `%s` to ffi_type", type->typeIdent->name); - } - return NULL; -} - -int assignFfiTypes(Umka *umka, ffi_type **types, const Signature *sig) -{ - int numArgs = 0; - for(int i = 0; i < sig->numParams && i < 16; i++) { - const Param *param = sig->param[i]; - ffi_type *type = mapToFfiType(umka, param->type); - - if (strcmp(param->name, "#upvalues") == 0) continue; - if (strcmp(param->name, "#result") == 0) continue; - - types[numArgs++] = type; - } - return numArgs; -} void doResolveExtern(Umka *umka) { @@ -216,13 +105,12 @@ void doResolveExtern(Umka *umka) for (int i = 0; i < ident->type->sig.numParams; i++) identAllocParam(&umka->idents, &umka->types, &umka->modules, &umka->blocks, &ident->type->sig, i); - if (ident->_extern) { - // todo: free? - DynamicCall *dynamicCall = malloc(sizeof(DynamicCall)); +#ifdef UMKA_FFI + if (ident->ffi) { + DynamicCall *dynamicCall = storageAdd(&umka->storage, sizeof(DynamicCall)); dynamicCall->entry = fn; - // todo: free? - ffi_type **types = malloc(sizeof(ffi_type)*16); + ffi_type **types = storageAdd(&umka->storage, sizeof(ffi_type)*16); ffi_type *retType = mapToFfiType(umka, ident->type->sig.resultType); int numArgs = assignFfiTypes(umka, types, &ident->type->sig); @@ -233,10 +121,13 @@ void doResolveExtern(Umka *umka) if (status != FFI_OK) umka->error.handler(umka->error.context, "Error creating ffi_cif: %d", status); - genCallExternDynamic(&umka->gen, dynamicCall); + genCallExternFfi(&umka->gen, dynamicCall); } else { genCallExtern(&umka->gen, fn); } +#else + genCallExtern(&umka->gen, fn); +#endif // UMKA_FFI doGarbageCollection(umka); identWarnIfUnusedAll(&umka->idents, blocksCurrent(&umka->blocks)); diff --git a/src/umka_stmt.h b/src/umka_stmt.h index 8f9b2e545..23904902f 100644 --- a/src/umka_stmt.h +++ b/src/umka_stmt.h @@ -8,7 +8,6 @@ void doGarbageCollectionDownToBlock(Umka *umka, int block); void doZeroVar(Umka *umka, const Ident *ident); void doResolveExtern(Umka *umka); -void* externalLoadFfi(Umka *umka, const Ident* ident); void parseAssignmentStmt(Umka *umka, const Type *type, Const *varPtrConstList); void parseDeclAssignmentStmt(Umka *umka, IdentName *names, const bool *exported, int num, bool constExpr); diff --git a/src/umka_types.c b/src/umka_types.c index 23bd29184..dce4c050a 100644 --- a/src/umka_types.c +++ b/src/umka_types.c @@ -165,7 +165,6 @@ static int typeSizeRecompute(const Type *type) } case TYPE_FIBER: return sizeof(void *); case TYPE_FN: return sizeof(int64_t); - case TYPE_EXTERNFN: return sizeof(int64_t); default: return -1; } } @@ -220,7 +219,6 @@ static int typeAlignmentRecompute(const Type *type) } case TYPE_FIBER: return typeSizeRecompute(type); case TYPE_FN: return sizeof(int64_t); - case TYPE_EXTERNFN: return sizeof(int64_t); default: return 0; } } diff --git a/src/umka_types.h b/src/umka_types.h index a72fdafbb..989259a75 100644 --- a/src/umka_types.h +++ b/src/umka_types.h @@ -38,7 +38,6 @@ typedef enum TYPE_CLOSURE, TYPE_FIBER, // Pointer of a special kind TYPE_FN, - TYPE_EXTERNFN } TypeKind; diff --git a/src/umka_vm.c b/src/umka_vm.c index 404529e2b..56d0b838d 100644 --- a/src/umka_vm.c +++ b/src/umka_vm.c @@ -1,8 +1,3 @@ -#include "umka_api.h" -#include "umka_compiler.h" -#include "umka_gen.h" -#include "umka_ident.h" -#include "umka_types.h" #define __USE_MINGW_ANSI_STDIO 1 //#define UMKA_VM_DEBUG @@ -33,10 +28,12 @@ #include #include -#include - #include "umka_vm.h" +#ifdef UMKA_FFI +#include "umka_ffi.h" +#endif + /* Virtual machine stack layout (64-bit slots): @@ -811,7 +808,6 @@ static FORCE_INLINE void doDerefImpl(Slot *slot, TypeKind typeKind, Error *error case TYPE_CLOSURE: break; // Always represented by pointer, not dereferenced case TYPE_FIBER: slot->ptrVal = *(void * *)slot->ptrVal; break; case TYPE_FN: slot->intVal = *(int64_t *)slot->ptrVal; break; - case TYPE_EXTERNFN: slot->intVal = *(int64_t *)slot->ptrVal; break; default: error->runtimeHandler(error->context, ERR_RUNTIME, "Illegal type"); return; } @@ -863,7 +859,6 @@ static FORCE_INLINE void doAssignImpl(void *lhs, Slot rhs, TypeKind typeKind, in } case TYPE_FIBER: *(void * *)lhs = rhs.ptrVal; break; case TYPE_FN: *(int64_t *)lhs = rhs.intVal; break; - case TYPE_EXTERNFN: *(int64_t *)lhs = rhs.intVal; break; default: error->runtimeHandler(error->context, ERR_RUNTIME, "Illegal type"); return; } @@ -1703,7 +1698,6 @@ static int doFillReprBuf(const Slot *slot, const Type *type, char *buf, int maxL case TYPE_FIBER: len += snprintf(buf + len, maxLen, "fiber @ %p", slot->ptrVal); break; case TYPE_FN: len += snprintf(buf + len, maxLen, "fn @ %lld", (long long int)slot->intVal); break; - case TYPE_EXTERNFN: len += snprintf(buf + len, maxLen, "extern fn @ %lld", (long long int)slot->intVal); break; default: break; } @@ -2709,7 +2703,6 @@ static FORCE_INLINE void doBuiltinValid(Fiber *fiber, Error *error) break; } case TYPE_FN: - case TYPE_EXTERNFN: { const int entryOffset = fiber->top->intVal; isValid = entryOffset > 0; @@ -3556,19 +3549,7 @@ static FORCE_INLINE void doCallIndirect(Fiber *fiber, Error *error) } -static FORCE_INLINE void doCallExtern(Fiber *fiber, Error *error) -{ - const UmkaExternFunc fn = (UmkaExternFunc)fiber->code[fiber->ip].operand.ptrVal; - - fiber->reg[REG_RESULT].ptrVal = error->context; // Upon entry, the result slot stores the Umka instance - - const int ip = fiber->ip; - fn(&fiber->base[2].apiSlot, &fiber->reg[REG_RESULT].apiSlot); // + 2 from base pointer for old base pointer and return address - fiber->ip = ip; - - fiber->ip++; -} - +#ifdef UMKA_FFI static FORCE_INLINE void ffiEntry(UmkaStackSlot *params, UmkaStackSlot *result, const DynamicCall *dynamicCall) { void* args[16] = {0}; @@ -3587,11 +3568,9 @@ static FORCE_INLINE void ffiEntry(UmkaStackSlot *params, UmkaStackSlot *result, if (dynamicCall->cif.rtype->type == FFI_TYPE_FLOAT) { resultSlot->realVal = resultSlot->real32Val; } - } - -static FORCE_INLINE void doCallExternDynamic(Fiber *fiber, Error *error) +static FORCE_INLINE void doCallExternFfi(Fiber *fiber, Error *error) { const DynamicCall *dynamicCall = fiber->code[fiber->ip].operand.ptrVal; @@ -3603,6 +3582,21 @@ static FORCE_INLINE void doCallExternDynamic(Fiber *fiber, Error *error) fiber->ip++; } +#endif // UMKA_FFI + + +static FORCE_INLINE void doCallExtern(Fiber *fiber, Error *error) +{ + const UmkaExternFunc fn = (UmkaExternFunc)fiber->code[fiber->ip].operand.ptrVal; + + fiber->reg[REG_RESULT].ptrVal = error->context; // Upon entry, the result slot stores the Umka instance + + const int ip = fiber->ip; + fn(&fiber->base[2].apiSlot, &fiber->reg[REG_RESULT].apiSlot); // + 2 from base pointer for old base pointer and return address + fiber->ip = ip; + + fiber->ip++; +} static FORCE_INLINE void doCallBuiltin(Fiber *fiber, Fiber **newFiber, HeapPages *pages, Error *error) @@ -3842,7 +3836,9 @@ static void vmLoop(VM *vm) case OP_CALL: doCall(fiber, error); break; case OP_CALL_INDIRECT: doCallIndirect(fiber, error); break; case OP_CALL_EXTERN: doCallExtern(fiber, error); break; - case OP_CALL_EXTERN_DYNAMIC: doCallExternDynamic(fiber, error); break; +#ifdef UMKA_FFI + case OP_CALL_EXTERN_FFI: doCallExternFfi(fiber, error); break; +#endif case OP_CALL_BUILTIN: { Fiber *newFiber = NULL; diff --git a/src/umka_vm.h b/src/umka_vm.h index cdeb5bf82..c577b9c4c 100644 --- a/src/umka_vm.h +++ b/src/umka_vm.h @@ -85,7 +85,9 @@ typedef enum OP_CALL, OP_CALL_INDIRECT, OP_CALL_EXTERN, - OP_CALL_EXTERN_DYNAMIC, +#ifdef UMKA_FFI + OP_CALL_EXTERN_FFI, +#endif OP_CALL_BUILTIN, OP_RETURN, OP_ENTER_FRAME, From 5a4adfd513ff64c814008668aa74b711ab2bcfef Mon Sep 17 00:00:00 2001 From: Michael Noser Date: Fri, 28 Nov 2025 03:15:36 +0100 Subject: [PATCH 3/5] Improve error reporting --- src/umka_ffi.c | 28 ++++++++++++++++++++-------- src/umka_stmt.c | 4 ++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/umka_ffi.c b/src/umka_ffi.c index d6eba710f..a69829e9e 100644 --- a/src/umka_ffi.c +++ b/src/umka_ffi.c @@ -1,6 +1,8 @@ #include "umka_ffi.h" #include "umka_common.h" #include "umka_compiler.h" +#include "umka_types.h" +#include static FfiStructs ffiStructs = {0}; @@ -76,21 +78,28 @@ ffi_type *mapToFfiType(Umka *umka,const struct tagType *type) { return &ffi_type_uint32; case TYPE_UINT: return &ffi_type_uint64; + case TYPE_CHAR: + return &ffi_type_uchar; + case TYPE_BOOL: + switch (sizeof(bool)) { + case 1: + return &ffi_type_uint8; + case 2: + return &ffi_type_uint16; + case 4: + return &ffi_type_uint32; + case 8: + return &ffi_type_uint64; + } // ptr types case TYPE_STR: case TYPE_NULL: case TYPE_ARRAY: case TYPE_PTR: - return &ffi_type_uint64; - case TYPE_BOOL: - return &ffi_type_uint8; - case TYPE_CHAR: - return &ffi_type_uchar; + return &ffi_type_pointer; case TYPE_STRUCT: - // this approach creates a new struct, every time a parameter of a struct type appears - // probabbly should be only created once for each struct, then looked up here return mapToFfiStruct(umka, type->typeIdent->type); // float types @@ -117,7 +126,10 @@ ffi_type *mapToFfiType(Umka *umka,const struct tagType *type) { case TYPE_CLOSURE: case TYPE_FIBER: case TYPE_FN: - umka->error.handler(umka->error.context, "Cannot convert type `%s` to ffi_type", type->typeIdent->name); + umka->error.handler( + umka->error.context, + "Type `%s` is unsupported in ffi function declarations", + typeKindSpelling(type->kind)); } return NULL; } diff --git a/src/umka_stmt.c b/src/umka_stmt.c index 920e43152..b108ddab3 100644 --- a/src/umka_stmt.c +++ b/src/umka_stmt.c @@ -115,11 +115,11 @@ void doResolveExtern(Umka *umka) int numArgs = assignFfiTypes(umka, types, &ident->type->sig); if (retType == NULL) - umka->error.handler(umka->error.context, "Unsupported return type for ffi function: %d", ident->type->sig.resultType->typeIdent->name); + umka->error.handler(umka->error.context, "Unsupported return type %s for ffi function %s", typeKindSpelling(ident->type->kind), ident->name); ffi_status status = ffi_prep_cif(&dynamicCall->cif, FFI_DEFAULT_ABI, numArgs, retType, types); if (status != FFI_OK) - umka->error.handler(umka->error.context, "Error creating ffi_cif: %d", status); + umka->error.handler(umka->error.context, "Error creating ffi_cif for function %s: %d", ident->name, status); genCallExternFfi(&umka->gen, dynamicCall); } else { From ebdae87c62a0af8ab4d2912a43a54137c664430f Mon Sep 17 00:00:00 2001 From: Michael Noser Date: Fri, 28 Nov 2025 21:04:55 +0100 Subject: [PATCH 4/5] Add libffi as submodule & integrate in make file --- .gitmodules | 3 +++ Makefile | 50 ++++++++++++++++++++++++++++++++++++++++---------- libffi | 1 + src/umka_ffi.c | 5 ++++- src/umka_ffi.h | 2 +- 5 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 .gitmodules create mode 160000 libffi diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..51b2466ec --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libffi"] + path = libffi + url = https://github.com/libffi/libffi diff --git a/Makefile b/Makefile index e48cf1d03..7488a24bd 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ BINDIR ?= $(PREFIX)/bin # platform specific settings: ifeq ($(PLATFORM), Linux) - LDFLAGS = -lm -ldl + LDFLAGS = -lm -ldl $(LIBFFI_LD) RANLIB = ar -crs LIBEXT = so DYNAMIC_CFLAGS_EXTRA = -shared -fvisibility=hidden @@ -29,9 +29,20 @@ else ifneq ($(findstring MINGW64_NT,$(PLATFORM)),) DYNAMIC_CFLAGS_EXTRA = -shared -fvisibility=hidden endif -LIBS = -L../libffi/build/.libs \ - -lffi -INCLUDE = -I../libffi/build/include +ifeq ($(UMKA_LIBFFI),) + LIBFFI_LIBS = + LIBFFI_LIB = + LIBFFI_LD = + LIBFFI_STATIC = + LIBFFI_INCLUDE = +else + LIBFFI_LIBS = ./libffi/build/.libs + LIBFFI_LIB = libffi.a + LIBFFI_LD = -L$(LIBFFI_LIBS) -l:$(LIBFFI_LIB) + LIBFFI_STATIC = $(LIBFFI_LIBS)/$(LIBFFI_LIB) + LIBFFI_INCLUDE = ./libffi/build/include + LIBFFI_CFLAGS = -I$(LIBFFI_INCLUDE) -DUMKA_FFI +endif # identical for all platforms: UMKA_LIB_STATIC = $(BUILD_PATH)/libumka.a @@ -39,7 +50,7 @@ UMKA_LIB_DYNAMIC = $(BUILD_PATH)/libumka.$(LIBEXT) UMKA_EXE = $(BUILD_PATH)/umka #CFLAGS = -s -fPIC -O3 -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS -CFLAGS = -fPIC -ggdb -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS -DUMKA_FFI $(LIBS) $(INCLUDE) +CFLAGS = -fPIC -ggdb -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS $(LIBFFI_CFLAGS) STATIC_CFLAGS = $(CFLAGS) -DUMKA_STATIC DYNAMIC_CFLAGS = $(CFLAGS) -DUMKA_BUILD $(DYNAMIC_CFLAGS_EXTRA) @@ -85,29 +96,48 @@ uninstall: @rm -f -- $(DESTDIR)$(INCLUDEDIR)/umka_api.h @echo "Uninstallation complete!" -$(UMKA_LIB_STATIC): $(OBJS_STATIC) +$(UMKA_LIB_STATIC): $(OBJS_STATIC) $(LIBFFI_STATIC) @echo AR $@ @mkdir -p -- $(BUILD_PATH)/include/ @$(RANLIB) $(UMKA_LIB_STATIC) $^ @cp $(APIS) $(BUILD_PATH)/include/ -$(UMKA_LIB_DYNAMIC): $(OBJS_DYNAMIC) +$(UMKA_LIB_DYNAMIC): $(OBJS_DYNAMIC) $(LIBFFI_STATIC) @echo LD $@ @mkdir -p -- $(BUILD_PATH)/include/ @$(CC) $(DYNAMIC_CFLAGS) -o $(UMKA_LIB_DYNAMIC) $^ $(LDFLAGS) @cp $(APIS) $(BUILD_PATH)/include/ -$(UMKA_EXE): $(OBJS_EXE) $(UMKA_LIB_STATIC) +$(UMKA_EXE): $(OBJS_EXE) $(UMKA_LIB_STATIC) $(LIBFFI_STATIC) @echo LD $@ @mkdir -p -- $(dir $@) @$(CC) $(STATIC_CFLAGS) -o $(UMKA_EXE) $^ $(LDFLAGS) -$(OBJ_PATH)/%_static.o: src/%.c +$(OBJ_PATH)/%_static.o: src/%.c $(LIBFFI_INCLUDE) @echo CC $@ @mkdir -p -- $(dir $@) @$(CC) $(STATIC_CFLAGS) -o $@ -c $^ -$(OBJ_PATH)/%_dynamic.o: src/%.c +$(OBJ_PATH)/%_dynamic.o: src/%.c $(LIBFFI_INCLUDE) @echo CC $@ @mkdir -p -- $(dir $@) @$(CC) $(DYNAMIC_CFLAGS) -o $@ -c $^ + +.PHONY: libffi/clean +libffi/clean: + rm -rf libffi/build + +libffi/build/.libs/libffi.a: libffi/build/Makefile + @cd libffi/build && make + +libffi/build/include: libffi/build/Makefile +libffi/build/Makefile: libffi/configure + @mkdir -p -- libffi/build/ + @cd libffi/build && CC=-fPIC ../configure --enable-pic + +libffi/configure: libffi/autogen.sh + @cd libffi && ./autogen.sh + +libffi/autogen.sh: + @git submodule init + @git submodule update diff --git a/libffi b/libffi new file mode 160000 index 000000000..e2eda0cf7 --- /dev/null +++ b/libffi @@ -0,0 +1 @@ +Subproject commit e2eda0cf72a0598b44278cc91860ea402273fa29 diff --git a/src/umka_ffi.c b/src/umka_ffi.c index a69829e9e..722bdd025 100644 --- a/src/umka_ffi.c +++ b/src/umka_ffi.c @@ -1,8 +1,9 @@ +#ifdef UMKA_FFI + #include "umka_ffi.h" #include "umka_common.h" #include "umka_compiler.h" #include "umka_types.h" -#include static FfiStructs ffiStructs = {0}; @@ -148,3 +149,5 @@ int assignFfiTypes(Umka *umka, ffi_type **types, const Signature *sig) } return numArgs; } + +#endif // UMKA_FFI diff --git a/src/umka_ffi.h b/src/umka_ffi.h index 339aec5b0..106eea552 100644 --- a/src/umka_ffi.h +++ b/src/umka_ffi.h @@ -1,7 +1,7 @@ #ifndef UMKA_FFI_H_ #define UMKA_FFI_H_ -#include +#include "ffi.h" #include "umka_common.h" #include "umka_types.h" From 35737355a55405b735e836ecfc7274a04be7f54c Mon Sep 17 00:00:00 2001 From: Michael Noser Date: Fri, 28 Nov 2025 21:36:44 +0100 Subject: [PATCH 5/5] Add workflow for ffi build --- .github/workflows/main-ffi.yml | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/main-ffi.yml diff --git a/.github/workflows/main-ffi.yml b/.github/workflows/main-ffi.yml new file mode 100644 index 000000000..419d10cd8 --- /dev/null +++ b/.github/workflows/main-ffi.yml @@ -0,0 +1,42 @@ +# This is a basic workflow to help you get started with Actions + +name: CI (FFI) + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ macos-latest, ubuntu-latest ] + + # The operating system of the runner the job will run on + runs-on: ${{ matrix.os }} + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4 + with: + submodules: true + + # prepare system for libffi build + - name: install dependencies + run: libffi/.ci/install.sh + + # Build it + - name: build + run: make all + env: + UMKA_LIBFFI: 1