diff --git a/examples/vm/project.json b/examples/vm/project.json index 588c7d7..00e4594 100644 --- a/examples/vm/project.json +++ b/examples/vm/project.json @@ -8,6 +8,6 @@ "mode": "flbb" }, "dependencies": [ - "vm@1.0.0-x64" + "vm@1.0.1-x64" ] } \ No newline at end of file diff --git a/libs/vm/build.sh b/libs/vm/build.sh index 7de0a2a..e303a8e 100644 --- a/libs/vm/build.sh +++ b/libs/vm/build.sh @@ -1 +1,2 @@ +deno run -A gen.ts gcc native.c -o native.so -fpic --shared \ No newline at end of file diff --git a/libs/vm/gen.ts b/libs/vm/gen.ts new file mode 100644 index 0000000..7f95d7e --- /dev/null +++ b/libs/vm/gen.ts @@ -0,0 +1,47 @@ +import { structGen } from "../../src/tools/structgen.ts"; +import { functionGen, addDatatype, finish } from "../../src/tools/bindgen.ts"; + +const struct = ` +struct vm_instance { + void* code; + + int64_t* stack; + int stack_ptr; + int max_stack; + + int64_t* global_variables; + uint8_t* global_variable_types; + int global_variable_size; + + int64_t spark; +}; +`; + + +const struct_bind = await structGen(struct, "struct vm_instance", [ + "code", + "stack", + "stack_ptr", + "max_stack", + "global_variables", + "global_variable_types", + "global_variable_size", + "spark" +]); + +addDatatype("struct vm_instance*", "int"); +addDatatype("vm_instance*", "int"); +addDatatype("int64_t", "int"); +addDatatype("uint64_t", "int"); + +functionGen(48327, "void stack_push(struct vm_instance* vm, int64_t value);"); +functionGen(48328, "int64_t stack_pop(struct vm_instance* vm);"); +functionGen(48329, "void invoke(struct vm_instance* vm, uint64_t location);"); +functionGen(48330, "struct vm_instance* vm_load(const char* file);"); +functionGen(48331, "void vm_destroy(struct vm_instance* vm);"); + + +const [ fl, c ] = finish(`#include "../../src/flvm/vm.h"\n`); + +Deno.writeTextFileSync("vm.fl", struct_bind + fl); +Deno.writeTextFileSync("native.c", c); diff --git a/libs/vm/native.c b/libs/vm/native.c index 3616c3e..510cb9c 100644 --- a/libs/vm/native.c +++ b/libs/vm/native.c @@ -1,36 +1,29 @@ #include "../../src/flvm/vm.h" - void native_stack_push(struct vm_instance* vm) { int64_t value = (int64_t) stack_pop(vm); struct vm_instance* vm2 = (struct vm_instance*) stack_pop(vm); stack_push(vm2, value); stack_push(vm, 0); } - void native_stack_pop(struct vm_instance* vm) { struct vm_instance* vm2 = (struct vm_instance*) stack_pop(vm); stack_push(vm, stack_pop(vm2)); } - void native_invoke(struct vm_instance* vm) { uint64_t location = (uint64_t) stack_pop(vm); struct vm_instance* vm2 = (struct vm_instance*) stack_pop(vm); invoke(vm2, location); stack_push(vm, 0); } - void native_vm_load(struct vm_instance* vm) { const char* file = (const char*) stack_pop(vm); stack_push(vm, vm_load(file)); } - void native_vm_destroy(struct vm_instance* vm) { struct vm_instance* vm2 = (struct vm_instance*) stack_pop(vm); vm_destroy(vm2); stack_push(vm, 0); } - - void init() { vm_native_register(48327, native_stack_push); vm_native_register(48328, native_stack_pop); diff --git a/libs/vm/project.json b/libs/vm/project.json index 785b5a1..5b7d79e 100644 --- a/libs/vm/project.json +++ b/libs/vm/project.json @@ -1,5 +1,5 @@ { "name": "vm", - "version": "1.0.0-x64", + "version": "1.0.1-x64", "type": "module" } \ No newline at end of file diff --git a/libs/vm/vm.fl b/libs/vm/vm.fl index f0ae2be..573a850 100644 --- a/libs/vm/vm.fl +++ b/libs/vm/vm.fl @@ -1,29 +1,24 @@ -$define struct vm_instance_offset_code 0 -$define struct vm_instance_offset_stack 8 -$define struct vm_instance_offset_stack_ptr 16 -$define struct vm_instance_offset_max_stack 20 -$define struct vm_instance_offset_global_variables 24 -$define struct vm_instance_offset_global_variable_types 32 -$define struct vm_instance_offset_global_variable_size 40 -$define struct vm_instance_offset_spark 48 +$define vm_instance_offset_code 0 +$define vm_instance_offset_stack 8 +$define vm_instance_offset_stack_ptr 16 +$define vm_instance_offset_max_stack 20 +$define vm_instance_offset_global_variables 24 +$define vm_instance_offset_global_variable_types 32 +$define vm_instance_offset_global_variable_size 40 +$define vm_instance_offset_spark 48 $define struct vm_instance_size 56 - function(assembly) stack_push(int vm, int value) -> void { "48327" } - function(assembly) stack_pop(int vm) -> int { "48328" } - function(assembly) invoke(int vm, int location) -> void { "48329" } - function(assembly) vm_load(str file) -> int { "48330" } - function(assembly) vm_destroy(int vm) -> void { "48331" -} \ No newline at end of file +} diff --git a/src/tools/bindgen.ts b/src/tools/bindgen.ts new file mode 100644 index 0000000..3e0a6ee --- /dev/null +++ b/src/tools/bindgen.ts @@ -0,0 +1,160 @@ +function split_c_signature(signature: string) { + let return_type = signature.split("(", 1)[0]; + let function_name; + let attributes; + + if (return_type.split(" ").length == 1) { + function_name = return_type; + return_type = " "; + } else { + const ret_array = return_type.split(" "); + let len = ret_array.length; + + function_name = ret_array[len - 1].trim(); + + ret_array.pop(); + len--; + + return_type = ret_array[len - 1]; + attributes = ""; + + // loop over the reversed array + let is_ret = true; + for (let i = len - 2; i >= 0; i--) { + const _allowed_return_types = ['short', 'long', 'signed', 'unsigned', 'const', 'restrict', 'restrict', 'mutable']; + if (_allowed_return_types.includes(ret_array[i]) && is_ret) { + return_type = ret_array[i] + " " + return_type; + } else { + is_ret = false; + attributes = ret_array[i] + " " + attributes; + } + } + + return_type = return_type.trim(); + attributes = attributes.trim(); + } + + let class_name = ""; + if (function_name.split("::", 1).length != 1) { + class_name = function_name.split("::", 1)[0]; + function_name = function_name.split("::", 1)[1]; + } + + if (function_name.startsWith("*")) { + function_name = function_name.substring(1); + return_type = "*"; + } + + let params = signature.split("(")[1].split(")")[0].split(",").map(p => p.trim()); + if (params[0] == "") { + params = []; + } + + return { + "return_type": return_type, + "class_name": class_name, + "function_name": function_name, + "attributes": attributes, + "params": params, + "signature": signature + }; +} + +const datatype_lookup: { [key: string]: string } = { + "void": "void", + "char": "chr", + "const char*": "str", + "uint32_t": "int", + "bool": "int" +}; + + +const translations: {id: number, translation: string}[] = []; +let outfl = ""; +let outc = "" + +function getDatatype(dt: string) { + if (!datatype_lookup[dt]) { + const newDt = prompt(`No datatype translation found for ${dt}! Translation? `); + datatype_lookup[dt] = newDt as string; + } + return datatype_lookup[dt]; +} + +export function addDatatype(dt: string, translation: string) { + datatype_lookup[dt] = translation; +} + +export function functionGen(id: number, sigStr: string) { + const sig = split_c_signature(sigStr); + + let fl = `function(assembly) ${sig.function_name}(`; + for (const param of sig.params) { + const datatype = param.substring(0, param.lastIndexOf(" ")).trim(); + const name = param.substring(param.lastIndexOf(" ")).trim(); + + + + fl += getDatatype(datatype); + fl += " " + fl += name; + fl += ", "; + } + + if (sig.params.length != 0) fl = fl.substring(0, fl.length - 2); + fl += `) -> ${getDatatype(sig.return_type)} {\n "${id}"\n}`; + + outfl += fl + "\n"; + + let c = `void native_${sig.function_name}(struct vm_instance* vm) {\n`; + for (let i = sig.params.length; i--; i > 0) { + + const datatype = sig.params[i].substring(0, sig.params[i].lastIndexOf(" ")).trim(); + let name = sig.params[i].substring(sig.params[i].lastIndexOf(" ")).trim(); + name = name == "vm" ? "vm2" : name; + + c += ` ${datatype} ${name} = (${datatype}) stack_pop(vm);\n`; + } + + if (sig.return_type != "void") { + c += ` stack_push(vm, ${sig.function_name}(`; + } else { + c += ` ${sig.function_name}(`; + } + + for (const param of sig.params) { + let name = param.substring(param.lastIndexOf(" ")).trim(); + name = name == "vm" ? "vm2" : name; + + c += `${name}, `; + } + + if (sig.params.length != 0) c = c.substring(0, c.length - 2); + + if (sig.return_type != "void") { + c += `));\n}`; + } else { + c += `);\n stack_push(vm, 0);\n}`; + } + + + outc += c + "\n"; + + translations.push({ + id: id, + translation: "native_" + sig.function_name + }); +} + +export function finish(include: string) { + let finalc = include + outc; + + + finalc += "void init() {\n"; + for (const translation of translations) { + finalc += ` vm_native_register(${translation.id}, ${translation.translation});\n`; + } + finalc += "}"; + + return [outfl, finalc]; +} \ No newline at end of file diff --git a/src/tools/structgen.ts b/src/tools/structgen.ts new file mode 100644 index 0000000..c47b6bc --- /dev/null +++ b/src/tools/structgen.ts @@ -0,0 +1,44 @@ +async function runCommand(command: string) { + // console.log("cmd: " + command); + const proc = Deno.run({ + cmd: command.split(" ").filter(v => v != ""), + stderr: "inherit", + stdout: "piped" + }); + + const status = await proc.status(); + const stdout = new TextDecoder().decode(await proc.output()); + proc.close(); + if (!status.success) { + throw new Error("Could not execute: " + command); + } + + return stdout; +} +function genCode(structDefinition: string, structName: string, members: string[]) { + let code = ""; + + code += "#include \n"; + code += "#include \n"; + code += "#include \n"; + + code += structDefinition + "\n"; + + code += "int main() {\n"; + for (const member of members) { + const name = structName.startsWith("struct ") ? structName.replace("struct ", "") : structName; + code += `\tprintf("$define ${name}_offset_${member} %ld\\n", __builtin_offsetof(${structName}, ${member}));\n`; + } + + code += `\tprintf("$define ${structName}_size %ld\\n", sizeof(${structName}));\n`; + + code += "}\n"; + + return code; +} + +export async function structGen(structDefinition: string, structName: string, members: string[]) { + Deno.writeTextFileSync("/tmp/struct.c", genCode(structDefinition, structName, members)); + await runCommand("gcc /tmp/struct.c -o /tmp/struct.elf"); + return await runCommand("/tmp/struct.elf"); +} \ No newline at end of file