Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcab2a8875 | ||
|
|
7b804b8809 |
@@ -5,13 +5,15 @@ RUNS ?= 7
|
|||||||
WARMUP ?= 2
|
WARMUP ?= 2
|
||||||
MAX_ITER ?= 2000000
|
MAX_ITER ?= 2000000
|
||||||
|
|
||||||
|
current_time = python3 -c "import time; print(time.time())"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release $(COMPARING)
|
||||||
cmake --build build
|
cmake --build build $(COMPARING)
|
||||||
|
|
||||||
debug-c: build
|
debug-c: build
|
||||||
gdb --args ./build/fzc ${FILE}
|
gdb --args ./build/fzc ${FILE}
|
||||||
@@ -22,8 +24,32 @@ debug-vm: build
|
|||||||
test: build
|
test: build
|
||||||
ctest --output-on-failure --test-dir build
|
ctest --output-on-failure --test-dir build
|
||||||
|
|
||||||
# Compile the benchmark once, then measure only VM process execution.
|
test-release: build-release
|
||||||
|
ctest --output-on-failure --test-dir build
|
||||||
|
|
||||||
|
bench: COMPARING=&>/dev/null
|
||||||
bench: build-release
|
bench: build-release
|
||||||
./build/fzc -o build/benchmark.fzb $(BENCH_FILE)
|
@./build/fzc -o build/benchmark.fzb $(BENCH_FILE)
|
||||||
python3 scripts/benchmark.py --runs $(RUNS) --warmup $(WARMUP) \
|
@python3 scripts/benchmark.py --runs $(RUNS) --warmup $(WARMUP) \
|
||||||
--max-iter $(MAX_ITER) --vm ./build/fz --program build/benchmark.fzb
|
--max-iter $(MAX_ITER) --vm ./build/fz --program build/benchmark.fzb
|
||||||
|
|
||||||
|
compare: COMPARING=&>/dev/null
|
||||||
|
compare: build-release
|
||||||
|
@echo "=== Million sums benchmark ==="
|
||||||
|
|
||||||
|
@./build/fzc -o build/bench.fzb $(BENCH_FILE) 1>/dev/null 2>/dev/null
|
||||||
|
|
||||||
|
@ST=$$($(current_time)); \
|
||||||
|
./build/fz --allow-infinite build/bench.fzb 1>/dev/null 2>/dev/null; \
|
||||||
|
ET=$$($(current_time)); \
|
||||||
|
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'FZLang : {delta:.5f} ms')"
|
||||||
|
|
||||||
|
@ST=$$($(current_time)); \
|
||||||
|
./benchmarks/loop-c 1>/dev/null 2>/dev/null; \
|
||||||
|
ET=$$($(current_time)); \
|
||||||
|
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'C : {delta:.5f} ms')"
|
||||||
|
|
||||||
|
@ST=$$($(current_time)); \
|
||||||
|
python3 ./benchmarks/loop.py 1>/dev/null 2>/dev/null; \
|
||||||
|
ET=$$($(current_time)); \
|
||||||
|
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'CPython: {delta:.5f} ms')"
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
+3
-2
@@ -3,6 +3,7 @@ uint i = 0;
|
|||||||
uint sum = 0;
|
uint sum = 0;
|
||||||
|
|
||||||
for (i = 0; i < 1000000; i = i + 1)
|
for (i = 0; i < 1000000; i = i + 1)
|
||||||
{
|
|
||||||
sum = sum + i;
|
sum = sum + i;
|
||||||
}
|
|
||||||
|
printline(sum);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
s = 0
|
||||||
|
for i in range(1_000_000):
|
||||||
|
s += i
|
||||||
|
print(s)
|
||||||
@@ -14,6 +14,8 @@ typedef enum
|
|||||||
FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table
|
FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table
|
||||||
FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table
|
FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table
|
||||||
FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable
|
FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable
|
||||||
|
FZL_OPCODE_LOAD_LOCAL = 0xF10006, // Load local variable
|
||||||
|
FZL_OPCODE_STORE_LOCAL = 0xF10007, // Store local variable
|
||||||
|
|
||||||
FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address
|
FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address
|
||||||
FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero
|
FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ typedef struct
|
|||||||
fzl_node_t *node;
|
fzl_node_t *node;
|
||||||
size_t ip;
|
size_t ip;
|
||||||
|
|
||||||
|
bool slot_set;
|
||||||
|
size_t slot;
|
||||||
|
|
||||||
char *classname;
|
char *classname;
|
||||||
} fzl_sym_t;
|
} fzl_sym_t;
|
||||||
|
|
||||||
@@ -32,6 +35,8 @@ typedef struct
|
|||||||
size_t size;
|
size_t size;
|
||||||
size_t remainder;
|
size_t remainder;
|
||||||
|
|
||||||
|
size_t free_slot;
|
||||||
|
|
||||||
fzl_sym_t *symbols;
|
fzl_sym_t *symbols;
|
||||||
} fzl_symtable_t;
|
} fzl_symtable_t;
|
||||||
|
|
||||||
@@ -46,6 +51,9 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
|
|||||||
fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
|
fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
|
||||||
char *classname);
|
char *classname);
|
||||||
|
|
||||||
|
// Assign a local slot to a variable
|
||||||
|
int fzl_symt_setslot(fzl_symtable_t *symt, char *name);
|
||||||
|
|
||||||
// Get entry from specified symtable
|
// Get entry from specified symtable
|
||||||
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res);
|
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ int op_rm_envtable(fzl_vm_t *vm);
|
|||||||
int op_store(fzl_vm_t *vm);
|
int op_store(fzl_vm_t *vm);
|
||||||
int op_lookup_env(fzl_vm_t *vm);
|
int op_lookup_env(fzl_vm_t *vm);
|
||||||
int op_write(fzl_vm_t *vm);
|
int op_write(fzl_vm_t *vm);
|
||||||
|
int op_load_local(fzl_vm_t *vm);
|
||||||
|
int op_store_local(fzl_vm_t *vm);
|
||||||
|
|
||||||
// --- flow ---
|
// --- flow ---
|
||||||
|
|
||||||
|
|||||||
+35
-18
@@ -855,14 +855,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
|
|
||||||
case FZL_NODETYPE_IDENTIFIER:
|
case FZL_NODETYPE_IDENTIFIER:
|
||||||
{
|
{
|
||||||
size_t n = add_const(
|
|
||||||
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
|
|
||||||
.as.string.chars = node->token->value,
|
|
||||||
.as.string.length = node->token->value_sz});
|
|
||||||
|
|
||||||
fzl_sym_t sym;
|
fzl_sym_t sym;
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
ssize_t i = walker->sym_top - 1;
|
||||||
|
for (; i >= 0; i--)
|
||||||
if ((exists =
|
if ((exists =
|
||||||
fzl_symt_get(walker->syms[i], node->token->value, &sym)))
|
fzl_symt_get(walker->syms[i], node->token->value, &sym)))
|
||||||
break;
|
break;
|
||||||
@@ -876,9 +872,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
}
|
}
|
||||||
|
|
||||||
push_op(walker, FZL_OPCODE_LOADCONST);
|
if (!sym.slot_set)
|
||||||
push_word(walker, n);
|
{
|
||||||
push_op(walker, FZL_OPCODE_LOOKUP_ENV);
|
snprintf(wlast_err, sizeof(wlast_err),
|
||||||
|
"internal error: local '%s' has no slot", sym.name);
|
||||||
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t depth = walker->sym_top - 1 - i;
|
||||||
|
push_op(walker, FZL_OPCODE_LOAD_LOCAL);
|
||||||
|
push_word(walker, depth);
|
||||||
|
push_word(walker, sym.slot);
|
||||||
|
|
||||||
current_res = (fzl_walk_res_t){
|
current_res = (fzl_walk_res_t){
|
||||||
.type = sym.type, .error = false, .classname = sym.classname};
|
.type = sym.type, .error = false, .classname = sym.classname};
|
||||||
@@ -1239,6 +1243,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fzl_symt_setslot(walker->syms[walker->sym_top - 1],
|
||||||
|
node->var.identifier->token->value) < 0)
|
||||||
|
{
|
||||||
|
snprintf(wlast_err, sizeof(wlast_err),
|
||||||
|
"failed to assign a local slot to '%s'",
|
||||||
|
(char *)node->var.identifier->token->value);
|
||||||
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
|
}
|
||||||
|
|
||||||
fzl_token_t *id_tok = node->var.identifier->token;
|
fzl_token_t *id_tok = node->var.identifier->token;
|
||||||
size_t const_n = add_const(
|
size_t const_n = add_const(
|
||||||
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
|
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
|
||||||
@@ -1283,7 +1296,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
{
|
{
|
||||||
fzl_sym_t sym;
|
fzl_sym_t sym;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
ssize_t i = walker->sym_top - 1;
|
||||||
|
for (; i >= 0; i--)
|
||||||
if ((found =
|
if ((found =
|
||||||
fzl_symt_get(walker->syms[i],
|
fzl_symt_get(walker->syms[i],
|
||||||
node->binary_op.left->token->value, &sym)))
|
node->binary_op.left->token->value, &sym)))
|
||||||
@@ -1308,14 +1322,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t const_n = add_const(
|
if (!sym.slot_set)
|
||||||
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
|
{
|
||||||
.as.string.chars = sym.name,
|
snprintf(wlast_err, sizeof(wlast_err),
|
||||||
.as.string.length =
|
"internal error: local '%s' has no slot", sym.name);
|
||||||
node->binary_op.left->token->value_sz});
|
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||||
push_op(walker, FZL_OPCODE_LOADCONST);
|
}
|
||||||
push_word(walker, const_n);
|
|
||||||
push_op(walker, FZL_OPCODE_WRITE);
|
size_t depth = walker->sym_top - 1 - i;
|
||||||
|
push_op(walker, FZL_OPCODE_STORE_LOCAL);
|
||||||
|
push_word(walker, depth);
|
||||||
|
push_word(walker, sym.slot);
|
||||||
}
|
}
|
||||||
else if (node->binary_op.left->type == FZL_NODETYPE_FIELD)
|
else if (node->binary_op.left->type == FZL_NODETYPE_FIELD)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ fzl_symtable_t *fzl_mksymt()
|
|||||||
if (!tbl)
|
if (!tbl)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
tbl->free_slot = 0;
|
||||||
tbl->remainder = 2;
|
tbl->remainder = 2;
|
||||||
tbl->size = 0;
|
tbl->size = 0;
|
||||||
tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t));
|
tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t));
|
||||||
@@ -38,8 +39,13 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
|
|||||||
symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t));
|
symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
symt->symbols[symt->size++] = (fzl_sym_t){
|
symt->symbols[symt->size++] = (fzl_sym_t){.name = name,
|
||||||
.name = name, .node = node, .type = type, .ip = ip, .classname = NULL};
|
.node = node,
|
||||||
|
.type = type,
|
||||||
|
.ip = ip,
|
||||||
|
.classname = NULL,
|
||||||
|
.slot_set = false,
|
||||||
|
.slot = 0};
|
||||||
return FZL_SYMTABLE_RESULT_OK;
|
return FZL_SYMTABLE_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +70,28 @@ fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
|
|||||||
return FZL_SYMTABLE_RESULT_ERR;
|
return FZL_SYMTABLE_RESULT_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fzl_symt_setslot(fzl_symtable_t *symt, char *name)
|
||||||
|
{
|
||||||
|
if (!symt)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < symt->size; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(symt->symbols[i].name, name) == 0)
|
||||||
|
{
|
||||||
|
if (symt->symbols[i].slot_set)
|
||||||
|
return symt->symbols[i].slot;
|
||||||
|
|
||||||
|
symt->symbols[i].slot = symt->free_slot++;
|
||||||
|
symt->symbols[i].slot_set = true;
|
||||||
|
|
||||||
|
return symt->symbols[i].slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res)
|
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res)
|
||||||
{
|
{
|
||||||
if (!symt)
|
if (!symt)
|
||||||
|
|||||||
+17
-1
@@ -1,4 +1,4 @@
|
|||||||
#include "common/fzl_opcodes.h"
|
#include <common/fzl_opcodes.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <vm/fzl_vm.h>
|
#include <vm/fzl_vm.h>
|
||||||
@@ -20,6 +20,22 @@ void fzl_vm_print_ops(fzl_vm_t *vm)
|
|||||||
|
|
||||||
switch (vm->ops[i])
|
switch (vm->ops[i])
|
||||||
{
|
{
|
||||||
|
case FZL_OPCODE_STORE_LOCAL:
|
||||||
|
{
|
||||||
|
size_t depth = vm->ops[++i];
|
||||||
|
size_t slot = vm->ops[++i];
|
||||||
|
printf("STORE_LOCAL D=%zu S=%zu\n", depth, slot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FZL_OPCODE_LOAD_LOCAL:
|
||||||
|
{
|
||||||
|
size_t depth = vm->ops[++i];
|
||||||
|
size_t slot = vm->ops[++i];
|
||||||
|
printf("LOAD_LOCAL D=%zu S=%zu\n", depth, slot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case FZL_OPCODE_BUILD_STR:
|
case FZL_OPCODE_BUILD_STR:
|
||||||
printf("BUILD_STR %zu\n", vm->ops[++i]);
|
printf("BUILD_STR %zu\n", vm->ops[++i]);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
#include <common/fzl_value.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <vm/fzl_vm.h>
|
#include <vm/fzl_vm.h>
|
||||||
|
#include <vm/fzl_vm_env.h>
|
||||||
#include <vm/internals/opcode.h>
|
#include <vm/internals/opcode.h>
|
||||||
|
|
||||||
// Allocate environment table and increment scope level
|
// Allocate environment table and increment scope level
|
||||||
@@ -125,3 +128,61 @@ int op_write(fzl_vm_t *vm)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int op_load_local(fzl_vm_t *vm)
|
||||||
|
{
|
||||||
|
size_t depth = vm->ops[++vm->ip];
|
||||||
|
size_t slot = vm->ops[++vm->ip];
|
||||||
|
|
||||||
|
if (depth >= vm->etop)
|
||||||
|
{
|
||||||
|
snprintf(last_err, sizeof(last_err), "runtime error - local depth out of range");
|
||||||
|
print_err_data(vm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
|
||||||
|
if (env->size <= slot)
|
||||||
|
{
|
||||||
|
snprintf(last_err, sizeof(last_err), "runtime error - local slot out of range");
|
||||||
|
print_err_data(vm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->dstack[vm->dtop++] = env->table[slot].value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_store_local(fzl_vm_t *vm)
|
||||||
|
{
|
||||||
|
if (vm->dtop < 1)
|
||||||
|
{
|
||||||
|
snprintf(last_err, sizeof(last_err),
|
||||||
|
"runtime error - expected value in data stack");
|
||||||
|
print_err_data(vm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fzl_value_t value = vm->dstack[--vm->dtop];
|
||||||
|
|
||||||
|
size_t depth = vm->ops[++vm->ip];
|
||||||
|
size_t slot = vm->ops[++vm->ip];
|
||||||
|
|
||||||
|
if (depth >= vm->etop)
|
||||||
|
{
|
||||||
|
snprintf(last_err, sizeof(last_err), "runtime error - local depth out of range");
|
||||||
|
print_err_data(vm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
|
||||||
|
if (env->size <= slot)
|
||||||
|
{
|
||||||
|
snprintf(last_err, sizeof(last_err), "runtime error - local slot out of range");
|
||||||
|
print_err_data(vm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->table[slot].value = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ const opcode_handler group_stack[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Context-relative opcodes group
|
// Context-relative opcodes group
|
||||||
const opcode_handler group_ctx[] = {
|
const opcode_handler group_ctx[] = {[1] = op_mk_envtable, [2] = op_rm_envtable,
|
||||||
[1] = op_mk_envtable, [2] = op_rm_envtable, [3] = op_store,
|
[3] = op_store, [4] = op_lookup_env,
|
||||||
[4] = op_lookup_env, [5] = op_write,
|
[5] = op_write, [6] = op_load_local,
|
||||||
};
|
[7] = op_store_local};
|
||||||
|
|
||||||
// Flow-relative opcodes group
|
// Flow-relative opcodes group
|
||||||
const opcode_handler group_flow[] = {
|
const opcode_handler group_flow[] = {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Slot indices follow declaration order, not first-use order.
|
||||||
|
uint first = 1;
|
||||||
|
uint second = 2;
|
||||||
|
second = second + 1;
|
||||||
|
first = first + second;
|
||||||
|
if (first != 4)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
// Shadowed locals use distinct slots in distinct environments.
|
||||||
|
uint value = 10;
|
||||||
|
{
|
||||||
|
uint value = 20;
|
||||||
|
value = value + 1;
|
||||||
|
if (value != 21)
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (value != 10)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
// Access an outer local from nested loop/body scopes.
|
||||||
|
uint total = 0;
|
||||||
|
for (uint i = 0; i < 4; i = i + 1)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
uint nested = i;
|
||||||
|
total = total + nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total != 6)
|
||||||
|
exit(1);
|
||||||
Reference in New Issue
Block a user