Skip to content

Commit

Permalink
* added rpmalloc, much more light-weight alternative to mimalloc
Browse files Browse the repository at this point in the history
* polished the compilation process
* fixed recently introduced bug with non-working dead code elimination
* made a few more little fixes and improvements
  • Loading branch information
vpisarev committed Apr 1, 2021
1 parent cafcc57 commit 8eef9c4
Show file tree
Hide file tree
Showing 67 changed files with 41,762 additions and 52,716 deletions.
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@
*.cmxs
*.cmxa
*.fxp
ficus
ficus0
ficus0.0
config.ml
lexer.ml
parser.ml
parser.mli
parser.output
src/ficus
src/result.pgm
bin/
tools/ocaml2fx/ocaml2fx
tools/ocaml2fx/*.fx

# ocamlbuild working directory
_build/

# ficus build directory
__build__/
__fxbuild__/

# vscode config files etc.
.vscode/
Expand Down
62 changes: 40 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,30 +1,51 @@
MKDIR := mkdir -p
RMDIR := rm -rf
RM := rm -rf
CC := cc -Wno-unknown-warning-option -Wno-dangling-else -O3
CC := cc -Wno-unknown-warning-option -Wno-dangling-else -Wno-static-in-inline -O3
INCLUDE := ./runtime
RUNTIME_DIR := ./runtime/ficus
RUNTIME_DEPS := $(wildcard $(RUNTIME_DIR)/*.h)
RUNTIME_IMPL_DEPS := $(wildcard $(RUNTIME_DIR)/impl/*.h)
BUILD_DIR := __build__
BUILD_DIR := __fxbuild__
BOOTSTRAP_BUILD_DIR := $(BUILD_DIR)/bootstrap
BOOTSTRAP_SRC := ./compiler/bootstrap
BOOTSTRAP_SRCS := $(wildcard $(BOOTSTRAP_SRC)/*.c)
BOOTSTRAP_OBJS := $(patsubst $(BOOTSTRAP_SRC)/%.c,$(BOOTSTRAP_BUILD_DIR)/%.o,$(BOOTSTRAP_SRCS))
FICUS0 := $(BOOTSTRAP_BUILD_DIR)/ficus0
CFLAGS := -I$(INCLUDE)
LDLIBS := -lm
LDFLAGS :=

FICUS_SRC := ./compiler
FICUS_SRCS := $(wildcard $(FICUS_SRC)/*.fx)
FICUS_STDLIB := ./lib
FICUS_STDLIB_SRCS := $(wildcard $(FICUS_STDLIB)/*.fx)
FICUS := ./ficus
FICUS_FLAGS := -verbose -I $(FICUS_STDLIB) -O3 -cflags $(CFLAGS)
FICUS_BIN := ./bin
FICUS := $(FICUS_BIN)/ficus

.PHONY: all clean make_dirs pre_ficus final_note
ifeq ($(OS),Windows_NT)
CFLAGS += -D WIN32
else
UNAME_S := $(shell uname -s)
UNAME_P := $(shell uname -m)
ifeq ($(UNAME_S),Darwin)
ifeq ($(UNAME_P),x86_64)
CFLAGS += -Xclang -fopenmp
LDFLAGS += -Xclang -fopenmp
LDLIBS += -L./runtime/lib/macos_x64/ -lomp
endif
#ifeq ($(UNAME_P),arm64)
# CFLAGS += -Xclang -fopenmp
# LDLIBS += -L./runtime/lib/macos/x64/ -lmimalloc -lomp
#endif
endif
endif

all: make_dirs $(FICUS0) pre_ficus $(FICUS) final_note
FICUS_FLAGS := -verbose -O3

.PHONY: all clean final_note

all: $(FICUS0) $(FICUS) final_note

$(FICUS0): $(BOOTSTRAP_OBJS)
@$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
Expand All @@ -33,34 +54,31 @@ $(BOOTSTRAP_BUILD_DIR)/ficus.o: $(BOOTSTRAP_SRC)/ficus.c $(RUNTIME_DEPS) $(RUNTI
@echo "CC ficus.c"
@$(CC) $(CFLAGS) -c $< -o $@

$(BOOTSTRAP_BUILD_DIR)/%.o: $(BOOTSTRAP_SRC)/%.c
$(BOOTSTRAP_BUILD_DIR)/%.o: $(BOOTSTRAP_SRC)/%.c | $(BOOTSTRAP_BUILD_DIR)
@echo "CC $<"
@$(CC) $(CFLAGS) -c $< -o $@

$(FICUS): $(FICUS_SRCS) $(FICUS_STDLIB_SRCS)
@$(RM) __build__/ficus/ficus
$(FICUS): $(FICUS0) $(FICUS_SRCS) $(FICUS_STDLIB_SRCS)
@echo "\033[34;1mBuilding fresh compiler from the actual ficus sources\033[0m"
@$(MKDIR) $(FICUS_BIN)
@$(FICUS0) $(FICUS_FLAGS) -o $(FICUS) $(FICUS_SRC)/fx.fx

make_dirs:
$(BOOTSTRAP_BUILD_DIR):
@echo "\033[34;1mBuilding reference compiler from the pre-generated .c sources\033[0m"
@$(MKDIR) $(BUILD_DIR)
@$(MKDIR) $(BOOTSTRAP_BUILD_DIR)

pre_ficus:
@echo "\033[34;1mBuilding fresh compiler from the actual ficus sources\033[0m"

final_note:
final_note: | $(FICUS)
@echo ""
@echo "\033[32;1m./ficus has been built successfully.\033[0m"
@echo "Now setup 'FICUS_PATH=<path_to_stdlib>' (lib subdirectory of the root directory),"
@echo "'FICUS_CFLAGS=-I<path_to_runtime>' (runtime subdirectory of the root directory)"
@echo "And optionally 'FICUS_LINKED_LIBRARIES=...'."
@echo "It's recommended to build ficus with -lmimalloc or another efficient memory allocator."
@echo "Then you can copy ficus to whatever directory you want and use it."
@echo "When compiling an application, ficus creates __build__/<appname> subdirectory"
@echo "\033[32;1m./ficus was built successfully.\033[0m"
@echo "Now setup 'FICUS_PATH=<path_to_stdlib>' (lib subdirectory of the root directory)"
@echo "and optionally 'FICUS_CFLAGS' and 'FICUS_LINK_LIBRARIES' to pass to C compiler."
@echo "After that you can copy ficus to whatever directory you want and use it."
@echo "When compiling an application, ficus creates __fxbuild__/<appname> subdirectory"
@echo "in the current directory for the intermediate .c and .o files,"
@echo "as well as the produced application".

clean:
@$(RMDIR) $(BUILD_DIR)
@$(RMDIR) $(BOOTSTRAP_BUILD_DIR)
@$(RMDIR) $(BUILD_DIR)/ficus
@$(RM) $(FICUS)
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,30 @@ The compiler is written in Ficus itself and needs C/C++ compiler and make utilit

```
cd <ficus_root>
make
FICUS_PATH=./lib FICUS_CFLAGS=-I./runtime ./ficus -run -O3 examples/fst.fx
# or
# ./ficus -I ./lib -cflags "-I./runtime" -run -O3 examples/fst.fx
make -j8
bin/ficus -run -O3 examples/fst.fx
```

`FICUS_PATH` and `FICUS_CFLAGS` are the two variables to setup to make ficus usable from any directory.
You can add `<ficus_root>/bin` to the `PATH`. You can also customize ficus compiler behaviour by setting the following environment variables:

* The first one, `FICUS_PATH`, should point to the standard library (`<ficus_root>/lib`), but can also contain
other directories separated by `:` on Unix and `;` on Windows. Note that if a compiled module imports other modules
from the directory where it resides, that directory does not need to be included.
* `FICUS_PATH` can point to the standard library (`<ficus_root>/lib`), though ficus attempts to find the standard library even without `FICUS_PATH`. It can also contain other directories separated by `:` on Unix and `;` on Windows. The directories with imported modules can also be provided via one or more command-line options `-I <import_path>`. Note that if a compiled module imports other modules from the directory where it resides, that directory does not need to be explicitly specified.

* The second one, `FICUS_CFLAGS`, is used by C/C++ compiler to build the produced .c/.cpp files.
The generated files include ficus runtime headers, and the path to the runtime directory needs
to be specified via command line option `-cflags` or the environment variable.
* `FICUS_CFLAGS` is used by C/C++ compiler to build the produced .c/.cpp files. Alternative way to pass extra flags to C/C++ compiler is via `-cflags "<cflags>"` command-line option, e.g. `-cflags "-ffast-math -mavx2"`.

* `FICUS_LINK_LIBRARIES` contains the linker flags and the extra linked libraries. Alternative way to pass the extra linker flags to C/C++ compiler is via `-clibs "<clibs>"` command-line option.

## How to use

run `./ficus --help` to get more complete up-to-date information about command line parameters
run `ficus --help` to get more complete up-to-date information about command line parameters

here is brief summary:
```
./ficus [-app|-run|...] [-O0|-O1|-O3] [-I<extra_module_path>]* <scriptname.fx> [-- <script arg1> <script arg2> ...]
ficus [-app|-run|...] [-O0|-O1|-O3] [-verbose] [-I <extra_module_path>]* [-o <appname>] <scriptname.fx> [-- <script arg1> <script arg2> ...]
```

* `-app` (the flag is set by default) generates C code for the specified script as well as for the imported modules (one .c file per one .fx file), then run the compiler for each of the generated .c files and then link the produced object files into the final app. Use `FICUS_CFLAGS` and `FICUS_LINK_LIBRARIES` environment variables to pass extra options to C compiler, e.g. `-ffast-math -mavx2` `-lmimalloc` etc. The compiled app, as well as the intermediate `.c` and `.o` files, is stored in `__build__/<scriptname>/<scriptname>`. Override the output name with `-o` option.
* `-run` builds the app (see flags `-app`) and then runs it.
* `-app` (the flag is set by default) generates C code for the specified script as well as for the imported modules (one .c file per one .fx file), then runs the compiler for each of the generated .c files and then links the produced object files into the final app. The compiled app, as well as the intermediate `.c` and `.o` files, is stored in `__fxbuild__/<appname>/<appname>`. By default `<appname>==<scriptname>`. Override the app name (and the output path) with `-o` option.
* `-run` builds the app (see flags `-app`) and then runs it. You can pass command-line parameters to the script after `--` separator.
* `-verbose` makes the compiler to report build progress and various information, which can be especially useful when building big apps

## Ficus 1.0

Expand Down
23 changes: 13 additions & 10 deletions compiler/Ast.fx
Original file line number Diff line number Diff line change
Expand Up @@ -507,9 +507,9 @@ fun dynvec_set(v: 't dynvec_t ref, i: int, newv: 't) = v->data[i] = newv

var freeze_ids = false
val all_ids = dynvec_create(IdNone)
var all_strhash: (string, int) Map.t = Map.empty(String.cmp)
var all_strhash: (string, int) Hashmap.t = Hashmap.empty(1024, "", -1, hash)
val all_strings = dynvec_create("")
var all_modules: (string, id_t) Map.t = Map.empty(String.cmp)
var all_modules: (string, id_t) Hashmap.t = Hashmap.empty(1024, "", noid, hash)
var all_modules_sorted: id_t list = []
var builtin_exceptions = empty_idmap
var all_compile_errs: exn list = []
Expand Down Expand Up @@ -607,15 +607,18 @@ fun is_unique_id(i: id_t) {
| _ => true
}

fun get_id_prefix(s: string): int =
match all_strhash.find_opt(s) {
| Some idx => idx
| _ =>
fun get_id_prefix(s: string): int
{
val h_idx = all_strhash.find_idx_or_insert(s)
val idx = all_strhash.r->table[h_idx].data
if idx >= 0 { idx }
else {
val idx = dynvec_push(all_strings)
all_strhash = all_strhash.add(s, idx)
all_strhash.r->table[h_idx].data = idx
dynvec_set(all_strings, idx, s)
idx
}
}

fun get_id(s: string): id_t {
val i = get_id_prefix(s)
Expand Down Expand Up @@ -731,7 +734,7 @@ fun find_module(mname_id: id_t, mfname: string) =
dm_env=empty_env, dm_parsed=false, dm_real=true
})
set_id_entry(m_fresh_id, IdModule(newmodule))
all_modules = all_modules.add(mfname, m_fresh_id)
all_modules.add(mfname, m_fresh_id)
newmodule
}

Expand Down Expand Up @@ -1577,10 +1580,10 @@ fun init_all(): void
{
freeze_ids = false
dynvec_clear(all_ids)
all_strhash = Map.empty(String.cmp)
all_strhash.clear()
for i <- __builtin_ids__ { ignore(get_id(i)) }
ignore(fname_always_import())
all_modules = Map.empty(String.cmp)
all_modules.clear()
all_modules_sorted = []
builtin_exceptions = Map.empty(cmp_id)
all_compile_errs = []
Expand Down
9 changes: 8 additions & 1 deletion compiler/Ast_typecheck.fx
Original file line number Diff line number Diff line change
Expand Up @@ -1620,7 +1620,14 @@ fun check_exp(e: exp_t, env: env_t, sc: scope_t list) {
val (typ2, loc2) = get_exp_ctx(e2)
unify(ctyp, TypBool, cloc, "if() condition should have 'bool' type")
unify(typ1, etyp, loc1, "if() expression should have the same type as its branches")
unify(typ2, etyp, loc2, "if() expression should have the same type as its branches")
if !maybe_unify(typ2, etyp, loc2, true) {
match e2 {
| ExpNop _ =>
throw compile_err(loc2, "if() expression of non-void type has no 'else' branch")
| _ =>
unify(typ2, etyp, loc2, "if() expression should have the same type as its branches")
}
}
val new_c = check_exp(c, env, sc)
val new_e1 = check_exp(e1, env, sc)
val new_e2 = check_exp(e2, env, sc)
Expand Down
12 changes: 7 additions & 5 deletions compiler/C_gen_code.fx
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,21 @@ fun gen_main(ismain: bool, mod_names: string list, loc: loc_t) =
fold fwd_decls = [], init_calls = [], deinit_calls = [] for m@idx <- mod_names {
(if idx == 0 { fwd_decls }
else { f"FX_EXTERN_C int fx_init_{m}();\nFX_EXTERN_C void fx_deinit_{m}();\n" :: fwd_decls },
f" if (fx_status >= 0) fx_status = fx_init_{m}();\n" :: init_calls,
f" fx_deinit_{m}();\n" :: deinit_calls)
f" if (fx_status >= 0) fx_status = fx_init_{m}();\n" :: init_calls,
f" fx_deinit_{m}();\n" :: deinit_calls)
}
CExp(CExpCCode(
"".join(fwd_decls) + "
int main(int argc, char** argv)
{
fx_init(argc, argv);
int fx_status = FX_OK;
fx_init(argc, argv);
int fx_status = FX_OK;
" +
"".join(init_calls) +
" if (fx_status < 0) fx_status = fx_print_bt();
" +
"".join(deinit_calls.rev()) +
" return fx_deinit(fx_status);
" return fx_deinit(fx_status);
}",
loc)) :: []
}
Expand Down
Loading

0 comments on commit 8eef9c4

Please sign in to comment.