mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Populate tree.
This commit is contained in:
parent
c01efc669f
commit
d6b7c96c3e
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
*~
|
||||
*.x86
|
||||
*.llvm
|
||||
*.out
|
||||
*.exe
|
||||
*.orig
|
||||
|
@ -1,7 +1,12 @@
|
||||
Rust authors:
|
||||
Initial author, project lead, target of blame:
|
||||
|
||||
Graydon Hoare <graydon@mozilla.com>
|
||||
|
||||
Other authors:
|
||||
|
||||
Andreas Gal <gal@mozilla.com>
|
||||
Brendan Eich <brendan@mozilla.org>
|
||||
Dave Herman <dherman@mozilla.com>
|
||||
Michael Bebenita <mbebenita@mozilla.com>
|
||||
Patrick Walton <pwalton@mozilla.com>
|
||||
Brendan Eich <brendan@mozilla.com>
|
||||
Roy Frostig <rfrostig@mozilla.com>
|
||||
|
32
LICENSE.txt
32
LICENSE.txt
@ -53,7 +53,8 @@ The following third party packages are included:
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
@ -71,9 +72,10 @@ The following third party packages are included:
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
* Two header files that are part of the Valgrind package. These files are found
|
||||
at src/rt/valgrind.h and src/rt/memcheck.h, within this distribution. These
|
||||
files are redistributed under the following terms, as noted in them:
|
||||
* Two header files that are part of the Valgrind package. These files are
|
||||
found at src/rt/valgrind.h and src/rt/memcheck.h, within this
|
||||
distribution. These files are redistributed under the following terms, as
|
||||
noted in them:
|
||||
|
||||
for src/rt/valgrind.h:
|
||||
|
||||
@ -158,20 +160,20 @@ well as the collective work itslf, is distributed under the following terms:
|
||||
Copyright (c) 2006-2010 Graydon Hoare
|
||||
Copyright (c) 2009-2010 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
@ -5,7 +5,8 @@ all: rust.pdf rust.html
|
||||
texi2pdf $<
|
||||
|
||||
%.html: %.texi
|
||||
makeinfo --html --force --no-split --output=$@ $<
|
||||
makeinfo --html --ifhtml --force --no-split --output=$@ $<
|
||||
|
||||
clean:
|
||||
rm -f rust.aux rust.cp rust.fn rust.ky rust.log rust.pdf rust.html rust.pg rust.toc rust.tp rust.vr
|
||||
rm -f rust.aux rust.cp rust.fn rust.ky rust.log rust.pdf \
|
||||
rust.html rust.pg rust.toc rust.tp rust.vr
|
3244
doc/rust.texi
Normal file
3244
doc/rust.texi
Normal file
File diff suppressed because it is too large
Load Diff
385
src/Makefile
385
src/Makefile
@ -19,27 +19,29 @@ endif
|
||||
|
||||
CFG_INFO := $(info cfg: building on $(CFG_OSTYPE) $(CFG_CPUTYPE))
|
||||
|
||||
CFG_GCC_COMPILE_FLAGS :=
|
||||
CFG_GCC_CFLAGS :=
|
||||
CFG_GCC_LINK_FLAGS :=
|
||||
CFG_VALGRIND :=
|
||||
|
||||
CFG_LLVM_CONFIG := llvm-config
|
||||
CFG_BOOT_FLAGS :=
|
||||
CFG_BOOT_FLAGS := $(FLAGS)
|
||||
|
||||
ifeq ($(CFG_OSTYPE), Linux)
|
||||
CFG_RUNTIME := librustrt.so
|
||||
CFG_STDLIB := libstd.so
|
||||
CFG_GCC_COMPILE_FLAGS += -fPIC
|
||||
CFG_GCC_CFLAGS += -fPIC
|
||||
CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread
|
||||
ifeq ($(CFG_CPUTYPE), x86_64)
|
||||
CFG_GCC_COMPILE_FLAGS += -m32
|
||||
CFG_GCC_CFLAGS += -m32
|
||||
CFG_GCC_LINK_FLAGS += -m32
|
||||
endif
|
||||
CFG_NATIVE := 1
|
||||
CFG_UNIXY := 1
|
||||
CFG_VALGRIND := $(shell which valgrind)
|
||||
ifdef CFG_VALGRIND
|
||||
CFG_VALGRIND += --run-libc-freeres=no --leak-check=full --quiet --vex-iropt-level=0
|
||||
CFG_VALGRIND += --leak-check=full \
|
||||
--quiet --vex-iropt-level=0 \
|
||||
--suppressions=etc/x86.supp
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -52,7 +54,7 @@ ifeq ($(CFG_OSTYPE), Darwin)
|
||||
# "on an i386" when the whole userspace is 64-bit and the compiler
|
||||
# emits 64-bit binaries by default. So we just force -m32 here. Smarter
|
||||
# approaches welcome!
|
||||
CFG_GCC_COMPILE_FLAGS += -m32
|
||||
CFG_GCC_CFLAGS += -m32
|
||||
CFG_GCC_LINK_FLAGS += -m32
|
||||
endif
|
||||
|
||||
@ -73,7 +75,7 @@ ifdef CFG_WINDOWSY
|
||||
CFG_EXE_SUFFIX := .exe
|
||||
CFG_BOOT := ./rustboot.exe
|
||||
CFG_COMPILER := ./rustc.exe
|
||||
CFG_GCC_COMPILE_FLAGS += -march=i686
|
||||
CFG_GCC_CFLAGS += -march=i686
|
||||
CFG_GCC_LINK_FLAGS += -shared -fPIC
|
||||
CFG_RUN_TARG = $(1)
|
||||
# FIXME: support msvc at some point
|
||||
@ -99,10 +101,10 @@ ifdef CFG_UNIXY
|
||||
endif
|
||||
CFG_OBJ_SUFFIX := .o
|
||||
CFG_EXE_SUFFIX := .exe
|
||||
CFG_GCC_COMPILE_FLAGS :=
|
||||
CFG_GCC_CFLAGS :=
|
||||
CFG_GCC_LINK_FLAGS := -shared
|
||||
ifeq ($(CFG_CPUTYPE), x86_64)
|
||||
CFG_GCC_COMPILE_FLAGS += -m32
|
||||
CFG_GCC_CFLAGS += -m32
|
||||
CFG_GCC_LINK_FLAGS += -m32
|
||||
endif
|
||||
endif
|
||||
@ -110,11 +112,11 @@ endif
|
||||
|
||||
ifdef CFG_GCC
|
||||
CFG_INFO := $(info cfg: using gcc)
|
||||
CFG_GCC_COMPILE_FLAGS += -Wall -Werror -fno-rtti -fno-exceptions -g
|
||||
CFG_GCC_CFLAGS += -Wall -Werror -fno-rtti -fno-exceptions -g
|
||||
CFG_GCC_LINK_FLAGS += -g
|
||||
CFG_COMPILE_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_COMPILE_FLAGS) -c -o $(1) $(2)
|
||||
CFG_COMPILE_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_CFLAGS) -c -o $(1) $(2)
|
||||
CFG_LINK_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_LINK_FLAGS) -o $(1)
|
||||
CFG_DEPEND_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_COMPILE_FLAGS) -MT "$(1)" -MM $(2)
|
||||
CFG_DEPEND_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_CFLAGS) -MT "$(1)" -MM $(2)
|
||||
else
|
||||
CFG_ERR := $(error please try on a system with gcc)
|
||||
endif
|
||||
@ -153,7 +155,8 @@ ifneq ($(CFG_LLVM_CONFIG),)
|
||||
$(info cfg: using LLVM version 2.8svn)
|
||||
else
|
||||
CFG_LLVM_CONFIG :=
|
||||
$(info cfg: incompatible LLVM version $(CFG_LLVM_VERSION), expected 2.8svn)
|
||||
$(info cfg: incompatible LLVM version $(CFG_LLVM_VERSION), \
|
||||
expected 2.8svn)
|
||||
endif
|
||||
endif
|
||||
ifdef CFG_LLVM_CONFIG
|
||||
@ -161,11 +164,12 @@ ifdef CFG_LLVM_CONFIG
|
||||
WHERE := $(shell ocamlc -where)
|
||||
LLVM_LIBS := llvm.cma llvm_bitwriter.cma
|
||||
LLVM_NATIVE_LIBS := llvm.cmxa llvm_bitwiter.cmxa
|
||||
LLVM_CLIBS := $(shell for c in `$(CFG_LLVM_CONFIG) --ldflags --libs` -lllvm -lllvm_bitwriter; do echo -cclib && echo $$c; done | xargs echo)
|
||||
LLVM_CLIBS := $(shell for c in `$(CFG_LLVM_CONFIG) --ldflags --libs` \
|
||||
-lllvm -lllvm_bitwriter; do echo -cclib && echo $$c; done | xargs echo)
|
||||
LLVM_INCS := -I boot/llvm -I $(WHERE)
|
||||
LLVM_MLS := $(addprefix boot/llvm/, llabi.ml llasm.ml llfinal.ml lltrans.ml \
|
||||
llemit.ml)
|
||||
CFG_LLC_COMPILE_FLAGS := -march=x86
|
||||
LLVM_MLS := $(addprefix boot/llvm/, llabi.ml llasm.ml llfinal.ml \
|
||||
lltrans.ml llemit.ml)
|
||||
CFG_LLC_CFLAGS := -march=x86
|
||||
$(info cfg: found llvm-config at $(CFG_LLVM_CONFIG))
|
||||
else
|
||||
VARIANT=x86
|
||||
@ -190,7 +194,8 @@ ML_INCS := -I boot/fe -I boot/me -I boot/be -I boot/driver/$(VARIANT) \
|
||||
ML_LIBS := unix.cma nums.cma bigarray.cma
|
||||
ML_NATIVE_LIBS := unix.cmxa nums.cmxa bigarray.cmxa
|
||||
OCAMLC_FLAGS := -g $(ML_INCS) -w Ael -warn-error Ael
|
||||
OCAMLOPT_FLAGS := $(ML_INCS) -w Ael -warn-error Ael $(CFG_OCAMLOPT_PROFILE_FLAGS)
|
||||
OCAMLOPT_FLAGS := $(ML_INCS) -w Ael -warn-error Ael \
|
||||
$(CFG_OCAMLOPT_PROFILE_FLAGS)
|
||||
|
||||
ifdef CFG_LLVM_CONFIG
|
||||
ML_LIBS += $(LLVM_LIBS) -custom -cclib -lstdc++ $(LLVM_CLIBS)
|
||||
@ -205,11 +210,12 @@ DRIVER_BOT_MLS := $(addprefix boot/driver/, session.ml)
|
||||
BE_MLS := $(addprefix boot/be/, x86.ml ra.ml pe.ml elf.ml \
|
||||
macho.ml)
|
||||
IL_MLS := $(addprefix boot/be/, asm.ml il.ml abi.ml)
|
||||
ME_MLS := $(addprefix boot/me/, walk.ml semant.ml resolve.ml alias.ml type.ml dead.ml \
|
||||
typestate.ml mode.ml mutable.ml gctype.ml loop.ml layout.ml transutil.ml \
|
||||
trans.ml dwarf.ml)
|
||||
FE_MLS := $(addprefix boot/fe/, ast.ml token.ml lexer.ml parser.ml pexp.ml item.ml cexp.ml)
|
||||
DRIVER_TOP_MLS := $(addprefix boot/driver/, $(VARIANT)/glue.ml lib.ml main.ml)
|
||||
ME_MLS := $(addprefix boot/me/, walk.ml semant.ml resolve.ml alias.ml \
|
||||
type.ml dead.ml effect.ml typestate.ml loop.ml layout.ml \
|
||||
transutil.ml trans.ml dwarf.ml)
|
||||
FE_MLS := $(addprefix boot/fe/, ast.ml token.ml lexer.ml parser.ml pexp.ml \
|
||||
item.ml cexp.ml)
|
||||
DRIVER_TOP_MLS := $(addprefix boot/driver/, lib.ml $(VARIANT)/glue.ml main.ml)
|
||||
|
||||
BOOT_MLS := $(UTIL_BOT_MLS) $(DRIVER_BOT_MLS) $(FE_MLS) $(IL_MLS) $(ME_MLS) \
|
||||
$(BE_MLS) $(LLVM_MLS) $(DRIVER_TOP_MLS)
|
||||
@ -226,8 +232,12 @@ RUNTIME_CS := rt/rust.cpp \
|
||||
rt/rust_comm.cpp \
|
||||
rt/rust_dom.cpp \
|
||||
rt/rust_task.cpp \
|
||||
rt/rust_chan.cpp \
|
||||
rt/rust_upcall.cpp \
|
||||
rt/rust_log.cpp \
|
||||
rt/rust_timer.cpp \
|
||||
rt/isaac/randport.cpp
|
||||
|
||||
RUNTIME_HDR := rt/rust.h \
|
||||
rt/rust_dwarf.h \
|
||||
rt/rust_internal.h \
|
||||
@ -253,7 +263,8 @@ $(CFG_RUNTIME): $(RUNTIME_OBJS) $(MKFILES) $(RUNTIME_HDR)
|
||||
|
||||
$(CFG_STDLIB): $(STDLIB_CRATE) $(CFG_BOOT) $(MKFILES)
|
||||
@$(call CFG_ECHO, compile: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -shared -o $@ $(STDLIB_CRATE)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) \
|
||||
-shared -o $@ $(STDLIB_CRATE)
|
||||
|
||||
%$(CFG_OBJ_SUFFIX): %.cpp $(MKFILES)
|
||||
@$(call CFG_ECHO, compile: $<)
|
||||
@ -262,7 +273,8 @@ $(CFG_STDLIB): $(STDLIB_CRATE) $(CFG_BOOT) $(MKFILES)
|
||||
ifdef CFG_NATIVE
|
||||
$(CFG_BOOT): $(BOOT_CMXS) $(MKFILES)
|
||||
@$(call CFG_ECHO, compile: $<)
|
||||
$(CFG_QUIET)ocamlopt$(OPT) -o $@ $(OCAMLOPT_FLAGS) $(ML_NATIVE_LIBS) $(BOOT_CMXS)
|
||||
$(CFG_QUIET)ocamlopt$(OPT) -o $@ $(OCAMLOPT_FLAGS) $(ML_NATIVE_LIBS) \
|
||||
$(BOOT_CMXS)
|
||||
else
|
||||
$(CFG_BOOT): $(BOOT_CMOS) $(MKFILES)
|
||||
@$(call CFG_ECHO, compile: $<)
|
||||
@ -288,7 +300,7 @@ endif
|
||||
# Main compiler targets and rules
|
||||
######################################################################
|
||||
|
||||
$(CFG_COMPILER): $(COMPILER_CRATE) $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
$(CFG_COMPILER): $(COMPILER_INPUTS) $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
@$(call CFG_ECHO, compile: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
|
||||
$(CFG_QUIET)chmod 0755 $@
|
||||
@ -302,13 +314,17 @@ self: $(CFG_COMPILER)
|
||||
# Testing
|
||||
######################################################################
|
||||
|
||||
TEST_XFAILS_X86 := test/run-pass/mlist_cycle.rs \
|
||||
TEST_XFAILS_X86 := test/run-pass/mlist-cycle.rs \
|
||||
test/run-pass/clone-with-exterior.rs \
|
||||
test/run-pass/obj-as.rs \
|
||||
test/run-pass/rec-auto.rs \
|
||||
test/run-pass/vec-slice.rs \
|
||||
test/run-pass/generic-fn-infer.rs \
|
||||
test/run-pass/generic-recursive-tag.rs \
|
||||
test/run-pass/generic-tag.rs \
|
||||
test/run-pass/generic-tag-alt.rs \
|
||||
test/run-pass/bind-obj-ctor.rs \
|
||||
test/run-pass/task-comm.rs \
|
||||
test/compile-fail/rec-missing-fields.rs \
|
||||
test/compile-fail/infinite-tag-type-recursion.rs \
|
||||
test/compile-fail/infinite-vec-type-recursion.rs
|
||||
@ -316,61 +332,74 @@ TEST_XFAILS_X86 := test/run-pass/mlist_cycle.rs \
|
||||
TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
|
||||
acyclic-unwind.rs \
|
||||
alt-tag.rs \
|
||||
argv.rs \
|
||||
basic.rs \
|
||||
bind-obj-ctor.rs \
|
||||
bind-thunk.rs \
|
||||
bind-trivial.rs \
|
||||
bitwise.rs \
|
||||
box-unbox.rs \
|
||||
cast.rs \
|
||||
char.rs \
|
||||
clone-with-exterior.rs \
|
||||
comm.rs \
|
||||
command-line-args.rs \
|
||||
complex.rs \
|
||||
dead-code-one-arm-if.rs \
|
||||
deep.rs \
|
||||
div-mod.rs \
|
||||
drop-on-ret.rs \
|
||||
else-if.rs \
|
||||
export-non-interference.rs \
|
||||
exterior.rs \
|
||||
foreach-simple.rs \
|
||||
foreach-simple-outer-slot.rs \
|
||||
foreach-put-structured.rs \
|
||||
vec-slice.rs \
|
||||
simple-obj.rs \
|
||||
import.rs \
|
||||
foreach-simple-outer-slot.rs \
|
||||
foreach-simple.rs \
|
||||
fun-call-variants.rs \
|
||||
fun-indirect-call.rs \
|
||||
generic-derived-type.rs \
|
||||
generic-drop-glue.rs \
|
||||
generic-fn.rs \
|
||||
generic-obj.rs \
|
||||
generic-obj-with-derived-type.rs \
|
||||
generic-tag.rs \
|
||||
generic-type.rs \
|
||||
generic-exterior-box.rs \
|
||||
generic-fn-infer.rs \
|
||||
vec-append.rs \
|
||||
vec-concat.rs \
|
||||
vec-drop.rs \
|
||||
mutable-vec-drop.rs \
|
||||
generic-fn.rs \
|
||||
generic-obj-with-derived-type.rs \
|
||||
generic-obj.rs \
|
||||
generic-recursive-tag.rs \
|
||||
generic-tag-alt.rs \
|
||||
generic-tag.rs \
|
||||
generic-type-synonym.rs \
|
||||
generic-type.rs \
|
||||
i32-sub.rs \
|
||||
i8-incr.rs \
|
||||
import.rs \
|
||||
inner-module.rs \
|
||||
large-records.rs \
|
||||
lazy-and-or.rs \
|
||||
lazychan.rs \
|
||||
linear-for-loop.rs \
|
||||
list.rs \
|
||||
many.rs \
|
||||
mlist-cycle.rs \
|
||||
mlist.rs \
|
||||
mlist_cycle.rs \
|
||||
mutable-vec-drop.rs \
|
||||
mutual-recursion-group.rs \
|
||||
native-mod.rc \
|
||||
native-opaque-type.rs \
|
||||
native.rc \
|
||||
command-line-args.rs \
|
||||
native_mod.rc \
|
||||
obj-as.rs \
|
||||
obj-drop.rs \
|
||||
obj-dtor.rs \
|
||||
obj-with-vec.rs \
|
||||
opeq.rs \
|
||||
preempt.rs \
|
||||
pred.rs \
|
||||
readalias.rs \
|
||||
rec-auto.rs \
|
||||
rec-extend.rs \
|
||||
rec-tup.rs \
|
||||
rec.rs \
|
||||
rec_tup.rs \
|
||||
return-nil.rs \
|
||||
i32-sub.rs \
|
||||
i8-incr.rs \
|
||||
simple-obj.rs \
|
||||
spawn-fn.rs \
|
||||
spawn.rs \
|
||||
stateful-obj.rs \
|
||||
@ -383,31 +412,31 @@ TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
|
||||
tail-direct.rs \
|
||||
threads.rs \
|
||||
tup.rs \
|
||||
type-sizes.rs \
|
||||
u32-decr.rs \
|
||||
u8-incr-decr.rs \
|
||||
u8-incr.rs \
|
||||
unit.rs \
|
||||
user.rs \
|
||||
utf8.rs \
|
||||
vec-append.rs \
|
||||
vec-concat.rs \
|
||||
vec-drop.rs \
|
||||
vec-slice.rs \
|
||||
vec.rs \
|
||||
writealias.rs \
|
||||
yield.rs \
|
||||
yield2.rs \
|
||||
native-opaque-type.rs \
|
||||
type-sizes.rs \
|
||||
obj-drop.rs \
|
||||
obj-dtor.rs \
|
||||
obj-with-vec.rs \
|
||||
else-if.rs \
|
||||
lazy-and-or.rs \
|
||||
task-comm.rs \
|
||||
) \
|
||||
$(addprefix test/run-fail/, \
|
||||
explicit-fail.rs \
|
||||
fail.rs \
|
||||
linked-failure.rs \
|
||||
pred.rs \
|
||||
vec_overrun.rs \
|
||||
str_overrun.rs \
|
||||
vec_underrun.rs \
|
||||
vec-overrun.rs \
|
||||
str-overrun.rs \
|
||||
vec-underrun.rs \
|
||||
) \
|
||||
$(addprefix test/compile-fail/, \
|
||||
rec-missing-fields.rs \
|
||||
@ -416,93 +445,109 @@ TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
|
||||
)
|
||||
|
||||
ifdef CFG_WINDOWSY
|
||||
TEST_XFAILS_X86 += test/run-pass/native_mod.rc
|
||||
TEST_XFAILS_LLVM += test/run-pass/native_mod.rc
|
||||
TEST_XFAILS_X86 += test/run-pass/native-mod.rc
|
||||
TEST_XFAILS_LLVM += test/run-pass/native-mod.rc
|
||||
else
|
||||
TEST_XFAILS_X86 += test/run-pass/preempt.rs
|
||||
TEST_XFAILS_LLVM += test/run-pass/preempt.rs
|
||||
endif
|
||||
|
||||
TEST_RUN_PASS_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-pass/*.rc))
|
||||
TEST_RUN_PASS_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-pass/*.rc))
|
||||
TEST_RUN_PASS_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-pass/*.rs))
|
||||
TEST_RUN_PASS_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-pass/*.rs))
|
||||
TEST_RUN_PASS_EXTRAS := $(wildcard test/run-pass/*/*.rs)
|
||||
TEST_RUN_PASS_EXES_X86 := \
|
||||
$(TEST_RUN_PASS_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RUN_PASS_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_RUN_PASS_EXES_LLVM := \
|
||||
$(TEST_RUN_PASS_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RUN_PASS_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_RUN_PASS_OUTS_X86 := \
|
||||
$(TEST_RUN_PASS_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_RUN_PASS_OUTS_LLVM := \
|
||||
$(TEST_RUN_PASS_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
RPASS_RC := $(wildcard test/run-pass/*.rc)
|
||||
RPASS_RS := $(wildcard test/run-pass/*.rs)
|
||||
RFAIL_RC := $(wildcard test/run-fail/*.rc)
|
||||
RFAIL_RS := $(wildcard test/run-fail/*.rs)
|
||||
CFAIL_RC := $(wildcard test/compile-fail/*.rc)
|
||||
CFAIL_RS := $(wildcard test/compile-fail/*.rs)
|
||||
|
||||
TEST_RPASS_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RPASS_RC))
|
||||
TEST_RPASS_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RPASS_RC))
|
||||
TEST_RPASS_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RPASS_RS))
|
||||
TEST_RPASS_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RPASS_RS))
|
||||
TEST_RPASS_EXTRAS := $(wildcard test/run-pass/*/*.rs)
|
||||
TEST_RPASS_EXES_X86 := \
|
||||
$(TEST_RPASS_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RPASS_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_RPASS_EXES_LLVM := \
|
||||
$(TEST_RPASS_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RPASS_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_RPASS_OUTS_X86 := \
|
||||
$(TEST_RPASS_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_RPASS_OUTS_LLVM := \
|
||||
$(TEST_RPASS_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
|
||||
|
||||
TEST_RUN_FAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-fail/*.rc))
|
||||
TEST_RUN_FAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-fail/*.rc))
|
||||
TEST_RUN_FAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-fail/*.rs))
|
||||
TEST_RUN_FAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-fail/*.rs))
|
||||
TEST_RUN_FAIL_EXTRAS := $(wildcard test/run-fail/*/*.rs)
|
||||
TEST_RUN_FAIL_EXES_X86 := \
|
||||
$(TEST_RUN_FAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RUN_FAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_RUN_FAIL_EXES_LLVM := \
|
||||
$(TEST_RUN_FAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RUN_FAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_RUN_FAIL_OUTS_X86 := \
|
||||
$(TEST_RUN_FAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_RUN_FAIL_OUTS_LLVM := \
|
||||
$(TEST_RUN_FAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
TEST_RFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RFAIL_RC))
|
||||
TEST_RFAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RFAIL_RC))
|
||||
TEST_RFAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RFAIL_RS))
|
||||
TEST_RFAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RFAIL_RS))
|
||||
TEST_RFAIL_EXTRAS := $(wildcard test/run-fail/*/*.rs)
|
||||
TEST_RFAIL_EXES_X86 := \
|
||||
$(TEST_RFAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RFAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_RFAIL_EXES_LLVM := \
|
||||
$(TEST_RFAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_RFAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_RFAIL_OUTS_X86 := \
|
||||
$(TEST_RFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_RFAIL_OUTS_LLVM := \
|
||||
$(TEST_RFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
|
||||
|
||||
TEST_COMPILE_FAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/compile-fail/*.rc))
|
||||
TEST_COMPILE_FAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/compile-fail/*.rc))
|
||||
TEST_COMPILE_FAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/compile-fail/*.rs))
|
||||
TEST_COMPILE_FAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/compile-fail/*.rs))
|
||||
TEST_COMPILE_FAIL_EXTRAS := $(wildcard test/compile-fail/*/*.rs)
|
||||
TEST_COMPILE_FAIL_EXES_X86 := \
|
||||
$(TEST_COMPILE_FAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_COMPILE_FAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_COMPILE_FAIL_EXES_LLVM := \
|
||||
$(TEST_COMPILE_FAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_COMPILE_FAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_COMPILE_FAIL_OUTS_X86 := \
|
||||
$(TEST_COMPILE_FAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_COMPILE_FAIL_OUTS_LLVM := \
|
||||
$(TEST_COMPILE_FAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
TEST_CFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(CFAIL_RC))
|
||||
TEST_CFAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(CFAIL_RC))
|
||||
TEST_CFAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(CFAIL_RS))
|
||||
TEST_CFAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(CFAIL_RS))
|
||||
TEST_CFAIL_EXTRAS := $(wildcard test/compile-fail/*/*.rs)
|
||||
TEST_CFAIL_EXES_X86 := \
|
||||
$(TEST_CFAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_CFAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
|
||||
TEST_CFAIL_EXES_LLVM := \
|
||||
$(TEST_CFAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
|
||||
$(TEST_CFAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
|
||||
TEST_CFAIL_OUTS_X86 := \
|
||||
$(TEST_CFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
|
||||
TEST_CFAIL_OUTS_LLVM := \
|
||||
$(TEST_CFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
|
||||
|
||||
ALL_TEST_CRATES := $(TEST_COMPILE_FAIL_CRATES_X86) \
|
||||
$(TEST_RUN_FAIL_CRATES_X86) \
|
||||
$(TEST_RUN_PASS_CRATES_X86)
|
||||
ALL_TEST_CRATES := $(TEST_CFAIL_CRATES_X86) \
|
||||
$(TEST_RFAIL_CRATES_X86) \
|
||||
$(TEST_RPASS_CRATES_X86)
|
||||
|
||||
ALL_TEST_SOURCES := $(TEST_COMPILE_FAIL_SOURCES_X86) \
|
||||
$(TEST_RUN_FAIL_SOURCES_X86) \
|
||||
$(TEST_RUN_PASS_SOURCES_X86)
|
||||
ALL_TEST_SOURCES := $(TEST_CFAIL_SOURCES_X86) \
|
||||
$(TEST_RFAIL_SOURCES_X86) \
|
||||
$(TEST_RPASS_SOURCES_X86)
|
||||
|
||||
ALL_TEST_INPUTS := $(wildcard test/*/*.rs test/*/*/*.rs test/*/*.rc)
|
||||
|
||||
|
||||
check_nocompile: $(TEST_COMPILE_FAIL_OUTS_X86)
|
||||
check_nocompile: $(TEST_CFAIL_OUTS_X86)
|
||||
|
||||
check: tidy \
|
||||
$(TEST_RPASS_EXES_X86) $(TEST_RFAIL_EXES_X86) \
|
||||
$(TEST_RPASS_OUTS_X86) $(TEST_RFAIL_OUTS_X86) \
|
||||
$(TEST_CFAIL_OUTS_X86)
|
||||
|
||||
check: $(TEST_RUN_PASS_EXES_X86) $(TEST_RUN_FAIL_EXES_X86) \
|
||||
$(TEST_RUN_PASS_OUTS_X86) $(TEST_RUN_FAIL_OUTS_X86) \
|
||||
$(TEST_COMPILE_FAIL_OUTS_X86)
|
||||
|
||||
ifeq ($(VARIANT),llvm)
|
||||
ALL_TEST_CRATES += $(TEST_COMPILE_FAIL_CRATES_LLVM) \
|
||||
$(TEST_RUN_FAIL_CRATES_LLVM) \
|
||||
$(TEST_RUN_PASS_CRATES_LLVM)
|
||||
ALL_TEST_CRATES += $(TEST_CFAIL_CRATES_LLVM) \
|
||||
$(TEST_RFAIL_CRATES_LLVM) \
|
||||
$(TEST_RPASS_CRATES_LLVM)
|
||||
|
||||
ALL_TEST_SOURCES += $(TEST_COMPILE_FAIL_SOURCES_LLVM) \
|
||||
$(TEST_RUN_FAIL_SOURCES_LLVM) \
|
||||
$(TEST_RUN_PASS_SOURCES_LLVM)
|
||||
ALL_TEST_SOURCES += $(TEST_CFAIL_SOURCES_LLVM) \
|
||||
$(TEST_RFAIL_SOURCES_LLVM) \
|
||||
$(TEST_RPASS_SOURCES_LLVM)
|
||||
|
||||
check_nocompile: $(TEST_COMPILE_FAIL_OUTS_LLVM)
|
||||
check_nocompile: $(TEST_CFAIL_OUTS_LLVM)
|
||||
|
||||
check: $(TEST_RUN_PASS_EXES_LLVM) $(TEST_RUN_FAIL_EXES_LLVM) \
|
||||
$(TEST_RUN_PASS_OUTS_LLVM) $(TEST_RUN_FAIL_OUTS_LLVM) \
|
||||
$(TEST_COMPILE_FAIL_OUTS_LLVM)
|
||||
check: tidy \
|
||||
$(TEST_RPASS_EXES_LLVM) $(TEST_RFAIL_EXES_LLVM) \
|
||||
$(TEST_RPASS_OUTS_LLVM) $(TEST_RFAIL_OUTS_LLVM) \
|
||||
$(TEST_CFAIL_OUTS_LLVM)
|
||||
endif
|
||||
|
||||
REQ := $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
BOOT := $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS)
|
||||
|
||||
test/run-pass/%.out: test/run-pass/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
|
||||
@$(call CFG_ECHO, run: $<)
|
||||
$(CFG_QUIET)$(call CFG_RUN_TARG, $<) > $@
|
||||
@ -510,55 +555,57 @@ test/run-pass/%.out: test/run-pass/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
|
||||
test/run-fail/%.out: test/run-fail/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
|
||||
@$(call CFG_ECHO, run: $<)
|
||||
$(CFG_QUIET)rm -f $@
|
||||
$(CFG_QUIET)$(call CFG_RUN_TARG, $<) >$@ 2>&1 ; X=$$? ; if [ $$X -eq 0 ] ; then exit 1 ; else exit 0 ; fi
|
||||
$(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $(basename $(basename $@)).rs | tr -d '\n\r'`" $@
|
||||
$(CFG_QUIET)$(call CFG_RUN_TARG, $<) >$@ 2>&1 ; X=$$? ; \
|
||||
if [ $$X -eq 0 ] ; then exit 1 ; else exit 0 ; fi
|
||||
$(CFG_QUIET)grep --text --quiet \
|
||||
"`awk -F: '/error-pattern/ { print $$2 }' \
|
||||
$(basename $(basename $@)).rs | tr -d '\n\r'`" $@
|
||||
|
||||
test/compile-fail/%.x86.out: test/compile-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME)
|
||||
test/compile-fail/%.x86.out: test/compile-fail/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [x86]: $<)
|
||||
$(CFG_QUIET)rm -f $@
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
|
||||
$(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
|
||||
$(BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
|
||||
$(CFG_QUIET)grep --text --quiet \
|
||||
"`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
|
||||
|
||||
test/compile-fail/%.llvm.out: test/compile-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME)
|
||||
test/compile-fail/%.llvm.out: test/compile-fail/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)rm -f $@
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
|
||||
$(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
|
||||
$(BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
|
||||
$(CFG_QUIET)grep --text --quiet \
|
||||
"`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
|
||||
|
||||
test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rc $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rc $(REQ)
|
||||
@$(call CFG_ECHO, compile [x86]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
|
||||
$(CFG_QUIET)chmod 0755 $@
|
||||
$(BOOT) -o $@ $<
|
||||
|
||||
%.s: %.bc
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)llc $(CFG_LLC_COMPILE_FLAGS) -o $@ $<
|
||||
$(CFG_QUIET)llc $(CFG_LLC_CFLAGS) -o $@ $<
|
||||
|
||||
%.llvm$(CFG_EXE_SUFFIX): %.s $(CFG_RUNTIME)
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)gcc $(CFG_GCC_COMPILE_FLAGS) -o $@ $< -L. -lrustrt
|
||||
$(CFG_QUIET)gcc $(CFG_GCC_CFLAGS) -o $@ $< -L. -lrustrt
|
||||
|
||||
test/run-pass/%.bc: test/run-pass/%.rc $(CFG_BOOT) $(CFG_STDLIB)
|
||||
test/run-pass/%.bc: test/run-pass/%.rc $(REQ)
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
|
||||
$(BOOT) -o $@ -llvm $<
|
||||
|
||||
test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rs $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [x86]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
|
||||
$(CFG_QUIET)chmod 0755 $@
|
||||
$(BOOT) -o $@ $<
|
||||
|
||||
test/run-pass/%.bc: test/run-pass/%.rs $(CFG_BOOT) $(CFG_STDLIB)
|
||||
test/run-pass/%.bc: test/run-pass/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
|
||||
$(BOOT) -o $@ -llvm $<
|
||||
|
||||
test/run-fail/%.x86$(CFG_EXE_SUFFIX): test/run-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
test/run-fail/%.x86$(CFG_EXE_SUFFIX): test/run-fail/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [x86]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
|
||||
$(CFG_QUIET)chmod 0755 $@
|
||||
$(BOOT) -o $@ $<
|
||||
|
||||
test/run-fail/%.bc: test/run-fail/%.rs $(CFG_BOOT) $(CFG_STDLIB)
|
||||
test/run-fail/%.bc: test/run-fail/%.rs $(REQ)
|
||||
@$(call CFG_ECHO, compile [llvm]: $<)
|
||||
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
|
||||
$(BOOT) -o $@ -llvm $<
|
||||
|
||||
|
||||
######################################################################
|
||||
@ -570,7 +617,9 @@ C_DEPFILES := $(RUNTIME_CS:%.cpp=%.d)
|
||||
|
||||
%.d: %.cpp $(MKFILES)
|
||||
@$(call CFG_ECHO, dep: $<)
|
||||
$(CFG_QUIET)$(call CFG_DEPEND_C, $@ $(patsubst %.cpp, %$(CFG_OBJ_SUFFIX), $<), $(RUNTIME_INCS)) $< $(CFG_PATH_MUNGE) >$@
|
||||
$(CFG_QUIET)$(call CFG_DEPEND_C, $@ \
|
||||
$(patsubst %.cpp, %$(CFG_OBJ_SUFFIX), $<), \
|
||||
$(RUNTIME_INCS)) $< $(CFG_PATH_MUNGE) >$@
|
||||
|
||||
%.d: %.ml $(MKFILES)
|
||||
@$(call CFG_ECHO, dep: $<)
|
||||
@ -593,15 +642,15 @@ CRATE_DEPFILES := $(ALL_TEST_CRATES:%.rc=%.d) $(STDLIB_DEPFILE)
|
||||
|
||||
$(STDLIB_DEPFILE): $(STDLIB_CRATE) $(MKFILES) $(CFG_BOOT)
|
||||
@$(call CFG_ECHO, dep: $<)
|
||||
$(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -shared -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
$(BOOT) -shared -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
|
||||
%.d: %.rc $(MKFILES) $(CFG_BOOT)
|
||||
@$(call CFG_ECHO, dep: $<)
|
||||
$(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
$(BOOT) -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
|
||||
%.d: %.rs $(MKFILES) $(CFG_BOOT)
|
||||
@$(call CFG_ECHO, dep: $<)
|
||||
$(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
$(BOOT) -rdeps $< $(CFG_PATH_MUNGE) >$@
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(CRATE_DEPFILES)
|
||||
@ -622,8 +671,9 @@ PKG_3RDPARTY := rt/valgrind.h rt/memcheck.h \
|
||||
rt/bigint/bigint.h rt/bigint/bigint_int.cpp \
|
||||
rt/bigint/bigint_ext.cpp rt/bigint/low_primes.h
|
||||
PKG_FILES := README \
|
||||
$(wildcard etc/*.*) \
|
||||
$(MKFILES) $(BOOT_MLS) boot/fe/lexer.mll \
|
||||
$(COMPILER_CRATE) $(COMPILER_INPUTS) \
|
||||
$(COMPILER_INPUTS) \
|
||||
$(STDLIB_CRATE) $(STDLIB_INPUTS) \
|
||||
$(RUNTIME_CS) $(RUNTIME_HDR) $(PKG_3RDPARTY) \
|
||||
$(ALL_TEST_INPUTS)
|
||||
@ -658,20 +708,29 @@ distcheck:
|
||||
# Cleanup
|
||||
######################################################################
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: clean tidy
|
||||
|
||||
tidy:
|
||||
@$(call CFG_ECHO, check: formatting)
|
||||
$(CFG_QUIET) python etc/tidy.py \
|
||||
$(wildcard ../*.txt) \
|
||||
../README \
|
||||
$(filter-out boot/fe/lexer.ml $(PKG_3RDPARTY), $(PKG_FILES))
|
||||
|
||||
clean:
|
||||
@$(call CFG_ECHO, cleaning)
|
||||
$(CFG_QUIET)rm -f $(RUNTIME_OBJS) $(BOOT_CMOS) $(BOOT_CMIS) $(BOOT_CMXS) $(BOOT_OBJS)
|
||||
$(CFG_QUIET)rm -f $(RUNTIME_OBJS)
|
||||
$(CFG_QUIET)rm -f $(BOOT_CMOS) $(BOOT_CMIS) $(BOOT_CMXS) $(BOOT_OBJS)
|
||||
$(CFG_QUIET)rm -f $(CFG_COMPILER)
|
||||
$(CFG_QUIET)rm -f $(ML_DEPFILES) $(C_DEPFILES) $(CRATE_DEPFILES)
|
||||
$(CFG_QUIET)rm -f boot/fe/lexer.ml
|
||||
$(CFG_QUIET)rm -f $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
|
||||
$(CFG_QUIET)rm -f $(TEST_RUN_PASS_EXES_X86) $(TEST_RUN_PASS_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_RUN_PASS_EXES_LLVM) $(TEST_RUN_PASS_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -f $(TEST_RUN_FAIL_EXES_X86) $(TEST_RUN_FAIL_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_RUN_FAIL_EXES_LLVM) $(TEST_RUN_FAIL_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -f $(TEST_COMPILE_FAIL_EXES_X86) $(TEST_COMPILE_FAIL_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_COMPILE_FAIL_EXES_LLVM) $(TEST_COMPILE_FAIL_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -f $(TEST_RPASS_EXES_X86) $(TEST_RPASS_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_RPASS_EXES_LLVM) $(TEST_RPASS_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_X86) $(TEST_RFAIL_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_LLVM) $(TEST_RFAIL_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_X86) $(TEST_CFAIL_OUTS_X86)
|
||||
$(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_LLVM) $(TEST_CFAIL_OUTS_LLVM)
|
||||
$(CFG_QUIET)rm -Rf $(PKG_NAME)-*.tar.gz dist
|
||||
$(CFG_QUIET)rm -f $(foreach ext,cmx cmi cmo cma o a d exe,$(wildcard boot/*/*.$(ext) boot/*/*/*.$(ext)))
|
||||
$(CFG_QUIET)rm -f $(foreach ext,cmx cmi cmo cma o a d exe,\
|
||||
$(wildcard boot/*/*.$(ext) boot/*/*/*.$(ext)))
|
||||
|
28
src/README
Normal file
28
src/README
Normal file
@ -0,0 +1,28 @@
|
||||
This is preliminary version of the Rust compiler.
|
||||
|
||||
Source layout:
|
||||
|
||||
boot/ The bootstrap compiler
|
||||
boot/fe - Front end (lexer, parser, AST)
|
||||
boot/me - Middle end (resolve, check, layout, trans)
|
||||
boot/be - Back end (IL, RA, insns, asm, objfiles)
|
||||
boot/util - Ubiquitous helpers
|
||||
boot/llvm - LLVM-based alternative back end
|
||||
boot/driver - Compiler driver
|
||||
|
||||
comp/ The self-hosted compiler (doesn't exist yet)
|
||||
comp/* - Same structure as in boot/
|
||||
|
||||
rt/ The runtime system
|
||||
rt/rust_*.cpp - The majority of the runtime services
|
||||
rt/isaac - The PRNG used for pseudo-random choices in the runtime
|
||||
rt/bigint - The bigint library used for the 'big' type
|
||||
rt/uthash - Small hashtable-and-list library for C, used in runtime
|
||||
rt/{sync,util} - Small utility classes for the runtime.
|
||||
|
||||
test/ Testsuite (for both bootstrap and self-hosted)
|
||||
test/compile-fail - Tests that should fail to compile
|
||||
test/run-fail - Tests that should compile, run and fail
|
||||
test/run-pass - Tests that should compile, run and succeed
|
||||
|
||||
Please be gentle, it's a work in progress.
|
207
src/boot/be/abi.ml
Normal file
207
src/boot/be/abi.ml
Normal file
@ -0,0 +1,207 @@
|
||||
|
||||
(*
|
||||
* The 'abi' structure is pretty much just a grab-bag of machine
|
||||
* dependencies and structure-layout information. Part of the latter
|
||||
* is shared with trans and semant.
|
||||
*
|
||||
* Make some attempt to factor it as time goes by.
|
||||
*)
|
||||
|
||||
(* Word offsets for structure fields in rust-internal.h, and elsewhere in
|
||||
compiler. *)
|
||||
|
||||
let rc_base_field_refcnt = 0;;
|
||||
|
||||
let task_field_refcnt = rc_base_field_refcnt;;
|
||||
let task_field_stk = task_field_refcnt + 1;;
|
||||
let task_field_runtime_sp = task_field_stk + 1;;
|
||||
let task_field_rust_sp = task_field_runtime_sp + 1;;
|
||||
let task_field_gc_alloc_chain = task_field_rust_sp + 1;;
|
||||
let task_field_dom = task_field_gc_alloc_chain + 1;;
|
||||
let n_visible_task_fields = task_field_dom + 1;;
|
||||
|
||||
let dom_field_interrupt_flag = 0;;
|
||||
|
||||
let frame_glue_fns_field_mark = 0;;
|
||||
let frame_glue_fns_field_drop = 1;;
|
||||
let frame_glue_fns_field_reloc = 2;;
|
||||
|
||||
let exterior_rc_slot_field_refcnt = 0;;
|
||||
let exterior_rc_slot_field_body = 1;;
|
||||
|
||||
let exterior_gc_slot_field_next = (-2);;
|
||||
let exterior_gc_slot_field_ctrl = (-1);;
|
||||
let exterior_gc_slot_field_refcnt = 0;;
|
||||
let exterior_gc_slot_field_body = 1;;
|
||||
|
||||
let exterior_rc_header_size = 1;;
|
||||
let exterior_gc_header_size = 3;;
|
||||
|
||||
let exterior_gc_malloc_return_adjustment = 2;;
|
||||
|
||||
let stk_field_valgrind_id = 0 + 1;;
|
||||
let stk_field_limit = stk_field_valgrind_id + 1;;
|
||||
let stk_field_data = stk_field_limit + 1;;
|
||||
|
||||
let binding_size = 2;;
|
||||
let binding_field_item = 0;;
|
||||
let binding_field_binding = 1;;
|
||||
|
||||
let general_code_alignment = 16;;
|
||||
|
||||
let tydesc_field_first_param = 0;;
|
||||
let tydesc_field_size = 1;;
|
||||
let tydesc_field_align = 2;;
|
||||
let tydesc_field_copy_glue = 3;;
|
||||
let tydesc_field_drop_glue = 4;;
|
||||
let tydesc_field_free_glue = 5;;
|
||||
let tydesc_field_mark_glue = 6;;
|
||||
let tydesc_field_obj_drop_glue = 7;;
|
||||
|
||||
let vec_elt_rc = 0;;
|
||||
let vec_elt_alloc = 1;;
|
||||
let vec_elt_fill = 2;;
|
||||
let vec_elt_data = 3;;
|
||||
|
||||
let calltup_elt_out_ptr = 0;;
|
||||
let calltup_elt_task_ptr = 1;;
|
||||
let calltup_elt_ty_params = 2;;
|
||||
let calltup_elt_args = 3;;
|
||||
let calltup_elt_iterator_args = 4;;
|
||||
let calltup_elt_indirect_args = 5;;
|
||||
|
||||
let iterator_args_elt_block_fn = 0;;
|
||||
let iterator_args_elt_outer_frame_ptr = 1;;
|
||||
|
||||
let indirect_args_elt_closure = 0;;
|
||||
|
||||
(* ty_params, src, dst, tydesc, taskptr. *)
|
||||
let worst_case_glue_call_args = 5;;
|
||||
|
||||
type abi =
|
||||
{
|
||||
abi_word_sz: int64;
|
||||
abi_word_bits: Il.bits;
|
||||
abi_word_ty: Common.ty_mach;
|
||||
|
||||
abi_is_2addr_machine: bool;
|
||||
abi_has_pcrel_data: bool;
|
||||
abi_has_pcrel_code: bool;
|
||||
|
||||
abi_n_hardregs: int;
|
||||
abi_str_of_hardreg: (int -> string);
|
||||
|
||||
abi_prealloc_quad: (Il.quad' -> Il.quad');
|
||||
abi_constrain_vregs: (Il.quad -> Bits.t array -> unit);
|
||||
|
||||
abi_emit_fn_prologue: (Il.emitter
|
||||
-> Common.size (* framesz *)
|
||||
-> Common.size (* callsz *)
|
||||
-> Common.nabi
|
||||
-> Common.fixup (* grow_task *)
|
||||
-> unit);
|
||||
|
||||
abi_emit_fn_epilogue: (Il.emitter -> unit);
|
||||
|
||||
abi_emit_fn_tail_call: (Il.emitter
|
||||
-> int64 (* caller_callsz *)
|
||||
-> int64 (* caller_argsz *)
|
||||
-> Il.code (* callee_code *)
|
||||
-> int64 (* callee_argsz *)
|
||||
-> unit);
|
||||
|
||||
abi_clobbers: (Il.quad -> Il.hreg list);
|
||||
|
||||
abi_emit_native_call: (Il.emitter
|
||||
-> Il.cell (* ret *)
|
||||
-> Common.nabi
|
||||
-> Common.fixup (* callee *)
|
||||
-> Il.operand array (* args *)
|
||||
-> unit);
|
||||
|
||||
abi_emit_native_void_call: (Il.emitter
|
||||
-> Common.nabi
|
||||
-> Common.fixup (* callee *)
|
||||
-> Il.operand array (* args *)
|
||||
-> unit);
|
||||
|
||||
abi_emit_native_call_in_thunk: (Il.emitter
|
||||
-> Il.cell (* ret *)
|
||||
-> Common.nabi
|
||||
-> Il.operand (* callee *)
|
||||
-> Il.operand array (* args *)
|
||||
-> unit);
|
||||
abi_emit_inline_memcpy: (Il.emitter
|
||||
-> int64 (* n_bytes *)
|
||||
-> Il.reg (* dst_ptr *)
|
||||
-> Il.reg (* src_ptr *)
|
||||
-> Il.reg (* tmp_reg *)
|
||||
-> bool (* ascending *)
|
||||
-> unit);
|
||||
|
||||
(* Global glue. *)
|
||||
abi_activate: (Il.emitter -> unit);
|
||||
abi_yield: (Il.emitter -> unit);
|
||||
abi_unwind: (Il.emitter -> Common.nabi -> Common.fixup -> unit);
|
||||
abi_get_next_pc_thunk:
|
||||
((Il.reg (* output *)
|
||||
* Common.fixup (* thunk in objfile *)
|
||||
* (Il.emitter -> unit)) (* fn to make thunk *)
|
||||
option);
|
||||
|
||||
abi_sp_reg: Il.reg;
|
||||
abi_fp_reg: Il.reg;
|
||||
abi_dwarf_fp_reg: int;
|
||||
abi_tp_cell: Il.cell;
|
||||
abi_implicit_args_sz: int64;
|
||||
abi_frame_base_sz: int64;
|
||||
abi_frame_info_sz: int64;
|
||||
abi_spill_slot: (Il.spill -> Il.mem);
|
||||
}
|
||||
;;
|
||||
|
||||
let load_fixup_addr
|
||||
(e:Il.emitter)
|
||||
(out_reg:Il.reg)
|
||||
(fix:Common.fixup)
|
||||
(rty:Il.referent_ty)
|
||||
: unit =
|
||||
|
||||
let cell = Il.Reg (out_reg, Il.AddrTy rty) in
|
||||
let op = Il.ImmPtr (fix, rty) in
|
||||
Il.emit e (Il.lea cell op);
|
||||
;;
|
||||
|
||||
let load_fixup_codeptr
|
||||
(e:Il.emitter)
|
||||
(out_reg:Il.reg)
|
||||
(fixup:Common.fixup)
|
||||
(has_pcrel_code:bool)
|
||||
(indirect:bool)
|
||||
: Il.code =
|
||||
if indirect
|
||||
then
|
||||
begin
|
||||
load_fixup_addr e out_reg fixup (Il.ScalarTy (Il.AddrTy Il.CodeTy));
|
||||
Il.CodePtr (Il.Cell (Il.Mem (Il.RegIn (out_reg, None),
|
||||
Il.ScalarTy (Il.AddrTy Il.CodeTy))))
|
||||
end
|
||||
else
|
||||
if has_pcrel_code
|
||||
then (Il.CodePtr (Il.ImmPtr (fixup, Il.CodeTy)))
|
||||
else
|
||||
begin
|
||||
load_fixup_addr e out_reg fixup Il.CodeTy;
|
||||
Il.CodePtr (Il.Cell (Il.Reg (out_reg, Il.AddrTy Il.CodeTy)))
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
755
src/boot/be/asm.ml
Normal file
755
src/boot/be/asm.ml
Normal file
@ -0,0 +1,755 @@
|
||||
(*
|
||||
|
||||
Our assembler is an all-at-once, buffer-in-memory job, very simple
|
||||
minded. I have 1gb of memory on my laptop: I don't expect to ever
|
||||
emit a program that large with this code.
|
||||
|
||||
It is based on the 'frag' type, which has a variant for every major
|
||||
type of machine-blob we know how to write (bytes, zstrings, BSS
|
||||
blocks, words of various sorts).
|
||||
|
||||
A frag can contain symbolic references between the sub-parts of
|
||||
it. These are accomplished through ref cells we call fixups, and a
|
||||
2-pass (resolution and writing) process defined recursively over
|
||||
the frag structure.
|
||||
|
||||
Fixups are defined by wrapping a frag in a DEF pseudo-frag with
|
||||
a fixup attached. This will record information about the wrapped
|
||||
frag -- positions and sizes -- in the fixup during resolution.
|
||||
|
||||
We say "positions" and "sizes" there, in plural, because both a
|
||||
file number and a memory number is recorded for each concept.
|
||||
|
||||
File numbers refer to positions and sizes in the file we're
|
||||
generating, and are based on the native int type for the host
|
||||
platform -- usually 31 or 62 bits -- whereas the expressions that
|
||||
*use* position fixups tend to promote them up to 32 or 64 bits
|
||||
somehow. On a 32 bit platform, you can't generate output buffers
|
||||
with 64-bit positions (ocaml limitation!)
|
||||
|
||||
Memory numbers are 64 bit, always, and refer to sizes and positions
|
||||
of frags when they are loaded into memory in the target. When
|
||||
you're generating code for a 32-bit target, or using a memory
|
||||
number in a context that's less than 64 bits, the value is
|
||||
range-checked and truncated. But in all other respects, we imagine
|
||||
a 32-bit address space is just the prefix of the continuing 64-bit
|
||||
address space. If you need to pin an object at a particular place
|
||||
from the point 2^32-1, say, you will need to do arithmetic and use
|
||||
the MEMPOS pseudo-frag, that sets the current memory position as
|
||||
it's being processed.
|
||||
|
||||
Fixups can be *used* anywhere else in the frag tree, as many times
|
||||
as you like. If you try to write an unresolved fixup, the emitter
|
||||
faults. When you specify the use of a fixup, you need to specify
|
||||
whether you want to use its file size, file position, memory size,
|
||||
or memory position.
|
||||
|
||||
Positions, addresses, sizes and such, of course, are in bytes.
|
||||
|
||||
Expressions are evaluated to an int64 (signed), even if the
|
||||
expression is an int32 or less. Depending on how you use the result
|
||||
of the expression, a range check error may fire (for example, if
|
||||
the expression evaluates to -2^24 and you're emitting a word16).
|
||||
|
||||
Word endianness is per-file. At the moment this seems acceptable.
|
||||
|
||||
Because we want to be *very specific* about the time and place
|
||||
arithmetic promotions occur, we define two separate expression-tree
|
||||
types (with the same polymorphic constructors) and two separate
|
||||
evaluation functions, with an explicit operator for marking the
|
||||
promotion-points.
|
||||
|
||||
*)
|
||||
|
||||
open Common;;
|
||||
|
||||
|
||||
let log (sess:Session.sess) =
|
||||
Session.log "asm"
|
||||
sess.Session.sess_log_asm
|
||||
sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
|
||||
if sess.Session.sess_log_asm
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
exception Bad_fit of string;;
|
||||
exception Undef_sym of string;;
|
||||
|
||||
type ('a, 'b) expr =
|
||||
IMM of 'a
|
||||
| ADD of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| SUB of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| MUL of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| DIV of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| REM of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| MAX of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| ALIGN of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| SLL of (('a, 'b) expr) * int
|
||||
| SLR of (('a, 'b) expr) * int
|
||||
| SAR of (('a, 'b) expr) * int
|
||||
| AND of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| XOR of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| OR of (('a, 'b) expr) * (('a, 'b) expr)
|
||||
| NOT of (('a, 'b) expr)
|
||||
| NEG of (('a, 'b) expr)
|
||||
| F_POS of fixup
|
||||
| F_SZ of fixup
|
||||
| M_POS of fixup
|
||||
| M_SZ of fixup
|
||||
| EXT of 'b
|
||||
|
||||
type expr32 = (int32, int) expr
|
||||
;;
|
||||
|
||||
type expr64 = (int64, expr32) expr
|
||||
;;
|
||||
|
||||
|
||||
let rec eval32 (e:expr32)
|
||||
: int32 =
|
||||
let chop64 kind name v =
|
||||
let x = Int64.to_int32 v in
|
||||
if (Int64.compare v (Int64.of_int32 x)) = 0 then
|
||||
x
|
||||
else raise (Bad_fit (kind
|
||||
^ " fixup "
|
||||
^ name
|
||||
^ " overflowed 32 bits in eval32: "
|
||||
^ Int64.to_string v))
|
||||
in
|
||||
let expandInt _ _ v = Int32.of_int v in
|
||||
let checkdef kind name v inj =
|
||||
match v with
|
||||
None ->
|
||||
raise (Undef_sym (kind ^ " fixup " ^ name
|
||||
^ " undefined in eval32"))
|
||||
| Some x -> inj kind name x
|
||||
in
|
||||
match e with
|
||||
IMM i -> i
|
||||
| ADD (a, b) -> Int32.add (eval32 a) (eval32 b)
|
||||
| SUB (a, b) -> Int32.sub (eval32 a) (eval32 b)
|
||||
| MUL (a, b) -> Int32.mul (eval32 a) (eval32 b)
|
||||
| DIV (a, b) -> Int32.div (eval32 a) (eval32 b)
|
||||
| REM (a, b) -> Int32.rem (eval32 a) (eval32 b)
|
||||
| MAX (a, b) -> i32_max (eval32 a) (eval32 b)
|
||||
| ALIGN (a, b) -> i32_align (eval32 a) (eval32 b)
|
||||
| SLL (a, b) -> Int32.shift_left (eval32 a) b
|
||||
| SLR (a, b) -> Int32.shift_right_logical (eval32 a) b
|
||||
| SAR (a, b) -> Int32.shift_right (eval32 a) b
|
||||
| AND (a, b) -> Int32.logand (eval32 a) (eval32 b)
|
||||
| XOR (a, b) -> Int32.logxor (eval32 a) (eval32 b)
|
||||
| OR (a, b) -> Int32.logor (eval32 a) (eval32 b)
|
||||
| NOT a -> Int32.lognot (eval32 a)
|
||||
| NEG a -> Int32.neg (eval32 a)
|
||||
| F_POS f ->
|
||||
checkdef "file position"
|
||||
f.fixup_name f.fixup_file_pos expandInt
|
||||
| F_SZ f ->
|
||||
checkdef "file size"
|
||||
f.fixup_name f.fixup_file_sz expandInt
|
||||
| M_POS f ->
|
||||
checkdef "mem position"
|
||||
f.fixup_name f.fixup_mem_pos chop64
|
||||
| M_SZ f ->
|
||||
checkdef "mem size" f.fixup_name f.fixup_mem_sz chop64
|
||||
| EXT i -> Int32.of_int i
|
||||
;;
|
||||
|
||||
let rec eval64 (e:expr64)
|
||||
: int64 =
|
||||
let checkdef kind name v inj =
|
||||
match v with
|
||||
None ->
|
||||
raise (Undef_sym (kind ^ " fixup '"
|
||||
^ name ^ "' undefined in eval64"))
|
||||
| Some x -> inj x
|
||||
in
|
||||
match e with
|
||||
IMM i -> i
|
||||
| ADD (a, b) -> Int64.add (eval64 a) (eval64 b)
|
||||
| SUB (a, b) -> Int64.sub (eval64 a) (eval64 b)
|
||||
| MUL (a, b) -> Int64.mul (eval64 a) (eval64 b)
|
||||
| DIV (a, b) -> Int64.div (eval64 a) (eval64 b)
|
||||
| REM (a, b) -> Int64.rem (eval64 a) (eval64 b)
|
||||
| MAX (a, b) -> i64_max (eval64 a) (eval64 b)
|
||||
| ALIGN (a, b) -> i64_align (eval64 a) (eval64 b)
|
||||
| SLL (a, b) -> Int64.shift_left (eval64 a) b
|
||||
| SLR (a, b) -> Int64.shift_right_logical (eval64 a) b
|
||||
| SAR (a, b) -> Int64.shift_right (eval64 a) b
|
||||
| AND (a, b) -> Int64.logand (eval64 a) (eval64 b)
|
||||
| XOR (a, b) -> Int64.logxor (eval64 a) (eval64 b)
|
||||
| OR (a, b) -> Int64.logor (eval64 a) (eval64 b)
|
||||
| NOT a -> Int64.lognot (eval64 a)
|
||||
| NEG a -> Int64.neg (eval64 a)
|
||||
| F_POS f ->
|
||||
checkdef "file position"
|
||||
f.fixup_name f.fixup_file_pos Int64.of_int
|
||||
| F_SZ f ->
|
||||
checkdef "file size"
|
||||
f.fixup_name f.fixup_file_sz Int64.of_int
|
||||
| M_POS f ->
|
||||
checkdef "mem position"
|
||||
f.fixup_name f.fixup_mem_pos (fun x -> x)
|
||||
| M_SZ f ->
|
||||
checkdef "mem size"
|
||||
f.fixup_name f.fixup_mem_sz (fun x -> x)
|
||||
| EXT e -> Int64.of_int32 (eval32 e)
|
||||
;;
|
||||
|
||||
|
||||
type frag =
|
||||
MARK (* MARK == 'PAD (IMM 0L)' *)
|
||||
| SEQ of frag array
|
||||
| PAD of int
|
||||
| BSS of int64
|
||||
| MEMPOS of int64
|
||||
| BYTE of int
|
||||
| BYTES of int array
|
||||
| CHAR of char
|
||||
| STRING of string
|
||||
| ZSTRING of string
|
||||
| ULEB128 of expr64
|
||||
| SLEB128 of expr64
|
||||
| WORD of (ty_mach * expr64)
|
||||
| ALIGN_FILE of (int * frag)
|
||||
| ALIGN_MEM of (int * frag)
|
||||
| DEF of (fixup * frag)
|
||||
| RELAX of relaxation
|
||||
|
||||
and relaxation =
|
||||
{ relax_options: frag array;
|
||||
relax_choice: int ref; }
|
||||
;;
|
||||
|
||||
exception Relax_more of relaxation;;
|
||||
|
||||
let new_relaxation (frags:frag array) =
|
||||
RELAX { relax_options = frags;
|
||||
relax_choice = ref ((Array.length frags) - 1); }
|
||||
;;
|
||||
|
||||
|
||||
let rec write_frag
|
||||
~(sess:Session.sess)
|
||||
~(lsb0:bool)
|
||||
~(buf:Buffer.t)
|
||||
~(frag:frag)
|
||||
: unit =
|
||||
let relax = Queue.create () in
|
||||
let bump_relax r =
|
||||
iflog sess (fun _ ->
|
||||
log sess "bumping relaxation to position %d"
|
||||
((!(r.relax_choice)) - 1));
|
||||
r.relax_choice := (!(r.relax_choice)) - 1;
|
||||
if !(r.relax_choice) < 0
|
||||
then bug () "relaxation ran out of options"
|
||||
in
|
||||
let rec loop _ =
|
||||
Queue.clear relax;
|
||||
Buffer.clear buf;
|
||||
resolve_frag_full relax frag;
|
||||
lower_frag ~sess ~lsb0 ~buf ~relax ~frag;
|
||||
if Queue.is_empty relax
|
||||
then ()
|
||||
else
|
||||
begin
|
||||
iflog sess (fun _ -> log sess "relaxing");
|
||||
Queue.iter bump_relax relax;
|
||||
loop ()
|
||||
end
|
||||
in
|
||||
loop ()
|
||||
|
||||
|
||||
and resolve_frag_full (relax:relaxation Queue.t) (frag:frag)
|
||||
: unit =
|
||||
let file_pos = ref 0 in
|
||||
let mem_pos = ref 0L in
|
||||
let bump i =
|
||||
mem_pos := Int64.add (!mem_pos) (Int64.of_int i);
|
||||
file_pos := (!file_pos) + i
|
||||
in
|
||||
|
||||
let uleb (e:expr64) : unit =
|
||||
let rec loop value =
|
||||
let value = Int64.shift_right_logical value 7 in
|
||||
if value = 0L
|
||||
then bump 1
|
||||
else
|
||||
begin
|
||||
bump 1;
|
||||
loop value
|
||||
end
|
||||
in
|
||||
loop (eval64 e)
|
||||
in
|
||||
|
||||
let sleb (e:expr64) : unit =
|
||||
let rec loop value =
|
||||
let byte = Int64.logand value 0xf7L in
|
||||
let value = Int64.shift_right value 7 in
|
||||
let signbit = Int64.logand byte 0x40L in
|
||||
if (((value = 0L) && (signbit = 0L)) ||
|
||||
((value = -1L) && (signbit = 0x40L)))
|
||||
then bump 1
|
||||
else
|
||||
begin
|
||||
bump 1;
|
||||
loop value
|
||||
end
|
||||
in
|
||||
loop (eval64 e)
|
||||
in
|
||||
let rec resolve_frag it =
|
||||
match it with
|
||||
| MARK -> ()
|
||||
| SEQ frags -> Array.iter resolve_frag frags
|
||||
| PAD i -> bump i
|
||||
| BSS i -> mem_pos := Int64.add (!mem_pos) i
|
||||
| MEMPOS i -> mem_pos := i
|
||||
| BYTE _ -> bump 1
|
||||
| BYTES ia -> bump (Array.length ia)
|
||||
| CHAR _ -> bump 1
|
||||
| STRING s -> bump (String.length s)
|
||||
| ZSTRING s -> bump ((String.length s) + 1)
|
||||
| ULEB128 e -> uleb e
|
||||
| SLEB128 e -> sleb e
|
||||
| WORD (mach,_) -> bump (bytes_of_ty_mach mach)
|
||||
| ALIGN_FILE (n, frag) ->
|
||||
let spill = (!file_pos) mod n in
|
||||
let pad = (n - spill) mod n in
|
||||
file_pos := (!file_pos) + pad;
|
||||
(*
|
||||
* NB: aligning the file *causes* likewise alignment of
|
||||
* memory, since we implement "file alignment" by
|
||||
* padding!
|
||||
*)
|
||||
mem_pos := Int64.add (!mem_pos) (Int64.of_int pad);
|
||||
resolve_frag frag
|
||||
|
||||
| ALIGN_MEM (n, frag) ->
|
||||
let n64 = Int64.of_int n in
|
||||
let spill = Int64.rem (!mem_pos) n64 in
|
||||
let pad = Int64.rem (Int64.sub n64 spill) n64 in
|
||||
mem_pos := Int64.add (!mem_pos) pad;
|
||||
resolve_frag frag
|
||||
|
||||
| DEF (f, i) ->
|
||||
let fpos1 = !file_pos in
|
||||
let mpos1 = !mem_pos in
|
||||
resolve_frag i;
|
||||
f.fixup_file_pos <- Some fpos1;
|
||||
f.fixup_mem_pos <- Some mpos1;
|
||||
f.fixup_file_sz <- Some ((!file_pos) - fpos1);
|
||||
f.fixup_mem_sz <- Some (Int64.sub (!mem_pos) mpos1)
|
||||
|
||||
| RELAX rel ->
|
||||
begin
|
||||
try
|
||||
resolve_frag rel.relax_options.(!(rel.relax_choice))
|
||||
with
|
||||
Bad_fit _ -> Queue.add rel relax
|
||||
end
|
||||
in
|
||||
resolve_frag frag
|
||||
|
||||
and lower_frag
|
||||
~(sess:Session.sess)
|
||||
~(lsb0:bool)
|
||||
~(buf:Buffer.t)
|
||||
~(relax:relaxation Queue.t)
|
||||
~(frag:frag)
|
||||
: unit =
|
||||
let byte (i:int) =
|
||||
if i < 0
|
||||
then raise (Bad_fit "byte underflow")
|
||||
else
|
||||
if i > 255
|
||||
then raise (Bad_fit "byte overflow")
|
||||
else Buffer.add_char buf (Char.chr i)
|
||||
in
|
||||
|
||||
let uleb (e:expr64) : unit =
|
||||
let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
|
||||
let rec loop value =
|
||||
let byte = Int64.logand value 0x7fL in
|
||||
let value = Int64.shift_right_logical value 7 in
|
||||
if value = 0L
|
||||
then emit1 byte
|
||||
else
|
||||
begin
|
||||
emit1 (Int64.logor byte 0x80L);
|
||||
loop value
|
||||
end
|
||||
in
|
||||
loop (eval64 e)
|
||||
in
|
||||
|
||||
let sleb (e:expr64) : unit =
|
||||
let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
|
||||
let rec loop value =
|
||||
let byte = Int64.logand value 0x7fL in
|
||||
let value = Int64.shift_right value 7 in
|
||||
let signbit = Int64.logand byte 0x40L in
|
||||
if (((value = 0L) && (signbit = 0L)) ||
|
||||
((value = -1L) && (signbit = 0x40L)))
|
||||
then emit1 byte
|
||||
else
|
||||
begin
|
||||
emit1 (Int64.logor byte 0x80L);
|
||||
loop value
|
||||
end
|
||||
in
|
||||
loop (eval64 e)
|
||||
in
|
||||
|
||||
let word (nbytes:int) (signed:bool) (e:expr64) =
|
||||
let i = eval64 e in
|
||||
|
||||
(*
|
||||
FIXME:
|
||||
|
||||
We should really base the entire assembler and memory-position
|
||||
system on Big_int.big_int, but in ocaml the big_int type lacks,
|
||||
oh, just about every useful function (no format string spec, no
|
||||
bitwise ops, blah blah) so it's useless; we're stuck on int64
|
||||
for bootstrapping.
|
||||
|
||||
For the time being we're just going to require you to represent
|
||||
those few unsigned 64 bit terms you have in mind via their
|
||||
signed bit pattern. Suboptimal but it's the best we can do.
|
||||
*)
|
||||
|
||||
let (top,bot) =
|
||||
if nbytes >= 8
|
||||
then
|
||||
if signed
|
||||
then (Int64.max_int,Int64.min_int)
|
||||
else (Int64.max_int,0L)
|
||||
else
|
||||
if signed
|
||||
then
|
||||
let bound = (Int64.shift_left 1L ((8 * nbytes) - 1)) in
|
||||
(Int64.sub bound 1L, Int64.neg bound)
|
||||
else
|
||||
let bound = (Int64.shift_left 1L (8 * nbytes)) in
|
||||
(Int64.sub bound 1L, 0L)
|
||||
in
|
||||
|
||||
let mask1 = Int64.logand 0xffL in
|
||||
let shift = Int64.shift_right_logical in
|
||||
let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
|
||||
if Int64.compare i bot = (-1)
|
||||
then raise (Bad_fit ("word underflow: "
|
||||
^ (Int64.to_string i)
|
||||
^ " into "
|
||||
^ (string_of_int nbytes)
|
||||
^ (if signed then " signed" else " unsigned")
|
||||
^ " bytes"))
|
||||
else
|
||||
if Int64.compare i top = 1
|
||||
then raise (Bad_fit ("word overflow: "
|
||||
^ (Int64.to_string i)
|
||||
^ " into "
|
||||
^ (string_of_int nbytes)
|
||||
^ (if signed then " signed" else " unsigned")
|
||||
^ " bytes"))
|
||||
else
|
||||
if lsb0
|
||||
then
|
||||
for n = 0 to (nbytes - 1) do
|
||||
emit1 (mask1 (shift i (8*n)))
|
||||
done
|
||||
else
|
||||
for n = (nbytes - 1) downto 0 do
|
||||
emit1 (mask1 (shift i (8*n)))
|
||||
done
|
||||
in
|
||||
match frag with
|
||||
MARK -> ()
|
||||
|
||||
| SEQ frags ->
|
||||
Array.iter
|
||||
begin
|
||||
fun frag ->
|
||||
lower_frag ~sess ~lsb0 ~buf ~relax ~frag
|
||||
end frags
|
||||
|
||||
| PAD c ->
|
||||
for i = 1 to c do
|
||||
Buffer.add_char buf '\x00'
|
||||
done
|
||||
|
||||
| BSS _ -> ()
|
||||
|
||||
| MEMPOS _ -> ()
|
||||
|
||||
| BYTE i -> byte i
|
||||
|
||||
| BYTES bs ->
|
||||
iflog sess (fun _ -> log sess "lowering %d bytes"
|
||||
(Array.length bs));
|
||||
Array.iter byte bs
|
||||
|
||||
| CHAR c ->
|
||||
iflog sess (fun _ -> log sess "lowering char: %c" c);
|
||||
Buffer.add_char buf c
|
||||
|
||||
| STRING s ->
|
||||
iflog sess (fun _ -> log sess "lowering string: %s" s);
|
||||
Buffer.add_string buf s
|
||||
|
||||
| ZSTRING s ->
|
||||
iflog sess (fun _ -> log sess "lowering zstring: %s" s);
|
||||
Buffer.add_string buf s;
|
||||
byte 0
|
||||
|
||||
| ULEB128 e -> uleb e
|
||||
| SLEB128 e -> sleb e
|
||||
|
||||
| WORD (m,e) ->
|
||||
iflog sess
|
||||
(fun _ ->
|
||||
log sess "lowering word %s"
|
||||
(string_of_ty_mach m));
|
||||
word (bytes_of_ty_mach m) (mach_is_signed m) e
|
||||
|
||||
| ALIGN_FILE (n, frag) ->
|
||||
let spill = (Buffer.length buf) mod n in
|
||||
let pad = (n - spill) mod n in
|
||||
for i = 1 to pad do
|
||||
Buffer.add_char buf '\x00'
|
||||
done;
|
||||
lower_frag sess lsb0 buf relax frag
|
||||
|
||||
| ALIGN_MEM (_, i) -> lower_frag sess lsb0 buf relax i
|
||||
| DEF (f, i) ->
|
||||
iflog sess (fun _ -> log sess "lowering fixup: %s" f.fixup_name);
|
||||
lower_frag sess lsb0 buf relax i;
|
||||
|
||||
| RELAX rel ->
|
||||
begin
|
||||
try
|
||||
lower_frag sess lsb0 buf relax
|
||||
rel.relax_options.(!(rel.relax_choice))
|
||||
with
|
||||
Bad_fit _ -> Queue.add rel relax
|
||||
end
|
||||
;;
|
||||
|
||||
let fold_flags (f:'a -> int64) (flags:'a list) : int64 =
|
||||
List.fold_left (Int64.logor) 0x0L (List.map f flags)
|
||||
;;
|
||||
|
||||
let write_out_frag sess lsb0 frag =
|
||||
let buf = Buffer.create 0xffff in
|
||||
let file = Session.filename_of sess.Session.sess_out in
|
||||
let out = open_out_bin file in
|
||||
write_frag ~sess ~lsb0 ~buf ~frag;
|
||||
Buffer.output_buffer out buf;
|
||||
flush out;
|
||||
close_out out;
|
||||
Unix.chmod file 0o755
|
||||
;;
|
||||
|
||||
(* Asm-reader stuff for loading info back from mapped files. *)
|
||||
(*
|
||||
* Unfortunately the ocaml Bigarray interface takes 'int' indices, so
|
||||
* f.e. can't do 64-bit offsets / files when running on a 32bit platform.
|
||||
* Despite the fact that we can possibly produce them. Sigh. Yet another
|
||||
* "bootstrap compiler limitation".
|
||||
*)
|
||||
type asm_reader =
|
||||
{
|
||||
asm_seek: int -> unit;
|
||||
asm_get_u32: unit -> int;
|
||||
asm_get_u16: unit -> int;
|
||||
asm_get_u8: unit -> int;
|
||||
asm_get_uleb: unit -> int;
|
||||
asm_get_zstr: unit -> string;
|
||||
asm_get_zstr_padded: int -> string;
|
||||
asm_get_off: unit -> int;
|
||||
asm_adv: int -> unit;
|
||||
asm_adv_u32: unit -> unit;
|
||||
asm_adv_u16: unit -> unit;
|
||||
asm_adv_u8: unit -> unit;
|
||||
asm_adv_zstr: unit -> unit;
|
||||
asm_close: unit -> unit;
|
||||
}
|
||||
;;
|
||||
|
||||
type mmap_arr =
|
||||
(int, Bigarray.int8_unsigned_elt, Bigarray.c_layout)
|
||||
Bigarray.Array1.t
|
||||
;;
|
||||
|
||||
let new_asm_reader (sess:Session.sess) (s:filename) : asm_reader =
|
||||
iflog sess (fun _ -> log sess "opening file %s" s);
|
||||
let fd = Unix.openfile s [ Unix.O_RDONLY ] 0 in
|
||||
let arr = (Bigarray.Array1.map_file
|
||||
fd ~pos:0L
|
||||
Bigarray.int8_unsigned
|
||||
Bigarray.c_layout
|
||||
false (-1))
|
||||
in
|
||||
let tmp = ref Nativeint.zero in
|
||||
let buf = Buffer.create 16 in
|
||||
let off = ref 0 in
|
||||
let is_open = ref true in
|
||||
let get_word_as_int (nbytes:int) : int =
|
||||
assert (!is_open);
|
||||
let lsb0 = true in
|
||||
tmp := Nativeint.zero;
|
||||
if lsb0
|
||||
then
|
||||
for j = nbytes-1 downto 0 do
|
||||
tmp := Nativeint.shift_left (!tmp) 8;
|
||||
tmp := Nativeint.logor (!tmp) (Nativeint.of_int arr.{(!off) + j})
|
||||
done
|
||||
else
|
||||
for j = 0 to nbytes-1 do
|
||||
tmp := Nativeint.shift_left (!tmp) 8;
|
||||
tmp := Nativeint.logor (!tmp) (Nativeint.of_int arr.{(!off) + j})
|
||||
done;
|
||||
off := (!off) + nbytes;
|
||||
Nativeint.to_int (!tmp)
|
||||
in
|
||||
let get_zstr_padded pad_opt =
|
||||
assert (!is_open);
|
||||
let i = ref (!off) in
|
||||
Buffer.clear buf;
|
||||
let buflen_ok _ =
|
||||
match pad_opt with
|
||||
None -> true
|
||||
| Some pad -> (Buffer.length buf) < pad
|
||||
in
|
||||
while arr.{!i} != 0 && (buflen_ok()) do
|
||||
Buffer.add_char buf (Char.chr arr.{!i});
|
||||
incr i
|
||||
done;
|
||||
begin
|
||||
match pad_opt with
|
||||
None -> off := (!off) + (Buffer.length buf) + 1
|
||||
| Some pad ->
|
||||
begin
|
||||
assert ((Buffer.length buf) <= pad);
|
||||
off := (!off) + pad
|
||||
end
|
||||
end;
|
||||
Buffer.contents buf
|
||||
in
|
||||
let bump i =
|
||||
assert (!is_open);
|
||||
off := (!off) + i
|
||||
in
|
||||
{
|
||||
asm_seek = (fun i -> off := i);
|
||||
asm_get_u32 = (fun _ -> get_word_as_int 4);
|
||||
asm_get_u16 = (fun _ -> get_word_as_int 2);
|
||||
asm_get_u8 = (fun _ -> get_word_as_int 1);
|
||||
asm_get_uleb =
|
||||
begin
|
||||
fun _ ->
|
||||
let rec loop result shift =
|
||||
let byte = arr.{!off} in
|
||||
incr off;
|
||||
let result = result lor ((byte land 0x7f) lsl shift) in
|
||||
if (byte land 0x80) = 0
|
||||
then result
|
||||
else loop result (shift+7)
|
||||
in
|
||||
loop 0 0
|
||||
end;
|
||||
asm_get_zstr = (fun _ -> get_zstr_padded None);
|
||||
asm_get_zstr_padded = (fun pad -> get_zstr_padded (Some pad));
|
||||
asm_get_off = (fun _ -> !off);
|
||||
asm_adv = bump;
|
||||
asm_adv_u32 = (fun _ -> bump 4);
|
||||
asm_adv_u16 = (fun _ -> bump 2);
|
||||
asm_adv_u8 = (fun _ -> bump 1);
|
||||
asm_adv_zstr = (fun _ -> while arr.{!off} != 0
|
||||
do incr off done);
|
||||
asm_close = (fun _ ->
|
||||
assert (!is_open);
|
||||
Unix.close fd;
|
||||
is_open := false)
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Metadata note-section encoding / decoding.
|
||||
*
|
||||
* Since the only object format that defines a "note" section at all is
|
||||
* ELF, we model the contents of the metadata section on ELF's
|
||||
* notes. But the same blob of data is stuck into PE and Mach-O files
|
||||
* too.
|
||||
*
|
||||
* The format is essentially just the ELF note format:
|
||||
*
|
||||
* <un-padded-size-of-name:u32>
|
||||
* <size-of-desc:u32>
|
||||
* <type-code=0:u32>
|
||||
* <name="rust":zstr>
|
||||
* <0-pad to 4-byte boundary>
|
||||
* <n=meta-count:u32>
|
||||
* <k1:zstr> <v1:zstr>
|
||||
* ...
|
||||
* <kn:zstr> <vn:zstr>
|
||||
* <0-pad to 4-byte boundary>
|
||||
*
|
||||
*)
|
||||
let note_rust_frags (meta:(Ast.ident * string) array) : frag =
|
||||
let desc_fixup = new_fixup ".rust.note metadata" in
|
||||
let desc =
|
||||
DEF (desc_fixup,
|
||||
SEQ [|
|
||||
WORD (TY_u32, IMM (Int64.of_int (Array.length meta)));
|
||||
SEQ (Array.map
|
||||
(fun (k,v) -> SEQ [| ZSTRING k; ZSTRING v; |])
|
||||
meta);
|
||||
ALIGN_FILE (4, MARK) |])
|
||||
in
|
||||
let name = "rust" in
|
||||
let ty = 0L in
|
||||
let padded_name = SEQ [| ZSTRING name;
|
||||
ALIGN_FILE (4, MARK) |]
|
||||
in
|
||||
let name_sz = IMM (Int64.of_int ((String.length name) + 1)) in
|
||||
SEQ [| WORD (TY_u32, name_sz);
|
||||
WORD (TY_u32, F_SZ desc_fixup);
|
||||
WORD (TY_u32, IMM ty);
|
||||
padded_name;
|
||||
desc;|]
|
||||
;;
|
||||
|
||||
let read_rust_note (ar:asm_reader) : (Ast.ident * string) array =
|
||||
ar.asm_adv_u32 ();
|
||||
ar.asm_adv_u32 ();
|
||||
assert ((ar.asm_get_u32 ()) = 0);
|
||||
let rust_name = ar.asm_get_zstr_padded 8 in
|
||||
assert (rust_name = "rust");
|
||||
let n = ar.asm_get_u32() in
|
||||
let meta = Queue.create () in
|
||||
for i = 1 to n
|
||||
do
|
||||
let k = ar.asm_get_zstr() in
|
||||
let v = ar.asm_get_zstr() in
|
||||
Queue.add (k,v) meta
|
||||
done;
|
||||
queue_to_arr meta
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1760
src/boot/be/elf.ml
Normal file
1760
src/boot/be/elf.ml
Normal file
File diff suppressed because it is too large
Load Diff
1135
src/boot/be/il.ml
Normal file
1135
src/boot/be/il.ml
Normal file
File diff suppressed because it is too large
Load Diff
1184
src/boot/be/macho.ml
Normal file
1184
src/boot/be/macho.ml
Normal file
File diff suppressed because it is too large
Load Diff
1149
src/boot/be/pe.ml
Normal file
1149
src/boot/be/pe.ml
Normal file
File diff suppressed because it is too large
Load Diff
664
src/boot/be/ra.ml
Normal file
664
src/boot/be/ra.ml
Normal file
@ -0,0 +1,664 @@
|
||||
open Il;;
|
||||
open Common;;
|
||||
|
||||
type ctxt =
|
||||
{
|
||||
ctxt_sess: Session.sess;
|
||||
ctxt_n_vregs: int;
|
||||
ctxt_abi: Abi.abi;
|
||||
mutable ctxt_quads: Il.quads;
|
||||
mutable ctxt_next_spill: int;
|
||||
mutable ctxt_next_label: int;
|
||||
(* More state as necessary. *)
|
||||
}
|
||||
;;
|
||||
|
||||
let new_ctxt
|
||||
(sess:Session.sess)
|
||||
(quads:Il.quads)
|
||||
(vregs:int)
|
||||
(abi:Abi.abi)
|
||||
: ctxt =
|
||||
{
|
||||
ctxt_sess = sess;
|
||||
ctxt_quads = quads;
|
||||
ctxt_n_vregs = vregs;
|
||||
ctxt_abi = abi;
|
||||
ctxt_next_spill = 0;
|
||||
ctxt_next_label = 0;
|
||||
}
|
||||
;;
|
||||
|
||||
let log (cx:ctxt) =
|
||||
Session.log "ra"
|
||||
cx.ctxt_sess.Session.sess_log_ra
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog (cx:ctxt) (thunk:(unit -> unit)) : unit =
|
||||
if cx.ctxt_sess.Session.sess_log_ra
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
let list_to_str list eltstr =
|
||||
(String.concat "," (List.map eltstr (List.sort compare list)))
|
||||
;;
|
||||
|
||||
let next_spill (cx:ctxt) : int =
|
||||
let i = cx.ctxt_next_spill in
|
||||
cx.ctxt_next_spill <- i + 1;
|
||||
i
|
||||
;;
|
||||
|
||||
let next_label (cx:ctxt) : string =
|
||||
let i = cx.ctxt_next_label in
|
||||
cx.ctxt_next_label <- i + 1;
|
||||
(".L" ^ (string_of_int i))
|
||||
;;
|
||||
|
||||
exception Ra_error of string ;;
|
||||
|
||||
let convert_labels (cx:ctxt) : unit =
|
||||
let quad_fixups = Array.map (fun q -> q.quad_fixup) cx.ctxt_quads in
|
||||
let qp_code (_:Il.quad_processor) (c:Il.code) : Il.code =
|
||||
match c with
|
||||
Il.CodeLabel lab ->
|
||||
let fix =
|
||||
match quad_fixups.(lab) with
|
||||
None ->
|
||||
let fix = new_fixup (next_label cx) in
|
||||
begin
|
||||
quad_fixups.(lab) <- Some fix;
|
||||
fix
|
||||
end
|
||||
| Some f -> f
|
||||
in
|
||||
Il.CodePtr (Il.ImmPtr (fix, Il.CodeTy))
|
||||
| _ -> c
|
||||
in
|
||||
let qp = { Il.identity_processor
|
||||
with Il.qp_code = qp_code }
|
||||
in
|
||||
Il.rewrite_quads qp cx.ctxt_quads;
|
||||
Array.iteri (fun i fix ->
|
||||
cx.ctxt_quads.(i) <- { cx.ctxt_quads.(i) with
|
||||
Il.quad_fixup = fix })
|
||||
quad_fixups;
|
||||
;;
|
||||
|
||||
let convert_pre_spills
|
||||
(cx:ctxt)
|
||||
(mkspill:(Il.spill -> Il.mem))
|
||||
: int =
|
||||
let n = ref 0 in
|
||||
let qp_mem (_:Il.quad_processor) (a:Il.mem) : Il.mem =
|
||||
match a with
|
||||
Il.Spill i ->
|
||||
begin
|
||||
if i+1 > (!n)
|
||||
then n := i+1;
|
||||
mkspill i
|
||||
end
|
||||
| _ -> a
|
||||
in
|
||||
let qp = Il.identity_processor in
|
||||
let qp = { qp with
|
||||
Il.qp_mem = qp_mem }
|
||||
in
|
||||
begin
|
||||
Il.rewrite_quads qp cx.ctxt_quads;
|
||||
!n
|
||||
end
|
||||
;;
|
||||
|
||||
let kill_quad (i:int) (cx:ctxt) : unit =
|
||||
cx.ctxt_quads.(i) <-
|
||||
{ Il.deadq with
|
||||
Il.quad_fixup = cx.ctxt_quads.(i).Il.quad_fixup }
|
||||
;;
|
||||
|
||||
let kill_redundant_moves (cx:ctxt) : unit =
|
||||
let process_quad i q =
|
||||
match q.Il.quad_body with
|
||||
Il.Unary u when
|
||||
((Il.is_mov u.Il.unary_op) &&
|
||||
(Il.Cell u.Il.unary_dst) = u.Il.unary_src) ->
|
||||
kill_quad i cx
|
||||
| _ -> ()
|
||||
in
|
||||
Array.iteri process_quad cx.ctxt_quads
|
||||
;;
|
||||
|
||||
let quad_jump_target_labels (q:quad) : Il.label list =
|
||||
let explicits =
|
||||
match q.Il.quad_body with
|
||||
Il.Jmp { Il.jmp_targ = Il.CodeLabel lab } -> [ lab ]
|
||||
| _ -> []
|
||||
in
|
||||
explicits @ q.quad_implicits;
|
||||
;;
|
||||
|
||||
let quad_used_vregs (q:quad) : Il.vreg list =
|
||||
let vregs = ref [] in
|
||||
let qp_reg _ r =
|
||||
match r with
|
||||
Il.Vreg v -> (vregs := (v :: (!vregs)); r)
|
||||
| _ -> r
|
||||
in
|
||||
let qp_cell_write qp c =
|
||||
match c with
|
||||
Il.Reg _ -> c
|
||||
| Il.Mem (a, b) -> Il.Mem (qp.qp_mem qp a, b)
|
||||
in
|
||||
let qp = { Il.identity_processor with
|
||||
Il.qp_reg = qp_reg;
|
||||
Il.qp_cell_write = qp_cell_write }
|
||||
in
|
||||
ignore (Il.process_quad qp q);
|
||||
!vregs
|
||||
;;
|
||||
|
||||
let quad_defined_vregs (q:quad) : Il.vreg list =
|
||||
let vregs = ref [] in
|
||||
let qp_cell_write _ c =
|
||||
match c with
|
||||
Il.Reg (Il.Vreg v, _) -> (vregs := (v :: (!vregs)); c)
|
||||
| _ -> c
|
||||
in
|
||||
let qp = { Il.identity_processor with
|
||||
Il.qp_cell_write = qp_cell_write }
|
||||
in
|
||||
ignore (Il.process_quad qp q);
|
||||
!vregs
|
||||
;;
|
||||
|
||||
let quad_is_unconditional_jump (q:quad) : bool =
|
||||
match q.Il.quad_body with
|
||||
Il.Jmp { jmp_op = Il.JMP } -> true
|
||||
| Il.Ret -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let calculate_live_bitvectors
|
||||
(cx:ctxt)
|
||||
: ((Bits.t array) * (Bits.t array)) =
|
||||
|
||||
log cx "calculating live bitvectors";
|
||||
|
||||
let quads = cx.ctxt_quads in
|
||||
let n_quads = Array.length quads in
|
||||
let n_vregs = cx.ctxt_n_vregs in
|
||||
let new_bitv _ = Bits.create n_vregs false in
|
||||
let (live_in_vregs:Bits.t array) = Array.init n_quads new_bitv in
|
||||
let (live_out_vregs:Bits.t array) = Array.init n_quads new_bitv in
|
||||
|
||||
let (quad_used_vrs:Bits.t array) = Array.init n_quads new_bitv in
|
||||
let (quad_defined_vrs:Bits.t array) = Array.init n_quads new_bitv in
|
||||
let (quad_uncond_jmp:bool array) = Array.make n_quads false in
|
||||
let (quad_jmp_targs:(Il.label list) array) = Array.make n_quads [] in
|
||||
|
||||
let outer_changed = ref true in
|
||||
|
||||
(* Working bit-vector. *)
|
||||
let scratch = new_bitv() in
|
||||
|
||||
(* bit-vector helpers. *)
|
||||
(* Setup pass. *)
|
||||
for i = 0 to n_quads - 1 do
|
||||
let q = quads.(i) in
|
||||
quad_uncond_jmp.(i) <- quad_is_unconditional_jump q;
|
||||
quad_jmp_targs.(i) <- quad_jump_target_labels q;
|
||||
List.iter
|
||||
(fun v -> Bits.set quad_used_vrs.(i) v true)
|
||||
(quad_used_vregs q);
|
||||
List.iter
|
||||
(fun v -> Bits.set quad_defined_vrs.(i) v true)
|
||||
(quad_defined_vregs q)
|
||||
done;
|
||||
|
||||
while !outer_changed do
|
||||
iflog cx (fun _ -> log cx "iterating outer bitvector calculation");
|
||||
outer_changed := false;
|
||||
for i = 0 to n_quads - 1 do
|
||||
Bits.clear live_in_vregs.(i);
|
||||
Bits.clear live_out_vregs.(i)
|
||||
done;
|
||||
let inner_changed = ref true in
|
||||
while !inner_changed do
|
||||
inner_changed := false;
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
log cx "iterating inner bitvector calculation over %d quads"
|
||||
n_quads);
|
||||
for i = n_quads - 1 downto 0 do
|
||||
|
||||
let note_change b = if b then inner_changed := true in
|
||||
let live_in = live_in_vregs.(i) in
|
||||
let live_out = live_out_vregs.(i) in
|
||||
let used = quad_used_vrs.(i) in
|
||||
let defined = quad_defined_vrs.(i) in
|
||||
|
||||
(* Union in the vregs we use. *)
|
||||
note_change (Bits.union live_in used);
|
||||
|
||||
(* Union in all our jump targets. *)
|
||||
List.iter
|
||||
(fun i -> note_change (Bits.union live_out live_in_vregs.(i)))
|
||||
(quad_jmp_targs.(i));
|
||||
|
||||
(* Union in our block successor if we have one *)
|
||||
if i < (n_quads - 1) && (not (quad_uncond_jmp.(i)))
|
||||
then note_change (Bits.union live_out live_in_vregs.(i+1));
|
||||
|
||||
(* Propagate live-out to live-in on anything we don't define. *)
|
||||
ignore (Bits.copy scratch defined);
|
||||
Bits.invert scratch;
|
||||
ignore (Bits.intersect scratch live_out);
|
||||
note_change (Bits.union live_in scratch);
|
||||
|
||||
done
|
||||
done;
|
||||
let kill_mov_to_dead_target i q =
|
||||
match q.Il.quad_body with
|
||||
Il.Unary { Il.unary_op=uop;
|
||||
Il.unary_dst=Il.Reg (Il.Vreg v, _) }
|
||||
when
|
||||
((Il.is_mov uop) &&
|
||||
not (Bits.get live_out_vregs.(i) v)) ->
|
||||
begin
|
||||
kill_quad i cx;
|
||||
outer_changed := true;
|
||||
end
|
||||
| _ -> ()
|
||||
in
|
||||
Array.iteri kill_mov_to_dead_target quads
|
||||
done;
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
log cx "finished calculating live bitvectors";
|
||||
log cx "=========================";
|
||||
for q = 0 to n_quads - 1 do
|
||||
let buf = Buffer.create 128 in
|
||||
for v = 0 to (n_vregs - 1)
|
||||
do
|
||||
if ((Bits.get live_in_vregs.(q) v)
|
||||
&& (Bits.get live_out_vregs.(q) v))
|
||||
then Printf.bprintf buf " %-2d" v
|
||||
else Buffer.add_string buf " "
|
||||
done;
|
||||
log cx "[%6d] live vregs: %s" q (Buffer.contents buf)
|
||||
done;
|
||||
log cx "========================="
|
||||
end;
|
||||
(live_in_vregs, live_out_vregs)
|
||||
;;
|
||||
|
||||
|
||||
let is_end_of_basic_block (q:quad) : bool =
|
||||
match q.Il.quad_body with
|
||||
Il.Jmp _ -> true
|
||||
| Il.Ret -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let is_beginning_of_basic_block (q:quad) : bool =
|
||||
match q.Il.quad_fixup with
|
||||
None -> false
|
||||
| Some _ -> true
|
||||
;;
|
||||
|
||||
let dump_quads cx =
|
||||
let f = cx.ctxt_abi.Abi.abi_str_of_hardreg in
|
||||
let len = (Array.length cx.ctxt_quads) - 1 in
|
||||
let ndigits_of n = (int_of_float (log10 (float_of_int n))) in
|
||||
let padded_num n maxnum =
|
||||
let ndigits = ndigits_of n in
|
||||
let maxdigits = ndigits_of maxnum in
|
||||
let pad = String.make (maxdigits - ndigits) ' ' in
|
||||
Printf.sprintf "%s%d" pad n
|
||||
in
|
||||
let padded_str str maxlen =
|
||||
let pad = String.make (maxlen - (String.length str)) ' ' in
|
||||
Printf.sprintf "%s%s" pad str
|
||||
in
|
||||
let maxlablen = ref 0 in
|
||||
for i = 0 to len
|
||||
do
|
||||
let q = cx.ctxt_quads.(i) in
|
||||
match q.quad_fixup with
|
||||
None -> ()
|
||||
| Some f ->
|
||||
maxlablen := max (!maxlablen) ((String.length f.fixup_name) + 1)
|
||||
done;
|
||||
for i = 0 to len
|
||||
do
|
||||
let q = cx.ctxt_quads.(i) in
|
||||
let qs = (string_of_quad f q) in
|
||||
let lab = match q.quad_fixup with
|
||||
None -> ""
|
||||
| Some f -> f.fixup_name ^ ":"
|
||||
in
|
||||
log cx "[%s] %s %s" (padded_num i len) (padded_str lab (!maxlablen)) qs
|
||||
done
|
||||
;;
|
||||
|
||||
let calculate_vreg_constraints (cx:ctxt) : Bits.t array =
|
||||
let abi = cx.ctxt_abi in
|
||||
let n_vregs = cx.ctxt_n_vregs in
|
||||
let n_hregs = abi.Abi.abi_n_hardregs in
|
||||
let constraints = Array.init n_vregs (fun _ -> Bits.create n_hregs true) in
|
||||
Array.iteri
|
||||
begin
|
||||
fun i q ->
|
||||
abi.Abi.abi_constrain_vregs q constraints;
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
let hr_str = cx.ctxt_abi.Abi.abi_str_of_hardreg in
|
||||
log cx "constraints for quad %d = %s"
|
||||
i (string_of_quad hr_str q);
|
||||
let qp_reg _ r =
|
||||
begin
|
||||
match r with
|
||||
Il.Hreg _ -> ()
|
||||
| Il.Vreg v ->
|
||||
let hregs = Bits.to_list constraints.(v) in
|
||||
log cx "<v%d> constrained to hregs: [%s]"
|
||||
v (list_to_str hregs hr_str)
|
||||
end;
|
||||
r
|
||||
in
|
||||
ignore (Il.process_quad { Il.identity_processor with
|
||||
Il.qp_reg = qp_reg } q)
|
||||
end;
|
||||
end
|
||||
cx.ctxt_quads;
|
||||
constraints
|
||||
;;
|
||||
|
||||
(* Simple local register allocator. Nothing fancy. *)
|
||||
let reg_alloc
|
||||
(sess:Session.sess)
|
||||
(quads:Il.quads)
|
||||
(vregs:int)
|
||||
(abi:Abi.abi) =
|
||||
try
|
||||
let cx = new_ctxt sess quads vregs abi in
|
||||
let _ =
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
log cx "un-allocated quads:";
|
||||
dump_quads cx
|
||||
end
|
||||
in
|
||||
|
||||
(* Work out pre-spilled slots and allocate 'em. *)
|
||||
let spill_slot (s:Il.spill) = abi.Abi.abi_spill_slot s in
|
||||
let n_pre_spills = convert_pre_spills cx spill_slot in
|
||||
|
||||
let (live_in_vregs, live_out_vregs) =
|
||||
Session.time_inner "RA liveness" sess
|
||||
(fun _ -> calculate_live_bitvectors cx)
|
||||
in
|
||||
let (vreg_constraints:Bits.t array) = (* vreg idx -> hreg bits.t *)
|
||||
calculate_vreg_constraints cx
|
||||
in
|
||||
let inactive_hregs = ref [] in (* [hreg] *)
|
||||
let active_hregs = ref [] in (* [hreg] *)
|
||||
let dirty_vregs = Hashtbl.create 0 in (* vreg -> () *)
|
||||
let hreg_to_vreg = Hashtbl.create 0 in (* hreg -> vreg *)
|
||||
let vreg_to_hreg = Hashtbl.create 0 in (* vreg -> hreg *)
|
||||
let vreg_to_spill = Hashtbl.create 0 in (* vreg -> spill *)
|
||||
let (word_ty:Il.scalar_ty) = Il.ValTy abi.Abi.abi_word_bits in
|
||||
let vreg_spill_cell v =
|
||||
Il.Mem ((spill_slot (Hashtbl.find vreg_to_spill v)),
|
||||
Il.ScalarTy word_ty)
|
||||
in
|
||||
let newq = ref [] in
|
||||
let fixup = ref None in
|
||||
let prepend q =
|
||||
newq := {q with quad_fixup = !fixup} :: (!newq);
|
||||
fixup := None
|
||||
in
|
||||
let hr h = Il.Reg (Il.Hreg h, Il.voidptr_t) in
|
||||
let hr_str = cx.ctxt_abi.Abi.abi_str_of_hardreg in
|
||||
let clean_hreg i hreg =
|
||||
if (Hashtbl.mem hreg_to_vreg hreg) &&
|
||||
(hreg < cx.ctxt_abi.Abi.abi_n_hardregs)
|
||||
then
|
||||
let vreg = Hashtbl.find hreg_to_vreg hreg in
|
||||
if Hashtbl.mem dirty_vregs vreg
|
||||
then
|
||||
begin
|
||||
Hashtbl.remove dirty_vregs vreg;
|
||||
if (Bits.get (live_out_vregs.(i)) vreg)
|
||||
then
|
||||
let spill_idx =
|
||||
if Hashtbl.mem vreg_to_spill vreg
|
||||
then Hashtbl.find vreg_to_spill vreg
|
||||
else
|
||||
begin
|
||||
let s = next_spill cx in
|
||||
Hashtbl.replace vreg_to_spill vreg s;
|
||||
s
|
||||
end
|
||||
in
|
||||
let spill_mem = spill_slot spill_idx in
|
||||
let spill_cell = Il.Mem (spill_mem, Il.ScalarTy word_ty) in
|
||||
log cx "spilling <%d> from %s to %s"
|
||||
vreg (hr_str hreg) (string_of_mem hr_str spill_mem);
|
||||
prepend (Il.mk_quad
|
||||
(Il.umov spill_cell (Il.Cell (hr hreg))));
|
||||
else ()
|
||||
end
|
||||
else ()
|
||||
else ()
|
||||
in
|
||||
|
||||
let inactivate_hreg hreg =
|
||||
if (Hashtbl.mem hreg_to_vreg hreg) &&
|
||||
(hreg < cx.ctxt_abi.Abi.abi_n_hardregs)
|
||||
then
|
||||
let vreg = Hashtbl.find hreg_to_vreg hreg in
|
||||
Hashtbl.remove vreg_to_hreg vreg;
|
||||
Hashtbl.remove hreg_to_vreg hreg;
|
||||
active_hregs := List.filter (fun x -> x != hreg) (!active_hregs);
|
||||
inactive_hregs := hreg :: (!inactive_hregs);
|
||||
else ()
|
||||
in
|
||||
|
||||
let spill_specific_hreg i hreg =
|
||||
clean_hreg i hreg;
|
||||
inactivate_hreg hreg
|
||||
in
|
||||
|
||||
let rec select_constrained
|
||||
(constraints:Bits.t)
|
||||
(hregs:Il.hreg list)
|
||||
: Il.hreg option =
|
||||
match hregs with
|
||||
[] -> None
|
||||
| h::hs ->
|
||||
if Bits.get constraints h
|
||||
then Some h
|
||||
else select_constrained constraints hs
|
||||
in
|
||||
|
||||
let spill_constrained constrs i =
|
||||
match select_constrained constrs (!active_hregs) with
|
||||
None ->
|
||||
raise (Ra_error ("unable to spill according to constraint"));
|
||||
| Some h ->
|
||||
begin
|
||||
spill_specific_hreg i h;
|
||||
h
|
||||
end
|
||||
in
|
||||
|
||||
let all_hregs = Bits.create abi.Abi.abi_n_hardregs true in
|
||||
|
||||
let spill_all_regs i =
|
||||
while (!active_hregs) != []
|
||||
do
|
||||
let _ = spill_constrained all_hregs i in
|
||||
()
|
||||
done
|
||||
in
|
||||
|
||||
let reload vreg hreg =
|
||||
if Hashtbl.mem vreg_to_spill vreg
|
||||
then
|
||||
prepend (Il.mk_quad
|
||||
(Il.umov
|
||||
(hr hreg)
|
||||
(Il.Cell (vreg_spill_cell vreg))))
|
||||
else ()
|
||||
in
|
||||
|
||||
let use_vreg def i vreg =
|
||||
if Hashtbl.mem vreg_to_hreg vreg
|
||||
then
|
||||
begin
|
||||
let h = Hashtbl.find vreg_to_hreg vreg in
|
||||
iflog cx (fun _ -> log cx "found cached assignment %s for <v%d>"
|
||||
(hr_str h) vreg);
|
||||
h
|
||||
end
|
||||
else
|
||||
let hreg =
|
||||
let constrs = vreg_constraints.(vreg) in
|
||||
match select_constrained constrs (!inactive_hregs) with
|
||||
None ->
|
||||
let h = spill_constrained constrs i in
|
||||
iflog cx
|
||||
(fun _ -> log cx "selected %s to spill and use for <v%d>"
|
||||
(hr_str h) vreg);
|
||||
h
|
||||
| Some h ->
|
||||
iflog cx (fun _ -> log cx "selected inactive %s for <v%d>"
|
||||
(hr_str h) vreg);
|
||||
h
|
||||
in
|
||||
inactive_hregs :=
|
||||
List.filter (fun x -> x != hreg) (!inactive_hregs);
|
||||
active_hregs := (!active_hregs) @ [hreg];
|
||||
Hashtbl.replace hreg_to_vreg hreg vreg;
|
||||
Hashtbl.replace vreg_to_hreg vreg hreg;
|
||||
if def
|
||||
then ()
|
||||
else
|
||||
reload vreg hreg;
|
||||
hreg
|
||||
in
|
||||
let qp_reg def i _ r =
|
||||
match r with
|
||||
Il.Hreg h -> (spill_specific_hreg i h; r)
|
||||
| Il.Vreg v -> (Il.Hreg (use_vreg def i v))
|
||||
in
|
||||
let qp_cell def i qp c =
|
||||
match c with
|
||||
Il.Reg (r, b) -> Il.Reg (qp_reg def i qp r, b)
|
||||
| Il.Mem (a, b) ->
|
||||
let qp = { qp with Il.qp_reg = qp_reg false i } in
|
||||
Il.Mem (qp.qp_mem qp a, b)
|
||||
in
|
||||
let qp i = { Il.identity_processor with
|
||||
Il.qp_cell_read = qp_cell false i;
|
||||
Il.qp_cell_write = qp_cell true i;
|
||||
Il.qp_reg = qp_reg false i }
|
||||
in
|
||||
cx.ctxt_next_spill <- n_pre_spills;
|
||||
convert_labels cx;
|
||||
for i = 0 to cx.ctxt_abi.Abi.abi_n_hardregs - 1
|
||||
do
|
||||
inactive_hregs := i :: (!inactive_hregs)
|
||||
done;
|
||||
for i = 0 to (Array.length cx.ctxt_quads) - 1
|
||||
do
|
||||
let quad = cx.ctxt_quads.(i) in
|
||||
let clobbers = cx.ctxt_abi.Abi.abi_clobbers quad in
|
||||
let used = quad_used_vregs quad in
|
||||
let defined = quad_defined_vregs quad in
|
||||
begin
|
||||
if List.exists (fun def -> List.mem def clobbers) defined
|
||||
then raise (Ra_error ("clobber and defined sets overlap"));
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
let hr (v:int) : string =
|
||||
if Hashtbl.mem vreg_to_hreg v
|
||||
then hr_str (Hashtbl.find vreg_to_hreg v)
|
||||
else "??"
|
||||
in
|
||||
let vr_str (v:int) : string =
|
||||
Printf.sprintf "v%d=%s" v (hr v)
|
||||
in
|
||||
let lstr lab ls fn =
|
||||
if List.length ls = 0
|
||||
then ()
|
||||
else log cx "\t%s: [%s]" lab (list_to_str ls fn)
|
||||
in
|
||||
log cx "processing quad %d = %s"
|
||||
i (string_of_quad hr_str quad);
|
||||
(lstr "dirt" (htab_keys dirty_vregs) vr_str);
|
||||
(lstr "clob" clobbers hr_str);
|
||||
(lstr "in" (Bits.to_list live_in_vregs.(i)) vr_str);
|
||||
(lstr "out" (Bits.to_list live_out_vregs.(i)) vr_str);
|
||||
(lstr "use" used vr_str);
|
||||
(lstr "def" defined vr_str);
|
||||
end;
|
||||
List.iter (clean_hreg i) clobbers;
|
||||
if is_beginning_of_basic_block quad
|
||||
then
|
||||
begin
|
||||
spill_all_regs i;
|
||||
fixup := quad.quad_fixup;
|
||||
prepend (Il.process_quad (qp i) quad)
|
||||
end
|
||||
else
|
||||
begin
|
||||
fixup := quad.quad_fixup;
|
||||
let newq = (Il.process_quad (qp i) quad) in
|
||||
begin
|
||||
if is_end_of_basic_block quad
|
||||
then spill_all_regs i
|
||||
else ()
|
||||
end;
|
||||
prepend newq
|
||||
end
|
||||
end;
|
||||
List.iter inactivate_hreg clobbers;
|
||||
List.iter (fun i -> Hashtbl.replace dirty_vregs i ()) defined;
|
||||
done;
|
||||
cx.ctxt_quads <- Array.of_list (List.rev (!newq));
|
||||
kill_redundant_moves cx;
|
||||
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
log cx "spills: %d pre-spilled, %d total"
|
||||
n_pre_spills cx.ctxt_next_spill;
|
||||
log cx "register-allocated quads:";
|
||||
dump_quads cx;
|
||||
end;
|
||||
(cx.ctxt_quads, cx.ctxt_next_spill)
|
||||
|
||||
with
|
||||
Ra_error s ->
|
||||
Session.fail sess "RA Error: %s" s;
|
||||
(quads, 0)
|
||||
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
2205
src/boot/be/x86.ml
Normal file
2205
src/boot/be/x86.ml
Normal file
File diff suppressed because it is too large
Load Diff
232
src/boot/driver/lib.ml
Normal file
232
src/boot/driver/lib.ml
Normal file
@ -0,0 +1,232 @@
|
||||
open Common;;
|
||||
|
||||
let log (sess:Session.sess) =
|
||||
Session.log "lib"
|
||||
sess.Session.sess_log_lib
|
||||
sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
|
||||
if sess.Session.sess_log_lib
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
(* FIXME: move these to sess. *)
|
||||
let ar_cache = Hashtbl.create 0 ;;
|
||||
let sects_cache = Hashtbl.create 0;;
|
||||
let meta_cache = Hashtbl.create 0;;
|
||||
let die_cache = Hashtbl.create 0;;
|
||||
|
||||
let get_ar
|
||||
(sess:Session.sess)
|
||||
(filename:filename)
|
||||
: Asm.asm_reader option =
|
||||
htab_search_or_add ar_cache filename
|
||||
begin
|
||||
fun _ ->
|
||||
let sniff =
|
||||
match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> Pe.sniff
|
||||
| MacOS_x86_macho -> Macho.sniff
|
||||
| Linux_x86_elf -> Elf.sniff
|
||||
in
|
||||
sniff sess filename
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
let get_sects
|
||||
(sess:Session.sess)
|
||||
(filename:filename) :
|
||||
(Asm.asm_reader * ((string,(int*int)) Hashtbl.t)) option =
|
||||
htab_search_or_add sects_cache filename
|
||||
begin
|
||||
fun _ ->
|
||||
match get_ar sess filename with
|
||||
None -> None
|
||||
| Some ar ->
|
||||
let get_sections =
|
||||
match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> Pe.get_sections
|
||||
| MacOS_x86_macho -> Macho.get_sections
|
||||
| Linux_x86_elf -> Elf.get_sections
|
||||
in
|
||||
Some (ar, (get_sections sess ar))
|
||||
end
|
||||
;;
|
||||
|
||||
let get_meta
|
||||
(sess:Session.sess)
|
||||
(filename:filename)
|
||||
: Ast.meta option =
|
||||
htab_search_or_add meta_cache filename
|
||||
begin
|
||||
fun _ ->
|
||||
match get_sects sess filename with
|
||||
None -> None
|
||||
| Some (ar, sects) ->
|
||||
match htab_search sects ".note.rust" with
|
||||
Some (off, _) ->
|
||||
ar.Asm.asm_seek off;
|
||||
Some (Asm.read_rust_note ar)
|
||||
| None -> None
|
||||
end
|
||||
;;
|
||||
|
||||
let get_dies_opt
|
||||
(sess:Session.sess)
|
||||
(filename:filename)
|
||||
: (Dwarf.rooted_dies option) =
|
||||
htab_search_or_add die_cache filename
|
||||
begin
|
||||
fun _ ->
|
||||
match get_sects sess filename with
|
||||
None -> None
|
||||
| Some (ar, sects) ->
|
||||
let debug_abbrev = Hashtbl.find sects ".debug_abbrev" in
|
||||
let debug_info = Hashtbl.find sects ".debug_info" in
|
||||
let abbrevs = Dwarf.read_abbrevs sess ar debug_abbrev in
|
||||
let dies = Dwarf.read_dies sess ar debug_info abbrevs in
|
||||
ar.Asm.asm_close ();
|
||||
Hashtbl.remove ar_cache filename;
|
||||
Some dies
|
||||
end
|
||||
;;
|
||||
|
||||
let get_dies
|
||||
(sess:Session.sess)
|
||||
(filename:filename)
|
||||
: Dwarf.rooted_dies =
|
||||
match get_dies_opt sess filename with
|
||||
None ->
|
||||
Printf.fprintf stderr "Error: bad crate file: %s\n%!" filename;
|
||||
exit 1
|
||||
| Some dies -> dies
|
||||
;;
|
||||
|
||||
let get_file_mod
|
||||
(sess:Session.sess)
|
||||
(abi:Abi.abi)
|
||||
(filename:filename)
|
||||
(nref:node_id ref)
|
||||
(oref:opaque_id ref)
|
||||
: Ast.mod_items =
|
||||
let dies = get_dies sess filename in
|
||||
let items = Hashtbl.create 0 in
|
||||
Dwarf.extract_mod_items nref oref abi items dies;
|
||||
items
|
||||
;;
|
||||
|
||||
let get_mod
|
||||
(sess:Session.sess)
|
||||
(abi:Abi.abi)
|
||||
(meta:Ast.meta_pat)
|
||||
(use_id:node_id)
|
||||
(nref:node_id ref)
|
||||
(oref:opaque_id ref)
|
||||
: (filename * Ast.mod_items) =
|
||||
let found = Queue.create () in
|
||||
let suffix =
|
||||
match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> ".dll"
|
||||
| MacOS_x86_macho -> ".dylib"
|
||||
| Linux_x86_elf -> ".so"
|
||||
in
|
||||
let rec meta_matches i f_meta =
|
||||
if i >= (Array.length meta)
|
||||
then true
|
||||
else
|
||||
match meta.(i) with
|
||||
(* FIXME: bind the wildcards. *)
|
||||
(_, None) -> meta_matches (i+1) f_meta
|
||||
| (k, Some v) ->
|
||||
match atab_search f_meta k with
|
||||
None -> false
|
||||
| Some v' ->
|
||||
if v = v'
|
||||
then meta_matches (i+1) f_meta
|
||||
else false
|
||||
in
|
||||
let file_matches file =
|
||||
log sess "searching for metadata in %s" file;
|
||||
match get_meta sess file with
|
||||
None -> false
|
||||
| Some f_meta ->
|
||||
log sess "matching metadata in %s" file;
|
||||
meta_matches 0 f_meta
|
||||
in
|
||||
iflog sess
|
||||
begin
|
||||
fun _ ->
|
||||
log sess "searching for library matching:";
|
||||
Array.iter
|
||||
begin
|
||||
fun (k,vo) ->
|
||||
match vo with
|
||||
None -> ()
|
||||
| Some v ->
|
||||
log sess "%s = %S" k v
|
||||
end
|
||||
meta;
|
||||
end;
|
||||
Queue.iter
|
||||
begin
|
||||
fun dir ->
|
||||
let dh = Unix.opendir dir in
|
||||
let rec scan _ =
|
||||
try
|
||||
let file = Unix.readdir dh in
|
||||
log sess "considering file %s" file;
|
||||
if (Filename.check_suffix file suffix) &&
|
||||
(file_matches file)
|
||||
then
|
||||
begin
|
||||
iflog sess
|
||||
begin
|
||||
fun _ ->
|
||||
log sess "matched against library %s" file;
|
||||
match get_meta sess file with
|
||||
None -> ()
|
||||
| Some meta ->
|
||||
Array.iter
|
||||
(fun (k,v) -> log sess "%s = %S" k v)
|
||||
meta;
|
||||
end;
|
||||
Queue.add file found;
|
||||
end;
|
||||
scan()
|
||||
with
|
||||
End_of_file -> ()
|
||||
in
|
||||
scan ()
|
||||
end
|
||||
sess.Session.sess_lib_dirs;
|
||||
match Queue.length found with
|
||||
0 -> Common.err (Some use_id) "unsatisfied 'use' clause"
|
||||
| 1 ->
|
||||
let filename = Queue.pop found in
|
||||
let items = get_file_mod sess abi filename nref oref in
|
||||
(filename, items)
|
||||
| _ -> Common.err (Some use_id) "multiple crates match 'use' clause"
|
||||
;;
|
||||
|
||||
let infer_lib_name
|
||||
(sess:Session.sess)
|
||||
(ident:filename)
|
||||
: filename =
|
||||
match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> ident ^ ".dll"
|
||||
| MacOS_x86_macho -> "lib" ^ ident ^ ".dylib"
|
||||
| Linux_x86_elf -> "lib" ^ ident ^ ".so"
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
37
src/boot/driver/llvm/glue.ml
Normal file
37
src/boot/driver/llvm/glue.ml
Normal file
@ -0,0 +1,37 @@
|
||||
(*
|
||||
* Glue for the LLVM backend.
|
||||
*)
|
||||
|
||||
let alt_argspecs sess = [
|
||||
("-llvm", Arg.Unit (fun _ -> sess.Session.sess_alt_backend <- true),
|
||||
"emit LLVM bitcode")
|
||||
];;
|
||||
|
||||
let alt_pipeline sess sem_cx crate =
|
||||
let process processor =
|
||||
processor sem_cx crate;
|
||||
if sess.Session.sess_failed then exit 1 else ()
|
||||
in
|
||||
Array.iter process
|
||||
[|
|
||||
Resolve.process_crate;
|
||||
Type.process_crate;
|
||||
Effect.process_crate;
|
||||
Typestate.process_crate;
|
||||
Loop.process_crate;
|
||||
Alias.process_crate;
|
||||
Dead.process_crate;
|
||||
Layout.process_crate
|
||||
|];
|
||||
Llemit.trans_and_process_crate sess sem_cx crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
421
src/boot/driver/main.ml
Normal file
421
src/boot/driver/main.ml
Normal file
@ -0,0 +1,421 @@
|
||||
|
||||
open Common;;
|
||||
|
||||
let _ =
|
||||
Gc.set { (Gc.get()) with
|
||||
Gc.space_overhead = 400; }
|
||||
;;
|
||||
|
||||
let (targ:Common.target) =
|
||||
match Sys.os_type with
|
||||
"Unix" ->
|
||||
(* FIXME: this is an absurd heuristic. *)
|
||||
if Sys.file_exists "/System/Library"
|
||||
then MacOS_x86_macho
|
||||
else Linux_x86_elf
|
||||
| "Win32" -> Win32_x86_pe
|
||||
| "Cygwin" -> Win32_x86_pe
|
||||
| _ -> Linux_x86_elf
|
||||
;;
|
||||
|
||||
let (abi:Abi.abi) = X86.abi;;
|
||||
|
||||
let (sess:Session.sess) =
|
||||
{
|
||||
Session.sess_in = None;
|
||||
Session.sess_out = None;
|
||||
Session.sess_library_mode = false;
|
||||
Session.sess_alt_backend = false;
|
||||
(* FIXME: need something fancier here for unix sub-flavours. *)
|
||||
Session.sess_targ = targ;
|
||||
Session.sess_log_lex = false;
|
||||
Session.sess_log_parse = false;
|
||||
Session.sess_log_ast = false;
|
||||
Session.sess_log_resolve = false;
|
||||
Session.sess_log_type = false;
|
||||
Session.sess_log_effect = false;
|
||||
Session.sess_log_typestate = false;
|
||||
Session.sess_log_loop = false;
|
||||
Session.sess_log_alias = false;
|
||||
Session.sess_log_dead = false;
|
||||
Session.sess_log_layout = false;
|
||||
Session.sess_log_itype = false;
|
||||
Session.sess_log_trans = false;
|
||||
Session.sess_log_dwarf = false;
|
||||
Session.sess_log_ra = false;
|
||||
Session.sess_log_insn = false;
|
||||
Session.sess_log_asm = false;
|
||||
Session.sess_log_obj = false;
|
||||
Session.sess_log_lib = false;
|
||||
Session.sess_log_out = stdout;
|
||||
Session.sess_trace_block = false;
|
||||
Session.sess_trace_drop = false;
|
||||
Session.sess_trace_tag = false;
|
||||
Session.sess_trace_gc = false;
|
||||
Session.sess_failed = false;
|
||||
Session.sess_spans = Hashtbl.create 0;
|
||||
Session.sess_report_timing = false;
|
||||
Session.sess_report_gc = false;
|
||||
Session.sess_report_deps = false;
|
||||
Session.sess_timings = Hashtbl.create 0;
|
||||
Session.sess_lib_dirs = Queue.create ();
|
||||
}
|
||||
;;
|
||||
|
||||
let default_output_filename (sess:Session.sess) : filename option =
|
||||
match sess.Session.sess_in with
|
||||
None -> None
|
||||
| Some fname ->
|
||||
let base = Filename.chop_extension (Filename.basename fname) in
|
||||
let out =
|
||||
if sess.Session.sess_library_mode
|
||||
then
|
||||
Lib.infer_lib_name sess base
|
||||
else
|
||||
base ^ (match sess.Session.sess_targ with
|
||||
Linux_x86_elf -> ""
|
||||
| MacOS_x86_macho -> ""
|
||||
| Win32_x86_pe -> ".exe")
|
||||
in
|
||||
Some out
|
||||
;;
|
||||
|
||||
let set_default_output_filename (sess:Session.sess) : unit =
|
||||
match sess.Session.sess_out with
|
||||
None -> (sess.Session.sess_out <- default_output_filename sess)
|
||||
| _ -> ()
|
||||
;;
|
||||
|
||||
|
||||
let dump_sig (filename:filename) : unit =
|
||||
let items =
|
||||
Lib.get_file_mod sess abi filename (ref (Node 0)) (ref (Opaque 0)) in
|
||||
Printf.fprintf stdout "%s\n" (Ast.fmt_to_str Ast.fmt_mod_items items);
|
||||
exit 0
|
||||
;;
|
||||
|
||||
let dump_meta (filename:filename) : unit =
|
||||
begin
|
||||
match Lib.get_meta sess filename with
|
||||
None -> Printf.fprintf stderr "Error: bad crate file: %s\n" filename
|
||||
| Some meta ->
|
||||
Array.iter
|
||||
begin
|
||||
fun (k,v) ->
|
||||
Printf.fprintf stdout "%s = %S\n" k v;
|
||||
end
|
||||
meta
|
||||
end;
|
||||
exit 0
|
||||
;;
|
||||
|
||||
let flag f opt desc =
|
||||
(opt, Arg.Unit f, desc)
|
||||
;;
|
||||
|
||||
let argspecs =
|
||||
[
|
||||
("-t", Arg.Symbol (["linux-x86-elf"; "win32-x86-pe"; "macos-x86-macho"],
|
||||
fun s -> (sess.Session.sess_targ <-
|
||||
(match s with
|
||||
"win32-x86-pe" -> Win32_x86_pe
|
||||
| "macos-x86-macho" -> MacOS_x86_macho
|
||||
| _ -> Linux_x86_elf))),
|
||||
(" target (default: " ^ (match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> "win32-x86-pe"
|
||||
| Linux_x86_elf -> "linux-x86-elf"
|
||||
| MacOS_x86_macho -> "macos-x86-macho"
|
||||
) ^ ")"));
|
||||
("-o", Arg.String (fun s -> sess.Session.sess_out <- Some s),
|
||||
"file to output (default: "
|
||||
^ (Session.filename_of sess.Session.sess_out) ^ ")");
|
||||
("-shared", Arg.Unit (fun _ -> sess.Session.sess_library_mode <- true),
|
||||
"compile a shared-library crate");
|
||||
("-L", Arg.String (fun s -> Queue.add s sess.Session.sess_lib_dirs),
|
||||
"dir to add to library path");
|
||||
("-litype", Arg.Unit (fun _ -> sess.Session.sess_log_itype <- true;
|
||||
Il.log_iltypes := true), "log IL types");
|
||||
(flag (fun _ -> sess.Session.sess_log_lex <- true)
|
||||
"-llex" "log lexing");
|
||||
(flag (fun _ -> sess.Session.sess_log_parse <- true)
|
||||
"-lparse" "log parsing");
|
||||
(flag (fun _ -> sess.Session.sess_log_ast <- true)
|
||||
"-last" "log AST");
|
||||
(flag (fun _ -> sess.Session.sess_log_resolve <- true)
|
||||
"-lresolve" "log resolution");
|
||||
(flag (fun _ -> sess.Session.sess_log_type <- true)
|
||||
"-ltype" "log type checking");
|
||||
(flag (fun _ -> sess.Session.sess_log_effect <- true)
|
||||
"-leffect" "log effect checking");
|
||||
(flag (fun _ -> sess.Session.sess_log_typestate <- true)
|
||||
"-ltypestate" "log typestate pass");
|
||||
(flag (fun _ -> sess.Session.sess_log_loop <- true)
|
||||
"-lloop" "log loop analysis");
|
||||
(flag (fun _ -> sess.Session.sess_log_alias <- true)
|
||||
"-lalias" "log alias analysis");
|
||||
(flag (fun _ -> sess.Session.sess_log_dead <- true)
|
||||
"-ldead" "log dead analysis");
|
||||
(flag (fun _ -> sess.Session.sess_log_layout <- true)
|
||||
"-llayout" "log frame layout");
|
||||
(flag (fun _ -> sess.Session.sess_log_trans <- true)
|
||||
"-ltrans" "log IR translation");
|
||||
(flag (fun _ -> sess.Session.sess_log_dwarf <- true)
|
||||
"-ldwarf" "log DWARF generation");
|
||||
(flag (fun _ -> sess.Session.sess_log_ra <- true)
|
||||
"-lra" "log register allocation");
|
||||
(flag (fun _ -> sess.Session.sess_log_insn <- true)
|
||||
"-linsn" "log instruction selection");
|
||||
(flag (fun _ -> sess.Session.sess_log_asm <- true)
|
||||
"-lasm" "log assembly");
|
||||
(flag (fun _ -> sess.Session.sess_log_obj <- true)
|
||||
"-lobj" "log object-file generation");
|
||||
(flag (fun _ -> sess.Session.sess_log_lib <- true)
|
||||
"-llib" "log library search");
|
||||
|
||||
(flag (fun _ -> sess.Session.sess_trace_block <- true)
|
||||
"-tblock" "emit block-boundary tracing code");
|
||||
(flag (fun _ -> sess.Session.sess_trace_drop <- true)
|
||||
"-tdrop" "emit slot-drop tracing code");
|
||||
(flag (fun _ -> sess.Session.sess_trace_tag <- true)
|
||||
"-ttag" "emit tag-construction tracing code");
|
||||
(flag (fun _ -> sess.Session.sess_trace_gc <- true)
|
||||
"-tgc" "emit GC tracing code");
|
||||
|
||||
("-tall", Arg.Unit (fun _ ->
|
||||
sess.Session.sess_trace_block <- true;
|
||||
sess.Session.sess_trace_drop <- true;
|
||||
sess.Session.sess_trace_tag <- true ),
|
||||
"emit all tracing code");
|
||||
|
||||
(flag (fun _ -> sess.Session.sess_report_timing <- true)
|
||||
"-rtime" "report timing of compiler phases");
|
||||
(flag (fun _ -> sess.Session.sess_report_gc <- true)
|
||||
"-rgc" "report gc behavior of compiler");
|
||||
("-rsig", Arg.String dump_sig,
|
||||
"report type-signature from DWARF info in compiled file, then exit");
|
||||
("-rmeta", Arg.String dump_meta,
|
||||
"report metadata from DWARF info in compiled file, then exit");
|
||||
("-rdeps", Arg.Unit (fun _ -> sess.Session.sess_report_deps <- true),
|
||||
"report dependencies of input, then exit");
|
||||
] @ (Glue.alt_argspecs sess)
|
||||
;;
|
||||
|
||||
let exit_if_failed _ =
|
||||
if sess.Session.sess_failed
|
||||
then exit 1
|
||||
else ()
|
||||
;;
|
||||
|
||||
Arg.parse
|
||||
argspecs
|
||||
(fun arg -> sess.Session.sess_in <- (Some arg))
|
||||
("usage: " ^ Sys.argv.(0) ^ " [options] (CRATE_FILE.rc|SOURCE_FILE.rs)\n")
|
||||
;;
|
||||
|
||||
let _ = set_default_output_filename sess
|
||||
;;
|
||||
|
||||
let _ =
|
||||
if sess.Session.sess_out = None
|
||||
then (Printf.fprintf stderr "Error: no output file specified\n"; exit 1)
|
||||
else ()
|
||||
;;
|
||||
|
||||
let _ =
|
||||
if sess.Session.sess_in = None
|
||||
then (Printf.fprintf stderr "Error: empty input filename\n"; exit 1)
|
||||
else ()
|
||||
;;
|
||||
|
||||
|
||||
let (crate:Ast.crate) =
|
||||
Session.time_inner "parse" sess
|
||||
begin
|
||||
fun _ ->
|
||||
let infile = Session.filename_of sess.Session.sess_in in
|
||||
let crate =
|
||||
if Filename.check_suffix infile ".rc"
|
||||
then
|
||||
Cexp.parse_crate_file sess
|
||||
(Lib.get_mod sess abi)
|
||||
(Lib.infer_lib_name sess)
|
||||
else
|
||||
if Filename.check_suffix infile ".rs"
|
||||
then
|
||||
Cexp.parse_src_file sess
|
||||
(Lib.get_mod sess abi)
|
||||
(Lib.infer_lib_name sess)
|
||||
else
|
||||
begin
|
||||
Printf.fprintf stderr
|
||||
"Error: unrecognized input file type: %s\n"
|
||||
infile;
|
||||
exit 1
|
||||
end
|
||||
in
|
||||
if sess.Session.sess_report_deps
|
||||
then
|
||||
let outfile = (Session.filename_of sess.Session.sess_out) in
|
||||
let depfile =
|
||||
match sess.Session.sess_targ with
|
||||
Linux_x86_elf
|
||||
| MacOS_x86_macho -> outfile ^ ".d"
|
||||
| Win32_x86_pe -> (Filename.chop_extension outfile) ^ ".d"
|
||||
in
|
||||
begin
|
||||
Array.iter
|
||||
begin
|
||||
fun out ->
|
||||
Printf.fprintf stdout "%s: \\\n" out;
|
||||
Hashtbl.iter
|
||||
(fun _ file ->
|
||||
Printf.fprintf stdout " %s \\\n" file)
|
||||
crate.node.Ast.crate_files;
|
||||
Printf.fprintf stdout "\n"
|
||||
end
|
||||
[| outfile; depfile|];
|
||||
exit 0
|
||||
end
|
||||
else
|
||||
crate
|
||||
end
|
||||
;;
|
||||
|
||||
exit_if_failed ()
|
||||
;;
|
||||
|
||||
if sess.Session.sess_log_ast
|
||||
then
|
||||
begin
|
||||
Printf.fprintf stdout "Post-parse AST:\n";
|
||||
Format.set_margin 80;
|
||||
Printf.fprintf stdout "%s\n" (Ast.fmt_to_str Ast.fmt_crate crate)
|
||||
end
|
||||
|
||||
let list_to_seq ls = Asm.SEQ (Array.of_list ls);;
|
||||
let select_insns (quads:Il.quads) : Asm.frag =
|
||||
Session.time_inner "insn" sess
|
||||
(fun _ -> X86.select_insns sess quads)
|
||||
;;
|
||||
|
||||
|
||||
(* Semantic passes. *)
|
||||
let sem_cx = Semant.new_ctxt sess abi crate.node
|
||||
;;
|
||||
|
||||
|
||||
let main_pipeline _ =
|
||||
let _ =
|
||||
Array.iter
|
||||
(fun proc ->
|
||||
proc sem_cx crate;
|
||||
exit_if_failed ())
|
||||
[| Resolve.process_crate;
|
||||
Type.process_crate;
|
||||
Effect.process_crate;
|
||||
Typestate.process_crate;
|
||||
Loop.process_crate;
|
||||
Alias.process_crate;
|
||||
Dead.process_crate;
|
||||
Layout.process_crate;
|
||||
Trans.process_crate |]
|
||||
in
|
||||
|
||||
(* Tying up various knots, allocating registers and selecting
|
||||
* instructions.
|
||||
*)
|
||||
let process_code _ (code:Semant.code) : Asm.frag =
|
||||
let frag =
|
||||
match code.Semant.code_vregs_and_spill with
|
||||
None -> select_insns code.Semant.code_quads
|
||||
| Some (n_vregs, spill_fix) ->
|
||||
let (quads', n_spills) =
|
||||
(Session.time_inner "RA" sess
|
||||
(fun _ ->
|
||||
Ra.reg_alloc sess
|
||||
code.Semant.code_quads
|
||||
n_vregs abi))
|
||||
in
|
||||
let insns = select_insns quads' in
|
||||
begin
|
||||
spill_fix.fixup_mem_sz <-
|
||||
Some (Int64.mul
|
||||
(Int64.of_int n_spills)
|
||||
abi.Abi.abi_word_sz);
|
||||
insns
|
||||
end
|
||||
in
|
||||
Asm.ALIGN_FILE (Abi.general_code_alignment,
|
||||
Asm.DEF (code.Semant.code_fixup, frag))
|
||||
in
|
||||
|
||||
let (file_frags:Asm.frag) =
|
||||
let process_file file_id frag_code =
|
||||
let file_fix = Hashtbl.find sem_cx.Semant.ctxt_file_fixups file_id in
|
||||
Asm.DEF (file_fix,
|
||||
list_to_seq (reduce_hash_to_list process_code frag_code))
|
||||
in
|
||||
list_to_seq (reduce_hash_to_list
|
||||
process_file sem_cx.Semant.ctxt_file_code)
|
||||
in
|
||||
|
||||
exit_if_failed ();
|
||||
let (glue_frags:Asm.frag) =
|
||||
list_to_seq (reduce_hash_to_list
|
||||
process_code sem_cx.Semant.ctxt_glue_code)
|
||||
in
|
||||
|
||||
exit_if_failed ();
|
||||
let code = Asm.SEQ [| file_frags; glue_frags |] in
|
||||
let data = list_to_seq (reduce_hash_to_list
|
||||
(fun _ (_, i) -> i) sem_cx.Semant.ctxt_data)
|
||||
in
|
||||
(* Emitting Dwarf and PE/ELF/Macho. *)
|
||||
let (dwarf:Dwarf.debug_records) =
|
||||
Session.time_inner "dwarf" sess
|
||||
(fun _ -> Dwarf.process_crate sem_cx crate)
|
||||
in
|
||||
|
||||
exit_if_failed ();
|
||||
let emitter =
|
||||
match sess.Session.sess_targ with
|
||||
Win32_x86_pe -> Pe.emit_file
|
||||
| MacOS_x86_macho -> Macho.emit_file
|
||||
| Linux_x86_elf -> Elf.emit_file
|
||||
in
|
||||
Session.time_inner "emit" sess
|
||||
(fun _ -> emitter sess crate code data sem_cx dwarf);
|
||||
exit_if_failed ()
|
||||
;;
|
||||
|
||||
if sess.Session.sess_alt_backend
|
||||
then Glue.alt_pipeline sess sem_cx crate
|
||||
else main_pipeline ()
|
||||
;;
|
||||
|
||||
if sess.Session.sess_report_timing
|
||||
then
|
||||
begin
|
||||
Printf.fprintf stdout "timing:\n\n";
|
||||
Array.iter
|
||||
begin
|
||||
fun name ->
|
||||
Printf.fprintf stdout "%20s: %f\n" name
|
||||
(Hashtbl.find sess.Session.sess_timings name)
|
||||
end
|
||||
(sorted_htab_keys sess.Session.sess_timings)
|
||||
end;
|
||||
;;
|
||||
|
||||
if sess.Session.sess_report_gc
|
||||
then Gc.print_stat stdout;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
111
src/boot/driver/session.ml
Normal file
111
src/boot/driver/session.ml
Normal file
@ -0,0 +1,111 @@
|
||||
(*
|
||||
* This module goes near the bottom of the dependency DAG, and holds option,
|
||||
* and global-state machinery for a single run of the compiler.
|
||||
*)
|
||||
|
||||
open Common;;
|
||||
|
||||
type sess =
|
||||
{
|
||||
mutable sess_in: filename option;
|
||||
mutable sess_out: filename option;
|
||||
mutable sess_library_mode: bool;
|
||||
mutable sess_alt_backend: bool;
|
||||
mutable sess_targ: target;
|
||||
mutable sess_log_lex: bool;
|
||||
mutable sess_log_parse: bool;
|
||||
mutable sess_log_ast: bool;
|
||||
mutable sess_log_resolve: bool;
|
||||
mutable sess_log_type: bool;
|
||||
mutable sess_log_effect: bool;
|
||||
mutable sess_log_typestate: bool;
|
||||
mutable sess_log_dead: bool;
|
||||
mutable sess_log_loop: bool;
|
||||
mutable sess_log_alias: bool;
|
||||
mutable sess_log_layout: bool;
|
||||
mutable sess_log_trans: bool;
|
||||
mutable sess_log_itype: bool;
|
||||
mutable sess_log_dwarf: bool;
|
||||
mutable sess_log_ra: bool;
|
||||
mutable sess_log_insn: bool;
|
||||
mutable sess_log_asm: bool;
|
||||
mutable sess_log_obj: bool;
|
||||
mutable sess_log_lib: bool;
|
||||
mutable sess_log_out: out_channel;
|
||||
mutable sess_trace_block: bool;
|
||||
mutable sess_trace_drop: bool;
|
||||
mutable sess_trace_tag: bool;
|
||||
mutable sess_trace_gc: bool;
|
||||
mutable sess_failed: bool;
|
||||
mutable sess_report_timing: bool;
|
||||
mutable sess_report_gc: bool;
|
||||
mutable sess_report_deps: bool;
|
||||
sess_timings: (string, float) Hashtbl.t;
|
||||
sess_spans: (node_id,span) Hashtbl.t;
|
||||
sess_lib_dirs: filename Queue.t;
|
||||
}
|
||||
;;
|
||||
|
||||
let add_time sess name amt =
|
||||
let existing =
|
||||
if Hashtbl.mem sess.sess_timings name
|
||||
then Hashtbl.find sess.sess_timings name
|
||||
else 0.0
|
||||
in
|
||||
(Hashtbl.replace sess.sess_timings name (existing +. amt))
|
||||
;;
|
||||
|
||||
let time_inner name sess thunk =
|
||||
let t0 = Unix.gettimeofday() in
|
||||
let x = thunk() in
|
||||
let t1 = Unix.gettimeofday() in
|
||||
add_time sess name (t1 -. t0);
|
||||
x
|
||||
;;
|
||||
|
||||
let get_span sess id =
|
||||
if Hashtbl.mem sess.sess_spans id
|
||||
then (Some (Hashtbl.find sess.sess_spans id))
|
||||
else None
|
||||
;;
|
||||
|
||||
let log name flag chan =
|
||||
let k1 s =
|
||||
Printf.fprintf chan "%s: %s\n%!" name s
|
||||
in
|
||||
let k2 _ = () in
|
||||
Printf.ksprintf (if flag then k1 else k2)
|
||||
;;
|
||||
|
||||
let fail sess =
|
||||
sess.sess_failed <- true;
|
||||
Printf.fprintf sess.sess_log_out
|
||||
;;
|
||||
|
||||
|
||||
let string_of_pos (p:pos) =
|
||||
let (filename, line, col) = p in
|
||||
Printf.sprintf "%s:%d:%d" filename line col
|
||||
;;
|
||||
|
||||
|
||||
let string_of_span (s:span) =
|
||||
let (filename, line0, col0) = s.lo in
|
||||
let (_, line1, col1) = s.hi in
|
||||
Printf.sprintf "%s:%d:%d - %d:%d" filename line0 col0 line1 col1
|
||||
;;
|
||||
|
||||
let filename_of (fo:filename option) : filename =
|
||||
match fo with
|
||||
None -> "<none>"
|
||||
| Some f -> f
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
16
src/boot/driver/x86/glue.ml
Normal file
16
src/boot/driver/x86/glue.ml
Normal file
@ -0,0 +1,16 @@
|
||||
(*
|
||||
* Glue, or lack thereof, for the standard x86 backend.
|
||||
*)
|
||||
|
||||
let alt_argspecs _ = [];;
|
||||
let alt_pipeline _ _ _ = ();;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
1360
src/boot/fe/ast.ml
Normal file
1360
src/boot/fe/ast.ml
Normal file
File diff suppressed because it is too large
Load Diff
762
src/boot/fe/cexp.ml
Normal file
762
src/boot/fe/cexp.ml
Normal file
@ -0,0 +1,762 @@
|
||||
|
||||
open Common;;
|
||||
open Token;;
|
||||
open Parser;;
|
||||
|
||||
(* NB: cexps (crate-expressions / constant-expressions) are only used
|
||||
* transiently during compilation: they are the outermost expression-language
|
||||
* describing crate configuration and constants. They are completely evaluated
|
||||
* at compile-time, in a little micro-interpreter defined here, with the
|
||||
* results of evaluation being the sequence of directives controlling the rest
|
||||
* of the compiler.
|
||||
*
|
||||
* Cexps, like pexps, do not escape the language front-end.
|
||||
*
|
||||
* You can think of the AST as a statement-language called "item" sandwiched
|
||||
* between two expression-languages, "cexp" on the outside and "pexp" on the
|
||||
* inside. The front-end evaluates cexp on the outside in order to get one big
|
||||
* directive-list, evaluating those parts of pexp that are directly used by
|
||||
* cexp in passing, and desugaring those remaining parts of pexp that are
|
||||
* embedded within the items of the directives.
|
||||
*
|
||||
* The rest of the compiler only deals with the directives, which are mostly
|
||||
* just a set of containers for items. Items are what most of AST describes
|
||||
* ("most" because the type-grammar spans both items and pexps).
|
||||
*
|
||||
*)
|
||||
|
||||
type meta = (Ast.ident * Pexp.pexp) array;;
|
||||
|
||||
type meta_pat = (Ast.ident * (Pexp.pexp option)) array;;
|
||||
|
||||
type auth = (Ast.name * Ast.effect);;
|
||||
|
||||
type cexp =
|
||||
CEXP_alt of cexp_alt identified
|
||||
| CEXP_let of cexp_let identified
|
||||
| CEXP_src_mod of cexp_src identified
|
||||
| CEXP_dir_mod of cexp_dir identified
|
||||
| CEXP_use_mod of cexp_use identified
|
||||
| CEXP_nat_mod of cexp_nat identified
|
||||
| CEXP_meta of meta identified
|
||||
| CEXP_auth of auth identified
|
||||
|
||||
and cexp_alt =
|
||||
{ alt_val: Pexp.pexp;
|
||||
alt_arms: (Pexp.pexp * cexp array) array;
|
||||
alt_else: cexp array }
|
||||
|
||||
and cexp_let =
|
||||
{ let_ident: Ast.ident;
|
||||
let_value: Pexp.pexp;
|
||||
let_body: cexp array; }
|
||||
|
||||
and cexp_src =
|
||||
{ src_ident: Ast.ident;
|
||||
src_path: Pexp.pexp option }
|
||||
|
||||
and cexp_dir =
|
||||
{ dir_ident: Ast.ident;
|
||||
dir_path: Pexp.pexp option;
|
||||
dir_body: cexp array }
|
||||
|
||||
and cexp_use =
|
||||
{ use_ident: Ast.ident;
|
||||
use_meta: meta_pat; }
|
||||
|
||||
and cexp_nat =
|
||||
{ nat_abi: string;
|
||||
nat_ident: Ast.ident;
|
||||
nat_path: Pexp.pexp option;
|
||||
(*
|
||||
* FIXME: possibly support embedding optional strings as
|
||||
* symbol-names, to handle mangling schemes that aren't
|
||||
* Token.IDENT values
|
||||
*)
|
||||
nat_items: Ast.mod_items;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
(* Cexp grammar. *)
|
||||
|
||||
let parse_meta_input (ps:pstate) : (Ast.ident * Pexp.pexp option) =
|
||||
let lab = (ctxt "meta input: label" Pexp.parse_ident ps) in
|
||||
match peek ps with
|
||||
EQ ->
|
||||
bump ps;
|
||||
let v =
|
||||
match peek ps with
|
||||
UNDERSCORE -> bump ps; None
|
||||
| _ -> Some (Pexp.parse_pexp ps)
|
||||
in
|
||||
(lab, v)
|
||||
| _ -> raise (unexpected ps)
|
||||
;;
|
||||
|
||||
let parse_meta_pat (ps:pstate) : meta_pat =
|
||||
bracketed_zero_or_more LPAREN RPAREN
|
||||
(Some COMMA) parse_meta_input ps
|
||||
;;
|
||||
|
||||
let parse_meta (ps:pstate) : meta =
|
||||
Array.map
|
||||
begin
|
||||
fun (id,v) ->
|
||||
match v with
|
||||
None ->
|
||||
raise (err ("wildcard found in meta pattern "
|
||||
^ "where value expected") ps)
|
||||
| Some v -> (id,v)
|
||||
end
|
||||
(parse_meta_pat ps)
|
||||
;;
|
||||
|
||||
let parse_optional_meta_pat
|
||||
(ps:pstate)
|
||||
(ident:Ast.ident)
|
||||
: meta_pat =
|
||||
match peek ps with
|
||||
LPAREN -> parse_meta_pat ps
|
||||
| _ ->
|
||||
let apos = lexpos ps in
|
||||
[| ("name", Some (span ps apos apos (Pexp.PEXP_str ident))) |]
|
||||
;;
|
||||
|
||||
let rec parse_cexps (ps:pstate) (term:Token.token) : cexp array =
|
||||
let cexps = Queue.create () in
|
||||
while ((peek ps) <> term)
|
||||
do
|
||||
Queue.push (parse_cexp ps) cexps
|
||||
done;
|
||||
expect ps term;
|
||||
queue_to_arr cexps
|
||||
|
||||
and parse_cexp (ps:pstate) : cexp =
|
||||
|
||||
let apos = lexpos ps in
|
||||
match peek ps with
|
||||
MOD ->
|
||||
begin
|
||||
bump ps;
|
||||
let name = ctxt "mod: name" Pexp.parse_ident ps in
|
||||
let path = ctxt "mod: path" parse_eq_pexp_opt ps
|
||||
in
|
||||
match peek ps with
|
||||
SEMI ->
|
||||
bump ps;
|
||||
let bpos = lexpos ps in
|
||||
CEXP_src_mod
|
||||
(span ps apos bpos { src_ident = name;
|
||||
src_path = path })
|
||||
| LBRACE ->
|
||||
let body =
|
||||
bracketed_zero_or_more LBRACE RBRACE
|
||||
None parse_cexp ps
|
||||
in
|
||||
let bpos = lexpos ps in
|
||||
CEXP_dir_mod
|
||||
(span ps apos bpos { dir_ident = name;
|
||||
dir_path = path;
|
||||
dir_body = body })
|
||||
| _ -> raise (unexpected ps)
|
||||
end
|
||||
|
||||
| NATIVE ->
|
||||
begin
|
||||
bump ps;
|
||||
let abi =
|
||||
match peek ps with
|
||||
MOD -> "cdecl"
|
||||
| LIT_STR s -> bump ps; s
|
||||
| _ -> raise (unexpected ps)
|
||||
in
|
||||
let _ = expect ps MOD in
|
||||
let name = ctxt "native mod: name" Pexp.parse_ident ps in
|
||||
let path = ctxt "native mod: path" parse_eq_pexp_opt ps in
|
||||
let items = Hashtbl.create 0 in
|
||||
let get_item ps =
|
||||
let (ident, item) = Item.parse_mod_item_from_signature ps in
|
||||
htab_put items ident item;
|
||||
in
|
||||
ignore (bracketed_zero_or_more
|
||||
LBRACE RBRACE None get_item ps);
|
||||
let bpos = lexpos ps in
|
||||
CEXP_nat_mod
|
||||
(span ps apos bpos { nat_abi = abi;
|
||||
nat_ident = name;
|
||||
nat_path = path;
|
||||
nat_items = items })
|
||||
end
|
||||
|
||||
| USE ->
|
||||
begin
|
||||
bump ps;
|
||||
let ident = ctxt "use mod: name" Pexp.parse_ident ps in
|
||||
let meta =
|
||||
ctxt "use mod: meta" parse_optional_meta_pat ps ident
|
||||
in
|
||||
let bpos = lexpos ps in
|
||||
expect ps SEMI;
|
||||
CEXP_use_mod
|
||||
(span ps apos bpos { use_ident = ident;
|
||||
use_meta = meta })
|
||||
end
|
||||
|
||||
| LET ->
|
||||
begin
|
||||
bump ps;
|
||||
expect ps LPAREN;
|
||||
let id = Pexp.parse_ident ps in
|
||||
expect ps EQ;
|
||||
let v = Pexp.parse_pexp ps in
|
||||
expect ps RPAREN;
|
||||
expect ps LBRACE;
|
||||
let body = parse_cexps ps RBRACE in
|
||||
let bpos = lexpos ps in
|
||||
CEXP_let
|
||||
(span ps apos bpos
|
||||
{ let_ident = id;
|
||||
let_value = v;
|
||||
let_body = body })
|
||||
end
|
||||
|
||||
| ALT ->
|
||||
begin
|
||||
bump ps;
|
||||
expect ps LPAREN;
|
||||
let v = Pexp.parse_pexp ps in
|
||||
expect ps RPAREN;
|
||||
expect ps LBRACE;
|
||||
let rec consume_arms arms =
|
||||
match peek ps with
|
||||
CASE ->
|
||||
begin
|
||||
bump ps;
|
||||
expect ps LPAREN;
|
||||
let cond = Pexp.parse_pexp ps in
|
||||
expect ps RPAREN;
|
||||
expect ps LBRACE;
|
||||
let consequent = parse_cexps ps RBRACE in
|
||||
let arm = (cond, consequent) in
|
||||
consume_arms (arm::arms)
|
||||
end
|
||||
| ELSE ->
|
||||
begin
|
||||
bump ps;
|
||||
expect ps LBRACE;
|
||||
let consequent = parse_cexps ps RBRACE in
|
||||
expect ps RBRACE;
|
||||
let bpos = lexpos ps in
|
||||
span ps apos bpos
|
||||
{ alt_val = v;
|
||||
alt_arms = Array.of_list (List.rev arms);
|
||||
alt_else = consequent }
|
||||
end
|
||||
|
||||
| _ -> raise (unexpected ps)
|
||||
in
|
||||
CEXP_alt (consume_arms [])
|
||||
end
|
||||
|
||||
| META ->
|
||||
bump ps;
|
||||
let meta = parse_meta ps in
|
||||
expect ps SEMI;
|
||||
let bpos = lexpos ps in
|
||||
CEXP_meta (span ps apos bpos meta)
|
||||
|
||||
| AUTH ->
|
||||
bump ps;
|
||||
let name = Pexp.parse_name ps in
|
||||
expect ps EQ;
|
||||
let effect = Pexp.parse_effect ps in
|
||||
expect ps SEMI;
|
||||
let bpos = lexpos ps in
|
||||
CEXP_auth (span ps apos bpos (name, effect))
|
||||
|
||||
| _ -> raise (unexpected ps)
|
||||
|
||||
|
||||
and parse_eq_pexp_opt (ps:pstate) : Pexp.pexp option =
|
||||
match peek ps with
|
||||
EQ ->
|
||||
begin
|
||||
bump ps;
|
||||
Some (Pexp.parse_pexp ps)
|
||||
end
|
||||
| _ -> None
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Dynamic-typed micro-interpreter for the cexp language.
|
||||
*
|
||||
* The product of evaluating a pexp is a pval.
|
||||
*
|
||||
* The product of evlauating a cexp is a cdir array.
|
||||
*)
|
||||
|
||||
type pval =
|
||||
PVAL_str of string
|
||||
| PVAL_num of int64
|
||||
| PVAL_bool of bool
|
||||
;;
|
||||
|
||||
type cdir =
|
||||
CDIR_meta of ((Ast.ident * string) array)
|
||||
| CDIR_syntax of Ast.name
|
||||
| CDIR_check of (Ast.name * pval array)
|
||||
| CDIR_mod of (Ast.ident * Ast.mod_item)
|
||||
| CDIR_auth of auth
|
||||
|
||||
type env = { env_bindings: (Ast.ident * pval) list;
|
||||
env_prefix: filename list;
|
||||
env_items: (filename, Ast.mod_items) Hashtbl.t;
|
||||
env_files: (node_id,filename) Hashtbl.t;
|
||||
env_required: (node_id, (required_lib * nabi_conv)) Hashtbl.t;
|
||||
env_required_syms: (node_id, string) Hashtbl.t;
|
||||
env_ps: pstate; }
|
||||
|
||||
let unexpected_val (expected:string) (v:pval) =
|
||||
let got =
|
||||
match v with
|
||||
PVAL_str s -> "str \"" ^ (String.escaped s) ^ "\""
|
||||
| PVAL_num i -> "num " ^ (Int64.to_string i)
|
||||
| PVAL_bool b -> if b then "bool true" else "bool false"
|
||||
in
|
||||
(* FIXME: proper error reporting, please. *)
|
||||
bug () "expected %s, got %s" expected got
|
||||
;;
|
||||
|
||||
let rewrap_items id items =
|
||||
let item = decl [||] (Ast.MOD_ITEM_mod items) in
|
||||
{ id = id; node = item }
|
||||
;;
|
||||
|
||||
|
||||
let rec eval_cexps (env:env) (exps:cexp array) : cdir array =
|
||||
Parser.arj (Array.map (eval_cexp env) exps)
|
||||
|
||||
and eval_cexp (env:env) (exp:cexp) : cdir array =
|
||||
match exp with
|
||||
CEXP_alt {node=ca} ->
|
||||
let v = eval_pexp env ca.alt_val in
|
||||
let rec try_arm i =
|
||||
if i >= Array.length ca.alt_arms
|
||||
then ca.alt_else
|
||||
else
|
||||
let (arm_head, arm_body) = ca.alt_arms.(i) in
|
||||
let v' = eval_pexp env arm_head in
|
||||
if v' = v
|
||||
then arm_body
|
||||
else try_arm (i+1)
|
||||
in
|
||||
eval_cexps env (try_arm 0)
|
||||
|
||||
| CEXP_let {node=cl} ->
|
||||
let ident = cl.let_ident in
|
||||
let v = eval_pexp env cl.let_value in
|
||||
let env = { env with
|
||||
env_bindings = ((ident,v)::env.env_bindings ) }
|
||||
in
|
||||
eval_cexps env cl.let_body
|
||||
|
||||
| CEXP_src_mod {node=s; id=id} ->
|
||||
let name = s.src_ident in
|
||||
let path =
|
||||
match s.src_path with
|
||||
None -> name ^ ".rs"
|
||||
| Some p -> eval_pexp_to_str env p
|
||||
in
|
||||
let full_path =
|
||||
List.fold_left Filename.concat ""
|
||||
(List.rev (path :: env.env_prefix))
|
||||
in
|
||||
let ps = env.env_ps in
|
||||
let p =
|
||||
make_parser
|
||||
ps.pstate_temp_id
|
||||
ps.pstate_node_id
|
||||
ps.pstate_opaque_id
|
||||
ps.pstate_sess
|
||||
ps.pstate_get_mod
|
||||
ps.pstate_infer_lib_name
|
||||
env.env_required
|
||||
env.env_required_syms
|
||||
full_path
|
||||
in
|
||||
let items = Item.parse_mod_items p EOF in
|
||||
htab_put env.env_files id full_path;
|
||||
[| CDIR_mod (name, rewrap_items id items) |]
|
||||
|
||||
| CEXP_dir_mod {node=d; id=id} ->
|
||||
let items = Hashtbl.create 0 in
|
||||
let name = d.dir_ident in
|
||||
let path =
|
||||
match d.dir_path with
|
||||
None -> name
|
||||
| Some p -> eval_pexp_to_str env p
|
||||
in
|
||||
let env = { env with
|
||||
env_prefix = path :: env.env_prefix } in
|
||||
let sub_directives = eval_cexps env d.dir_body in
|
||||
let add d =
|
||||
match d with
|
||||
CDIR_mod (name, item) ->
|
||||
htab_put items name item
|
||||
| _ -> raise (err "non-'mod' directive found in 'dir' directive"
|
||||
env.env_ps)
|
||||
in
|
||||
Array.iter add sub_directives;
|
||||
[| CDIR_mod (name, rewrap_items id (Item.empty_view, items)) |]
|
||||
|
||||
| CEXP_use_mod {node=u; id=id} ->
|
||||
let ps = env.env_ps in
|
||||
let name = u.use_ident in
|
||||
let (path, items) =
|
||||
let meta_pat =
|
||||
Array.map
|
||||
begin
|
||||
fun (k,vo) ->
|
||||
match vo with
|
||||
None -> (k, None)
|
||||
| Some p -> (k, Some (eval_pexp_to_str env p))
|
||||
end
|
||||
u.use_meta
|
||||
in
|
||||
ps.pstate_get_mod meta_pat id ps.pstate_node_id ps.pstate_opaque_id
|
||||
in
|
||||
iflog ps
|
||||
begin
|
||||
fun _ ->
|
||||
log ps "extracted mod signature from %s (binding to %s)"
|
||||
path name;
|
||||
log ps "%a" Ast.sprintf_mod_items items;
|
||||
end;
|
||||
let rlib = REQUIRED_LIB_rust { required_libname = path;
|
||||
required_prefix = 1 }
|
||||
in
|
||||
let item = decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, items)) in
|
||||
let item = { id = id; node = item } in
|
||||
let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in
|
||||
Item.note_required_mod env.env_ps span CONV_rust rlib item;
|
||||
[| CDIR_mod (name, item) |]
|
||||
|
||||
| CEXP_nat_mod {node=cn;id=id} ->
|
||||
let conv =
|
||||
let v = cn.nat_abi in
|
||||
match string_to_conv v with
|
||||
None -> unexpected_val "calling convention" (PVAL_str v)
|
||||
| Some c -> c
|
||||
in
|
||||
let name = cn.nat_ident in
|
||||
let filename =
|
||||
match cn.nat_path with
|
||||
None -> env.env_ps.pstate_infer_lib_name name
|
||||
| Some p -> eval_pexp_to_str env p
|
||||
in
|
||||
let item =
|
||||
decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, cn.nat_items))
|
||||
in
|
||||
let item = { id = id; node = item } in
|
||||
let rlib = REQUIRED_LIB_c { required_libname = filename;
|
||||
required_prefix = 1 }
|
||||
in
|
||||
let ps = env.env_ps in
|
||||
let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in
|
||||
Item.note_required_mod env.env_ps span conv rlib item;
|
||||
[| CDIR_mod (name, item) |]
|
||||
|
||||
| CEXP_meta m ->
|
||||
[| CDIR_meta
|
||||
begin
|
||||
Array.map
|
||||
begin
|
||||
fun (id, p) -> (id, eval_pexp_to_str env p)
|
||||
end
|
||||
m.node
|
||||
end |]
|
||||
|
||||
| CEXP_auth a -> [| CDIR_auth a.node |]
|
||||
|
||||
|
||||
and eval_pexp (env:env) (exp:Pexp.pexp) : pval =
|
||||
match exp.node with
|
||||
| Pexp.PEXP_binop (bop, a, b) ->
|
||||
begin
|
||||
let av = eval_pexp env a in
|
||||
let bv = eval_pexp env b in
|
||||
match (bop, av, bv) with
|
||||
(Ast.BINOP_add, PVAL_str az, PVAL_str bz) ->
|
||||
PVAL_str (az ^ bz)
|
||||
| _ ->
|
||||
let av = (need_num av) in
|
||||
let bv = (need_num bv) in
|
||||
PVAL_num
|
||||
begin
|
||||
match bop with
|
||||
Ast.BINOP_add -> Int64.add av bv
|
||||
| Ast.BINOP_sub -> Int64.sub av bv
|
||||
| Ast.BINOP_mul -> Int64.mul av bv
|
||||
| Ast.BINOP_div -> Int64.div av bv
|
||||
| _ ->
|
||||
bug ()
|
||||
"unhandled arithmetic op in Cexp.eval_pexp"
|
||||
end
|
||||
end
|
||||
|
||||
| Pexp.PEXP_unop (uop, a) ->
|
||||
begin
|
||||
match uop with
|
||||
Ast.UNOP_not ->
|
||||
PVAL_bool (not (eval_pexp_to_bool env a))
|
||||
| Ast.UNOP_neg ->
|
||||
PVAL_num (Int64.neg (eval_pexp_to_num env a))
|
||||
| _ -> bug () "Unexpected unop in Cexp.eval_pexp"
|
||||
end
|
||||
|
||||
| Pexp.PEXP_lval (Pexp.PLVAL_ident ident) ->
|
||||
begin
|
||||
match ltab_search env.env_bindings ident with
|
||||
None -> raise (err (Printf.sprintf "no binding for '%s' found"
|
||||
ident) env.env_ps)
|
||||
| Some v -> v
|
||||
end
|
||||
|
||||
| Pexp.PEXP_lit (Ast.LIT_bool b) ->
|
||||
PVAL_bool b
|
||||
|
||||
| Pexp.PEXP_lit (Ast.LIT_int (i, _)) ->
|
||||
PVAL_num i
|
||||
|
||||
| Pexp.PEXP_str s ->
|
||||
PVAL_str s
|
||||
|
||||
| _ -> bug () "unexpected Pexp in Cexp.eval_pexp"
|
||||
|
||||
|
||||
and eval_pexp_to_str (env:env) (exp:Pexp.pexp) : string =
|
||||
match eval_pexp env exp with
|
||||
PVAL_str s -> s
|
||||
| v -> unexpected_val "str" v
|
||||
|
||||
and need_num (cv:pval) : int64 =
|
||||
match cv with
|
||||
PVAL_num n -> n
|
||||
| v -> unexpected_val "num" v
|
||||
|
||||
and eval_pexp_to_num (env:env) (exp:Pexp.pexp) : int64 =
|
||||
need_num (eval_pexp env exp)
|
||||
|
||||
and eval_pexp_to_bool (env:env) (exp:Pexp.pexp) : bool =
|
||||
match eval_pexp env exp with
|
||||
PVAL_bool b -> b
|
||||
| v -> unexpected_val "bool" v
|
||||
|
||||
;;
|
||||
|
||||
|
||||
let find_main_fn
|
||||
(ps:pstate)
|
||||
(crate_items:Ast.mod_items)
|
||||
: Ast.name =
|
||||
let fns = ref [] in
|
||||
let extend prefix_name ident =
|
||||
match prefix_name with
|
||||
None -> Ast.NAME_base (Ast.BASE_ident ident)
|
||||
| Some n -> Ast.NAME_ext (n, Ast.COMP_ident ident)
|
||||
in
|
||||
let rec dig prefix_name items =
|
||||
Hashtbl.iter (extract_fn prefix_name) items
|
||||
and extract_fn prefix_name ident item =
|
||||
if not (Array.length item.node.Ast.decl_params = 0) ||
|
||||
Hashtbl.mem ps.pstate_required item.id
|
||||
then ()
|
||||
else
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_mod (_, items) ->
|
||||
dig (Some (extend prefix_name ident)) items
|
||||
|
||||
| Ast.MOD_ITEM_fn _ ->
|
||||
if ident = "main"
|
||||
then fns := (extend prefix_name ident) :: (!fns)
|
||||
else ()
|
||||
|
||||
| _ -> ()
|
||||
in
|
||||
dig None crate_items;
|
||||
match !fns with
|
||||
[] -> raise (err "no 'main' function found" ps)
|
||||
| [x] -> x
|
||||
| _ -> raise (err "multiple 'main' functions found" ps)
|
||||
;;
|
||||
|
||||
|
||||
let with_err_handling sess thunk =
|
||||
try
|
||||
thunk ()
|
||||
with
|
||||
Parse_err (ps, str) ->
|
||||
Session.fail sess "Parse error: %s\n%!" str;
|
||||
List.iter
|
||||
(fun (cx,pos) ->
|
||||
Session.fail sess "%s:E (parse context): %s\n%!"
|
||||
(Session.string_of_pos pos) cx)
|
||||
ps.pstate_ctxt;
|
||||
let apos = lexpos ps in
|
||||
span ps apos apos
|
||||
{ Ast.crate_items = (Item.empty_view, Hashtbl.create 0);
|
||||
Ast.crate_meta = [||];
|
||||
Ast.crate_auth = Hashtbl.create 0;
|
||||
Ast.crate_required = Hashtbl.create 0;
|
||||
Ast.crate_required_syms = Hashtbl.create 0;
|
||||
Ast.crate_main = None;
|
||||
Ast.crate_files = Hashtbl.create 0 }
|
||||
;;
|
||||
|
||||
|
||||
let parse_crate_file
|
||||
(sess:Session.sess)
|
||||
(get_mod:get_mod_fn)
|
||||
(infer_lib_name:(Ast.ident -> filename))
|
||||
: Ast.crate =
|
||||
let fname = Session.filename_of sess.Session.sess_in in
|
||||
let tref = ref (Temp 0) in
|
||||
let nref = ref (Node 0) in
|
||||
let oref = ref (Opaque 0) in
|
||||
let required = Hashtbl.create 4 in
|
||||
let required_syms = Hashtbl.create 4 in
|
||||
let ps =
|
||||
make_parser tref nref oref sess get_mod
|
||||
infer_lib_name required required_syms fname
|
||||
in
|
||||
|
||||
let files = Hashtbl.create 0 in
|
||||
let items = Hashtbl.create 4 in
|
||||
let target_bindings =
|
||||
let (os, arch, libc) =
|
||||
match sess.Session.sess_targ with
|
||||
Linux_x86_elf -> ("linux", "x86", "libc.so.6")
|
||||
| Win32_x86_pe -> ("win32", "x86", "msvcrt.dll")
|
||||
| MacOS_x86_macho -> ("macos", "x86", "libc.dylib")
|
||||
in
|
||||
[
|
||||
("target_os", PVAL_str os);
|
||||
("target_arch", PVAL_str arch);
|
||||
("target_libc", PVAL_str libc)
|
||||
]
|
||||
in
|
||||
let build_bindings =
|
||||
[
|
||||
("build_compiler", PVAL_str Sys.executable_name);
|
||||
("build_input", PVAL_str fname);
|
||||
]
|
||||
in
|
||||
let initial_bindings =
|
||||
target_bindings
|
||||
@ build_bindings
|
||||
in
|
||||
let env = { env_bindings = initial_bindings;
|
||||
env_prefix = [Filename.dirname fname];
|
||||
env_items = Hashtbl.create 0;
|
||||
env_files = files;
|
||||
env_required = required;
|
||||
env_required_syms = required_syms;
|
||||
env_ps = ps; }
|
||||
in
|
||||
let auth = Hashtbl.create 0 in
|
||||
with_err_handling sess
|
||||
begin
|
||||
fun _ ->
|
||||
let apos = lexpos ps in
|
||||
let cexps = parse_cexps ps EOF in
|
||||
let cdirs = eval_cexps env cexps in
|
||||
let meta = Queue.create () in
|
||||
let _ =
|
||||
Array.iter
|
||||
begin
|
||||
fun d ->
|
||||
match d with
|
||||
CDIR_mod (name, item) -> htab_put items name item
|
||||
| CDIR_meta metas ->
|
||||
Array.iter (fun m -> Queue.add m meta) metas
|
||||
| CDIR_auth (n,e) ->
|
||||
if Hashtbl.mem auth n
|
||||
then raise (err "duplicate 'auth' clause" ps)
|
||||
else Hashtbl.add auth n e
|
||||
| _ ->
|
||||
raise
|
||||
(err "unhandled directive at top level" ps)
|
||||
end
|
||||
cdirs
|
||||
in
|
||||
let bpos = lexpos ps in
|
||||
let main =
|
||||
if ps.pstate_sess.Session.sess_library_mode
|
||||
then None
|
||||
else Some (find_main_fn ps items) in
|
||||
let crate = { Ast.crate_items = (Item.empty_view, items);
|
||||
Ast.crate_meta = queue_to_arr meta;
|
||||
Ast.crate_auth = auth;
|
||||
Ast.crate_required = required;
|
||||
Ast.crate_required_syms = required_syms;
|
||||
Ast.crate_main = main;
|
||||
Ast.crate_files = files }
|
||||
in
|
||||
let cratei = span ps apos bpos crate in
|
||||
htab_put files cratei.id fname;
|
||||
cratei
|
||||
end
|
||||
;;
|
||||
|
||||
let parse_src_file
|
||||
(sess:Session.sess)
|
||||
(get_mod:get_mod_fn)
|
||||
(infer_lib_name:(Ast.ident -> filename))
|
||||
: Ast.crate =
|
||||
let fname = Session.filename_of sess.Session.sess_in in
|
||||
let tref = ref (Temp 0) in
|
||||
let nref = ref (Node 0) in
|
||||
let oref = ref (Opaque 0) in
|
||||
let required = Hashtbl.create 0 in
|
||||
let required_syms = Hashtbl.create 0 in
|
||||
let ps =
|
||||
make_parser tref nref oref sess get_mod
|
||||
infer_lib_name required required_syms fname
|
||||
in
|
||||
with_err_handling sess
|
||||
begin
|
||||
fun _ ->
|
||||
let apos = lexpos ps in
|
||||
let items = Item.parse_mod_items ps EOF in
|
||||
let bpos = lexpos ps in
|
||||
let files = Hashtbl.create 0 in
|
||||
let main =
|
||||
if ps.pstate_sess.Session.sess_library_mode
|
||||
then None
|
||||
else Some (find_main_fn ps (snd items))
|
||||
in
|
||||
let crate = { Ast.crate_items = items;
|
||||
Ast.crate_required = required;
|
||||
Ast.crate_required_syms = required_syms;
|
||||
Ast.crate_auth = Hashtbl.create 0;
|
||||
Ast.crate_meta = [||];
|
||||
Ast.crate_main = main;
|
||||
Ast.crate_files = files }
|
||||
in
|
||||
let cratei = span ps apos bpos crate in
|
||||
htab_put files cratei.id fname;
|
||||
cratei
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1139
src/boot/fe/item.ml
Normal file
1139
src/boot/fe/item.ml
Normal file
File diff suppressed because it is too large
Load Diff
362
src/boot/fe/lexer.mll
Normal file
362
src/boot/fe/lexer.mll
Normal file
@ -0,0 +1,362 @@
|
||||
|
||||
|
||||
{
|
||||
|
||||
open Token;;
|
||||
open Common;;
|
||||
|
||||
exception Lex_err of (string * Common.pos);;
|
||||
|
||||
let fail lexbuf s =
|
||||
let p = lexbuf.Lexing.lex_start_p in
|
||||
let pos =
|
||||
(p.Lexing.pos_fname,
|
||||
p.Lexing.pos_lnum ,
|
||||
(p.Lexing.pos_cnum) - (p.Lexing.pos_bol))
|
||||
in
|
||||
raise (Lex_err (s, pos))
|
||||
;;
|
||||
|
||||
let bump_line p = { p with
|
||||
Lexing.pos_lnum = p.Lexing.pos_lnum + 1;
|
||||
Lexing.pos_bol = p.Lexing.pos_cnum }
|
||||
;;
|
||||
|
||||
let keyword_table = Hashtbl.create 100
|
||||
let _ =
|
||||
List.iter (fun (kwd, tok) -> Common.htab_put keyword_table kwd tok)
|
||||
[ ("mod", MOD);
|
||||
("use", USE);
|
||||
("meta", META);
|
||||
("auth", AUTH);
|
||||
|
||||
("syntax", SYNTAX);
|
||||
|
||||
("if", IF);
|
||||
("else", ELSE);
|
||||
("while", WHILE);
|
||||
("do", DO);
|
||||
("alt", ALT);
|
||||
("case", CASE);
|
||||
|
||||
("for", FOR);
|
||||
("each", EACH);
|
||||
("put", PUT);
|
||||
("ret", RET);
|
||||
("be", BE);
|
||||
|
||||
("fail", FAIL);
|
||||
("drop", DROP);
|
||||
|
||||
("type", TYPE);
|
||||
("check", CHECK);
|
||||
("claim", CLAIM);
|
||||
("prove", PROVE);
|
||||
|
||||
("io", IO);
|
||||
("state", STATE);
|
||||
("unsafe", UNSAFE);
|
||||
|
||||
("native", NATIVE);
|
||||
("mutable", MUTABLE);
|
||||
("auto", AUTO);
|
||||
|
||||
("fn", FN);
|
||||
("iter", ITER);
|
||||
|
||||
("import", IMPORT);
|
||||
("export", EXPORT);
|
||||
|
||||
("let", LET);
|
||||
|
||||
("log", LOG);
|
||||
("spawn", SPAWN);
|
||||
("thread", THREAD);
|
||||
("yield", YIELD);
|
||||
("join", JOIN);
|
||||
|
||||
("bool", BOOL);
|
||||
|
||||
("int", INT);
|
||||
("uint", UINT);
|
||||
|
||||
("char", CHAR);
|
||||
("str", STR);
|
||||
|
||||
("rec", REC);
|
||||
("tup", TUP);
|
||||
("tag", TAG);
|
||||
("vec", VEC);
|
||||
("any", ANY);
|
||||
|
||||
("obj", OBJ);
|
||||
|
||||
("port", PORT);
|
||||
("chan", CHAN);
|
||||
|
||||
("task", TASK);
|
||||
|
||||
("true", LIT_BOOL true);
|
||||
("false", LIT_BOOL false);
|
||||
|
||||
("in", IN);
|
||||
|
||||
("as", AS);
|
||||
("with", WITH);
|
||||
|
||||
("bind", BIND);
|
||||
|
||||
("u8", MACH TY_u8);
|
||||
("u16", MACH TY_u16);
|
||||
("u32", MACH TY_u32);
|
||||
("u64", MACH TY_u64);
|
||||
("i8", MACH TY_i8);
|
||||
("i16", MACH TY_i16);
|
||||
("i32", MACH TY_i32);
|
||||
("i64", MACH TY_i64);
|
||||
("f32", MACH TY_f32);
|
||||
("f64", MACH TY_f64)
|
||||
]
|
||||
;;
|
||||
}
|
||||
|
||||
let hexdig = ['0'-'9' 'a'-'f' 'A'-'F']
|
||||
let bin = "0b" ['0' '1']['0' '1' '_']*
|
||||
let hex = "0x" hexdig ['0'-'9' 'a'-'f' 'A'-'F' '_']*
|
||||
let dec = ['0'-'9']+
|
||||
let exp = ['e''E']['-''+']? dec
|
||||
let flo = (dec '.' dec (exp?)) | (dec exp)
|
||||
|
||||
let ws = [ ' ' '\t' '\r' ]
|
||||
|
||||
let id = ['a'-'z' 'A'-'Z' '_']['a'-'z' 'A'-'Z' '0'-'9' '_']*
|
||||
|
||||
rule token = parse
|
||||
ws+ { token lexbuf }
|
||||
| '\n' { lexbuf.Lexing.lex_curr_p
|
||||
<- (bump_line lexbuf.Lexing.lex_curr_p);
|
||||
token lexbuf }
|
||||
| "//" [^'\n']* { token lexbuf }
|
||||
|
||||
| '+' { PLUS }
|
||||
| '-' { MINUS }
|
||||
| '*' { STAR }
|
||||
| '/' { SLASH }
|
||||
| '%' { PERCENT }
|
||||
| '=' { EQ }
|
||||
| '<' { LT }
|
||||
| "<=" { LE }
|
||||
| "==" { EQEQ }
|
||||
| "!=" { NE }
|
||||
| ">=" { GE }
|
||||
| '>' { GT }
|
||||
| '!' { NOT }
|
||||
| '&' { AND }
|
||||
| "&&" { ANDAND }
|
||||
| '|' { OR }
|
||||
| "||" { OROR }
|
||||
| "<<" { LSL }
|
||||
| ">>" { LSR }
|
||||
| ">>>" { ASR }
|
||||
| '~' { TILDE }
|
||||
| '{' { LBRACE }
|
||||
| '_' (dec as n) { IDX (int_of_string n) }
|
||||
| '_' { UNDERSCORE }
|
||||
| '}' { RBRACE }
|
||||
|
||||
| "+=" { OPEQ (PLUS) }
|
||||
| "-=" { OPEQ (MINUS) }
|
||||
| "*=" { OPEQ (STAR) }
|
||||
| "/=" { OPEQ (SLASH) }
|
||||
| "%=" { OPEQ (PERCENT) }
|
||||
| "&=" { OPEQ (AND) }
|
||||
| "|=" { OPEQ (OR) }
|
||||
| "<<=" { OPEQ (LSL) }
|
||||
| ">>=" { OPEQ (LSR) }
|
||||
| ">>>=" { OPEQ (ASR) }
|
||||
| "^=" { OPEQ (CARET) }
|
||||
|
||||
| '#' { POUND }
|
||||
| '@' { AT }
|
||||
| '^' { CARET }
|
||||
| '.' { DOT }
|
||||
| ',' { COMMA }
|
||||
| ';' { SEMI }
|
||||
| ':' { COLON }
|
||||
| "<-" { LARROW }
|
||||
| "<|" { SEND }
|
||||
| "->" { RARROW }
|
||||
| '(' { LPAREN }
|
||||
| ')' { RPAREN }
|
||||
| '[' { LBRACKET }
|
||||
| ']' { RBRACKET }
|
||||
|
||||
| id as i
|
||||
{ try
|
||||
Hashtbl.find keyword_table i
|
||||
with
|
||||
Not_found -> IDENT (i)
|
||||
}
|
||||
|
||||
| bin as n { LIT_INT (Int64.of_string n, n) }
|
||||
| hex as n { LIT_INT (Int64.of_string n, n) }
|
||||
| dec as n { LIT_INT (Int64.of_string n, n) }
|
||||
| flo as n { LIT_FLO n }
|
||||
|
||||
| '\'' { char lexbuf }
|
||||
| '"' { let buf = Buffer.create 32 in
|
||||
str buf lexbuf }
|
||||
|
||||
| eof { EOF }
|
||||
|
||||
and str buf = parse
|
||||
_ as ch
|
||||
{
|
||||
match ch with
|
||||
'"' -> LIT_STR (Buffer.contents buf)
|
||||
| '\\' -> str_escape buf lexbuf
|
||||
| _ ->
|
||||
Buffer.add_char buf ch;
|
||||
let c = Char.code ch in
|
||||
if bounds 0 c 0x7f
|
||||
then str buf lexbuf
|
||||
else
|
||||
if ((c land 0b1110_0000) == 0b1100_0000)
|
||||
then ext_str 1 buf lexbuf
|
||||
else
|
||||
if ((c land 0b1111_0000) == 0b1110_0000)
|
||||
then ext_str 2 buf lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1000) == 0b1111_0000)
|
||||
then ext_str 3 buf lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1100) == 0b1111_1000)
|
||||
then ext_str 4 buf lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1110) == 0b1111_1100)
|
||||
then ext_str 5 buf lexbuf
|
||||
else fail lexbuf "bad initial utf-8 byte"
|
||||
}
|
||||
|
||||
and str_escape buf = parse
|
||||
'x' ((hexdig hexdig) as h)
|
||||
| 'u' ((hexdig hexdig hexdig hexdig) as h)
|
||||
| 'U'
|
||||
((hexdig hexdig hexdig hexdig
|
||||
hexdig hexdig hexdig hexdig) as h)
|
||||
{
|
||||
Buffer.add_string buf (char_as_utf8 (int_of_string ("0x" ^ h)));
|
||||
str buf lexbuf
|
||||
}
|
||||
| 'n' { Buffer.add_char buf '\n'; str buf lexbuf }
|
||||
| 'r' { Buffer.add_char buf '\r'; str buf lexbuf }
|
||||
| 't' { Buffer.add_char buf '\t'; str buf lexbuf }
|
||||
| '\\' { Buffer.add_char buf '\\'; str buf lexbuf }
|
||||
| '"' { Buffer.add_char buf '"'; str buf lexbuf }
|
||||
| _ as c { fail lexbuf ("bad escape: \\" ^ (Char.escaped c)) }
|
||||
|
||||
|
||||
and ext_str n buf = parse
|
||||
_ as ch
|
||||
{
|
||||
let c = Char.code ch in
|
||||
if ((c land 0b1100_0000) == (0b1000_0000))
|
||||
then
|
||||
begin
|
||||
Buffer.add_char buf ch;
|
||||
if n = 1
|
||||
then str buf lexbuf
|
||||
else ext_str (n-1) buf lexbuf
|
||||
end
|
||||
else
|
||||
fail lexbuf "bad trailing utf-8 byte"
|
||||
}
|
||||
|
||||
|
||||
and char = parse
|
||||
'\\' { char_escape lexbuf }
|
||||
| _ as c
|
||||
{
|
||||
let c = Char.code c in
|
||||
if bounds 0 c 0x7f
|
||||
then end_char c lexbuf
|
||||
else
|
||||
if ((c land 0b1110_0000) == 0b1100_0000)
|
||||
then ext_char 1 (c land 0b0001_1111) lexbuf
|
||||
else
|
||||
if ((c land 0b1111_0000) == 0b1110_0000)
|
||||
then ext_char 2 (c land 0b0000_1111) lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1000) == 0b1111_0000)
|
||||
then ext_char 3 (c land 0b0000_0111) lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1100) == 0b1111_1000)
|
||||
then ext_char 4 (c land 0b0000_0011) lexbuf
|
||||
else
|
||||
if ((c land 0b1111_1110) == 0b1111_1100)
|
||||
then ext_char 5 (c land 0b0000_0001) lexbuf
|
||||
else fail lexbuf "bad initial utf-8 byte"
|
||||
}
|
||||
|
||||
and char_escape = parse
|
||||
'x' ((hexdig hexdig) as h)
|
||||
| 'u' ((hexdig hexdig hexdig hexdig) as h)
|
||||
| 'U'
|
||||
((hexdig hexdig hexdig hexdig
|
||||
hexdig hexdig hexdig hexdig) as h)
|
||||
{
|
||||
end_char (int_of_string ("0x" ^ h)) lexbuf
|
||||
}
|
||||
| 'n' { end_char (Char.code '\n') lexbuf }
|
||||
| 'r' { end_char (Char.code '\r') lexbuf }
|
||||
| 't' { end_char (Char.code '\t') lexbuf }
|
||||
| '\\' { end_char (Char.code '\\') lexbuf }
|
||||
| '\'' { end_char (Char.code '\'') lexbuf }
|
||||
| _ as c { fail lexbuf ("bad escape: \\" ^ (Char.escaped c)) }
|
||||
|
||||
|
||||
and ext_char n accum = parse
|
||||
_ as c
|
||||
{
|
||||
let c = Char.code c in
|
||||
if ((c land 0b1100_0000) == (0b1000_0000))
|
||||
then
|
||||
let accum = (accum lsl 6) lor (c land 0b0011_1111) in
|
||||
if n = 1
|
||||
then end_char accum lexbuf
|
||||
else ext_char (n-1) accum lexbuf
|
||||
else
|
||||
fail lexbuf "bad trailing utf-8 byte"
|
||||
}
|
||||
|
||||
and end_char accum = parse
|
||||
'\'' { LIT_CHAR accum }
|
||||
|
||||
|
||||
and bracequote buf depth = parse
|
||||
|
||||
'\\' '{' { Buffer.add_char buf '{';
|
||||
bracequote buf depth lexbuf }
|
||||
|
||||
| '{' { Buffer.add_char buf '{';
|
||||
bracequote buf (depth+1) lexbuf }
|
||||
|
||||
| '\\' '}' { Buffer.add_char buf '}';
|
||||
bracequote buf depth lexbuf }
|
||||
|
||||
| '}' { if depth = 1
|
||||
then BRACEQUOTE (Buffer.contents buf)
|
||||
else
|
||||
begin
|
||||
Buffer.add_char buf '}';
|
||||
bracequote buf (depth-1) lexbuf
|
||||
end }
|
||||
|
||||
| '\\' [^'{' '}'] { let s = Lexing.lexeme lexbuf in
|
||||
Buffer.add_string buf s;
|
||||
bracequote buf depth lexbuf }
|
||||
|
||||
|
||||
| [^'\\' '{' '}']+ { let s = Lexing.lexeme lexbuf in
|
||||
Buffer.add_string buf s;
|
||||
bracequote buf depth lexbuf }
|
374
src/boot/fe/parser.ml
Normal file
374
src/boot/fe/parser.ml
Normal file
@ -0,0 +1,374 @@
|
||||
|
||||
open Common;;
|
||||
open Token;;
|
||||
|
||||
(* Fundamental parser types and actions *)
|
||||
|
||||
type get_mod_fn = (Ast.meta_pat
|
||||
-> node_id
|
||||
-> (node_id ref)
|
||||
-> (opaque_id ref)
|
||||
-> (filename * Ast.mod_items))
|
||||
;;
|
||||
|
||||
type pstate =
|
||||
{ mutable pstate_peek : token;
|
||||
mutable pstate_ctxt : (string * pos) list;
|
||||
mutable pstate_rstr : bool;
|
||||
mutable pstate_depth: int;
|
||||
pstate_lexbuf : Lexing.lexbuf;
|
||||
pstate_file : filename;
|
||||
pstate_sess : Session.sess;
|
||||
pstate_temp_id : temp_id ref;
|
||||
pstate_node_id : node_id ref;
|
||||
pstate_opaque_id : opaque_id ref;
|
||||
pstate_get_mod : get_mod_fn;
|
||||
pstate_infer_lib_name : (Ast.ident -> filename);
|
||||
pstate_required : (node_id, (required_lib * nabi_conv)) Hashtbl.t;
|
||||
pstate_required_syms : (node_id, string) Hashtbl.t; }
|
||||
;;
|
||||
|
||||
let log (ps:pstate) = Session.log "parse"
|
||||
ps.pstate_sess.Session.sess_log_parse
|
||||
ps.pstate_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog ps thunk =
|
||||
if ps.pstate_sess.Session.sess_log_parse
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
let make_parser
|
||||
(tref:temp_id ref)
|
||||
(nref:node_id ref)
|
||||
(oref:opaque_id ref)
|
||||
(sess:Session.sess)
|
||||
(get_mod:get_mod_fn)
|
||||
(infer_lib_name:Ast.ident -> filename)
|
||||
(required:(node_id, (required_lib * nabi_conv)) Hashtbl.t)
|
||||
(required_syms:(node_id, string) Hashtbl.t)
|
||||
(fname:string)
|
||||
: pstate =
|
||||
let lexbuf = Lexing.from_channel (open_in fname) in
|
||||
let spos = { lexbuf.Lexing.lex_start_p with Lexing.pos_fname = fname } in
|
||||
let cpos = { lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = fname } in
|
||||
lexbuf.Lexing.lex_start_p <- spos;
|
||||
lexbuf.Lexing.lex_curr_p <- cpos;
|
||||
let first = Lexer.token lexbuf in
|
||||
let ps =
|
||||
{ pstate_peek = first;
|
||||
pstate_ctxt = [];
|
||||
pstate_rstr = false;
|
||||
pstate_depth = 0;
|
||||
pstate_lexbuf = lexbuf;
|
||||
pstate_file = fname;
|
||||
pstate_sess = sess;
|
||||
pstate_temp_id = tref;
|
||||
pstate_node_id = nref;
|
||||
pstate_opaque_id = oref;
|
||||
pstate_get_mod = get_mod;
|
||||
pstate_infer_lib_name = infer_lib_name;
|
||||
pstate_required = required;
|
||||
pstate_required_syms = required_syms; }
|
||||
in
|
||||
iflog ps (fun _ -> log ps "made parser for: %s\n%!" fname);
|
||||
ps
|
||||
;;
|
||||
|
||||
exception Parse_err of (pstate * string)
|
||||
;;
|
||||
|
||||
let lexpos (ps:pstate) : pos =
|
||||
let p = ps.pstate_lexbuf.Lexing.lex_start_p in
|
||||
(p.Lexing.pos_fname,
|
||||
p.Lexing.pos_lnum ,
|
||||
(p.Lexing.pos_cnum) - (p.Lexing.pos_bol))
|
||||
;;
|
||||
|
||||
let next_node_id (ps:pstate) : node_id =
|
||||
let id = !(ps.pstate_node_id) in
|
||||
ps.pstate_node_id := Node ((int_of_node id)+1);
|
||||
id
|
||||
;;
|
||||
|
||||
let next_opaque_id (ps:pstate) : opaque_id =
|
||||
let id = !(ps.pstate_opaque_id) in
|
||||
ps.pstate_opaque_id := Opaque ((int_of_opaque id)+1);
|
||||
id
|
||||
;;
|
||||
|
||||
let span
|
||||
(ps:pstate)
|
||||
(apos:pos)
|
||||
(bpos:pos)
|
||||
(x:'a)
|
||||
: 'a identified =
|
||||
let span = { lo = apos; hi = bpos } in
|
||||
let id = next_node_id ps in
|
||||
iflog ps (fun _ -> log ps "span for node #%d: %s"
|
||||
(int_of_node id) (Session.string_of_span span));
|
||||
htab_put ps.pstate_sess.Session.sess_spans id span;
|
||||
{ node = x; id = id }
|
||||
;;
|
||||
|
||||
let decl p i =
|
||||
{ Ast.decl_params = p;
|
||||
Ast.decl_item = i }
|
||||
;;
|
||||
|
||||
let spans
|
||||
(ps:pstate)
|
||||
(things:('a identified) array)
|
||||
(apos:pos)
|
||||
(thing:'a)
|
||||
: ('a identified) array =
|
||||
Array.append things [| (span ps apos (lexpos ps) thing) |]
|
||||
;;
|
||||
|
||||
(*
|
||||
* The point of this is to make a new node_id entry for a node that is a
|
||||
* "copy" of an lval returned from somewhere else. For example if you create
|
||||
* a temp, the lval it returns can only be used in *one* place, for the
|
||||
* node_id denotes the place that lval is first used; subsequent uses of
|
||||
* 'the same' reference must clone_lval it into a new node_id. Otherwise
|
||||
* there is trouble.
|
||||
*)
|
||||
|
||||
let clone_span
|
||||
(ps:pstate)
|
||||
(oldnode:'a identified)
|
||||
(newthing:'b)
|
||||
: 'b identified =
|
||||
let s = Hashtbl.find ps.pstate_sess.Session.sess_spans oldnode.id in
|
||||
span ps s.lo s.hi newthing
|
||||
;;
|
||||
|
||||
let rec clone_lval (ps:pstate) (lval:Ast.lval) : Ast.lval =
|
||||
match lval with
|
||||
Ast.LVAL_base nb ->
|
||||
let nnb = clone_span ps nb nb.node in
|
||||
Ast.LVAL_base nnb
|
||||
| Ast.LVAL_ext (base, ext) ->
|
||||
Ast.LVAL_ext ((clone_lval ps base), ext)
|
||||
;;
|
||||
|
||||
let clone_atom (ps:pstate) (atom:Ast.atom) : Ast.atom =
|
||||
match atom with
|
||||
Ast.ATOM_literal _ -> atom
|
||||
| Ast.ATOM_lval lv -> Ast.ATOM_lval (clone_lval ps lv)
|
||||
;;
|
||||
|
||||
let ctxt (n:string) (f:pstate -> 'a) (ps:pstate) : 'a =
|
||||
(ps.pstate_ctxt <- (n, lexpos ps) :: ps.pstate_ctxt;
|
||||
let res = f ps in
|
||||
ps.pstate_ctxt <- List.tl ps.pstate_ctxt;
|
||||
res)
|
||||
;;
|
||||
|
||||
let rstr (r:bool) (f:pstate -> 'a) (ps:pstate) : 'a =
|
||||
let prev = ps.pstate_rstr in
|
||||
(ps.pstate_rstr <- r;
|
||||
let res = f ps in
|
||||
ps.pstate_rstr <- prev;
|
||||
res)
|
||||
;;
|
||||
|
||||
let err (str:string) (ps:pstate) =
|
||||
(Parse_err (ps, (str)))
|
||||
;;
|
||||
|
||||
|
||||
let (slot_nil:Ast.slot) =
|
||||
{ Ast.slot_mode = Ast.MODE_interior;
|
||||
Ast.slot_mutable = false;
|
||||
Ast.slot_ty = Some Ast.TY_nil }
|
||||
;;
|
||||
|
||||
let (slot_auto:Ast.slot) =
|
||||
{ Ast.slot_mode = Ast.MODE_interior;
|
||||
Ast.slot_mutable = true;
|
||||
Ast.slot_ty = None }
|
||||
;;
|
||||
|
||||
let build_tmp
|
||||
(ps:pstate)
|
||||
(slot:Ast.slot)
|
||||
(apos:pos)
|
||||
(bpos:pos)
|
||||
: (temp_id * Ast.lval * Ast.stmt) =
|
||||
let nonce = !(ps.pstate_temp_id) in
|
||||
ps.pstate_temp_id := Temp ((int_of_temp nonce)+1);
|
||||
iflog ps
|
||||
(fun _ -> log ps "building temporary %d" (int_of_temp nonce));
|
||||
let decl = Ast.DECL_slot (Ast.KEY_temp nonce, (span ps apos bpos slot)) in
|
||||
let declstmt = span ps apos bpos (Ast.STMT_decl decl) in
|
||||
let tmp = Ast.LVAL_base (span ps apos bpos (Ast.BASE_temp nonce)) in
|
||||
(nonce, tmp, declstmt)
|
||||
;;
|
||||
|
||||
(* Simple helpers *)
|
||||
|
||||
(* FIXME: please rename these, they make eyes bleed. *)
|
||||
|
||||
let arr (ls:'a list) : 'a array = Array.of_list ls ;;
|
||||
let arl (ls:'a list) : 'a array = Array.of_list (List.rev ls) ;;
|
||||
let arj (ar:('a array array)) = Array.concat (Array.to_list ar) ;;
|
||||
let arj1st (pairs:(('a array) * 'b) array) : (('a array) * 'b array) =
|
||||
let (az, bz) = List.split (Array.to_list pairs) in
|
||||
(Array.concat az, Array.of_list bz)
|
||||
|
||||
|
||||
(* Bottom-most parser actions. *)
|
||||
|
||||
let peek (ps:pstate) : token =
|
||||
iflog ps
|
||||
begin
|
||||
fun _ ->
|
||||
log ps "peeking at: %s // %s"
|
||||
(string_of_tok ps.pstate_peek)
|
||||
(match ps.pstate_ctxt with
|
||||
(s, _) :: _ -> s
|
||||
| _ -> "<empty>")
|
||||
end;
|
||||
ps.pstate_peek
|
||||
;;
|
||||
|
||||
|
||||
let bump (ps:pstate) : unit =
|
||||
begin
|
||||
iflog ps (fun _ -> log ps "bumping past: %s"
|
||||
(string_of_tok ps.pstate_peek));
|
||||
ps.pstate_peek <- Lexer.token ps.pstate_lexbuf
|
||||
end
|
||||
;;
|
||||
|
||||
let bump_bracequote (ps:pstate) : unit =
|
||||
begin
|
||||
assert (ps.pstate_peek = LBRACE);
|
||||
iflog ps (fun _ -> log ps "bumping past: %s"
|
||||
(string_of_tok ps.pstate_peek));
|
||||
let buf = Buffer.create 32 in
|
||||
ps.pstate_peek <- Lexer.bracequote buf 1 ps.pstate_lexbuf
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
let expect (ps:pstate) (t:token) : unit =
|
||||
let p = peek ps in
|
||||
if p == t
|
||||
then bump ps
|
||||
else
|
||||
let msg = ("Expected '" ^ (string_of_tok t) ^
|
||||
"', found '" ^ (string_of_tok p ) ^ "'") in
|
||||
raise (Parse_err (ps, msg))
|
||||
;;
|
||||
|
||||
let unexpected (ps:pstate) =
|
||||
err ("Unexpected token '" ^ (string_of_tok (peek ps)) ^ "'") ps
|
||||
;;
|
||||
|
||||
|
||||
|
||||
(* Parser combinators. *)
|
||||
|
||||
let one_or_more
|
||||
(sep:token)
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
let accum = ref [prule ps] in
|
||||
while peek ps == sep
|
||||
do
|
||||
bump ps;
|
||||
accum := (prule ps) :: !accum
|
||||
done;
|
||||
arl !accum
|
||||
;;
|
||||
|
||||
let bracketed_seq
|
||||
(mandatory:int)
|
||||
(bra:token)
|
||||
(ket:token)
|
||||
(sepOpt:token option)
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
expect ps bra;
|
||||
let accum = ref [] in
|
||||
let dosep _ =
|
||||
(match sepOpt with
|
||||
None -> ()
|
||||
| Some tok ->
|
||||
if (!accum = [])
|
||||
then ()
|
||||
else expect ps tok)
|
||||
in
|
||||
while mandatory > List.length (!accum) do
|
||||
dosep ();
|
||||
accum := (prule ps) :: (!accum)
|
||||
done;
|
||||
while (not (peek ps = ket))
|
||||
do
|
||||
dosep ();
|
||||
accum := (prule ps) :: !accum
|
||||
done;
|
||||
expect ps ket;
|
||||
arl !accum
|
||||
;;
|
||||
|
||||
|
||||
let bracketed_zero_or_more
|
||||
(bra:token)
|
||||
(ket:token)
|
||||
(sepOpt:token option)
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
bracketed_seq 0 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
|
||||
;;
|
||||
|
||||
|
||||
let paren_comma_list
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
bracketed_zero_or_more LPAREN RPAREN (Some COMMA) prule ps
|
||||
;;
|
||||
|
||||
let bracketed_one_or_more
|
||||
(bra:token)
|
||||
(ket:token)
|
||||
(sepOpt:token option)
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
bracketed_seq 1 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
|
||||
;;
|
||||
|
||||
let bracketed_two_or_more
|
||||
(bra:token)
|
||||
(ket:token)
|
||||
(sepOpt:token option)
|
||||
(prule:pstate -> 'a)
|
||||
(ps:pstate)
|
||||
: 'a array =
|
||||
bracketed_seq 2 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
|
||||
;;
|
||||
|
||||
|
||||
let bracketed (bra:token) (ket:token) (prule:pstate -> 'a) (ps:pstate) : 'a =
|
||||
expect ps bra;
|
||||
let res = ctxt "bracketed" prule ps in
|
||||
expect ps ket;
|
||||
res
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1354
src/boot/fe/pexp.ml
Normal file
1354
src/boot/fe/pexp.ml
Normal file
File diff suppressed because it is too large
Load Diff
308
src/boot/fe/token.ml
Normal file
308
src/boot/fe/token.ml
Normal file
@ -0,0 +1,308 @@
|
||||
type token =
|
||||
|
||||
(* Expression operator symbols *)
|
||||
PLUS
|
||||
| MINUS
|
||||
| STAR
|
||||
| SLASH
|
||||
| PERCENT
|
||||
| EQ
|
||||
| LT
|
||||
| LE
|
||||
| EQEQ
|
||||
| NE
|
||||
| GE
|
||||
| GT
|
||||
| NOT
|
||||
| TILDE
|
||||
| CARET
|
||||
| AND
|
||||
| ANDAND
|
||||
| OR
|
||||
| OROR
|
||||
| LSL
|
||||
| LSR
|
||||
| ASR
|
||||
| OPEQ of token
|
||||
| AS
|
||||
| WITH
|
||||
|
||||
(* Structural symbols *)
|
||||
| AT
|
||||
| DOT
|
||||
| COMMA
|
||||
| SEMI
|
||||
| COLON
|
||||
| RARROW
|
||||
| SEND
|
||||
| LARROW
|
||||
| LPAREN
|
||||
| RPAREN
|
||||
| LBRACKET
|
||||
| RBRACKET
|
||||
| LBRACE
|
||||
| RBRACE
|
||||
|
||||
(* Module and crate keywords *)
|
||||
| MOD
|
||||
| USE
|
||||
| AUTH
|
||||
| META
|
||||
|
||||
(* Metaprogramming keywords *)
|
||||
| SYNTAX
|
||||
| POUND
|
||||
|
||||
(* Statement keywords *)
|
||||
| IF
|
||||
| ELSE
|
||||
| DO
|
||||
| WHILE
|
||||
| ALT
|
||||
| CASE
|
||||
|
||||
| FAIL
|
||||
| DROP
|
||||
|
||||
| IN
|
||||
| FOR
|
||||
| EACH
|
||||
| PUT
|
||||
| RET
|
||||
| BE
|
||||
|
||||
(* Type and type-state keywords *)
|
||||
| TYPE
|
||||
| CHECK
|
||||
| CLAIM
|
||||
| PROVE
|
||||
|
||||
(* Effect keywords *)
|
||||
| IO
|
||||
| STATE
|
||||
| UNSAFE
|
||||
|
||||
(* Type qualifiers *)
|
||||
| NATIVE
|
||||
| AUTO
|
||||
| MUTABLE
|
||||
|
||||
(* Name management *)
|
||||
| IMPORT
|
||||
| EXPORT
|
||||
|
||||
(* Value / stmt declarators *)
|
||||
| LET
|
||||
|
||||
(* Magic runtime services *)
|
||||
| LOG
|
||||
| SPAWN
|
||||
| BIND
|
||||
| THREAD
|
||||
| YIELD
|
||||
| JOIN
|
||||
|
||||
(* Literals *)
|
||||
| LIT_INT of (int64 * string)
|
||||
| LIT_FLO of string
|
||||
| LIT_STR of string
|
||||
| LIT_CHAR of int
|
||||
| LIT_BOOL of bool
|
||||
|
||||
(* Name components *)
|
||||
| IDENT of string
|
||||
| IDX of int
|
||||
| UNDERSCORE
|
||||
|
||||
(* Reserved type names *)
|
||||
| BOOL
|
||||
| INT
|
||||
| UINT
|
||||
| CHAR
|
||||
| STR
|
||||
| MACH of Common.ty_mach
|
||||
|
||||
(* Algebraic type constructors *)
|
||||
| REC
|
||||
| TUP
|
||||
| TAG
|
||||
| VEC
|
||||
| ANY
|
||||
|
||||
(* Callable type constructors *)
|
||||
| FN
|
||||
| ITER
|
||||
|
||||
(* Object type *)
|
||||
| OBJ
|
||||
|
||||
(* Comm and task types *)
|
||||
| CHAN
|
||||
| PORT
|
||||
| TASK
|
||||
|
||||
| EOF
|
||||
|
||||
| BRACEQUOTE of string
|
||||
|
||||
;;
|
||||
|
||||
let rec string_of_tok t =
|
||||
match t with
|
||||
(* Operator symbols (mostly) *)
|
||||
PLUS -> "+"
|
||||
| MINUS -> "-"
|
||||
| STAR -> "*"
|
||||
| SLASH -> "/"
|
||||
| PERCENT -> "%"
|
||||
| EQ -> "="
|
||||
| LT -> "<"
|
||||
| LE -> "<="
|
||||
| EQEQ -> "=="
|
||||
| NE -> "!="
|
||||
| GE -> ">="
|
||||
| GT -> ">"
|
||||
| TILDE -> "~"
|
||||
| CARET -> "^"
|
||||
| NOT -> "!"
|
||||
| AND -> "&"
|
||||
| ANDAND -> "&&"
|
||||
| OR -> "|"
|
||||
| OROR -> "||"
|
||||
| LSL -> "<<"
|
||||
| LSR -> ">>"
|
||||
| ASR -> ">>>"
|
||||
| OPEQ op -> string_of_tok op ^ "="
|
||||
| AS -> "as"
|
||||
| WITH -> "with"
|
||||
|
||||
(* Structural symbols *)
|
||||
| AT -> "@"
|
||||
| DOT -> "."
|
||||
| COMMA -> ","
|
||||
| SEMI -> ";"
|
||||
| COLON -> ":"
|
||||
| RARROW -> "->"
|
||||
| SEND -> "<|"
|
||||
| LARROW -> "<-"
|
||||
| LPAREN -> "("
|
||||
| RPAREN -> ")"
|
||||
| LBRACKET -> "["
|
||||
| RBRACKET -> "]"
|
||||
| LBRACE -> "{"
|
||||
| RBRACE -> "}"
|
||||
|
||||
(* Module and crate keywords *)
|
||||
| MOD -> "mod"
|
||||
| USE -> "use"
|
||||
| AUTH -> "auth"
|
||||
|
||||
(* Metaprogramming keywords *)
|
||||
| SYNTAX -> "syntax"
|
||||
| META -> "meta"
|
||||
| POUND -> "#"
|
||||
|
||||
(* Control-flow keywords *)
|
||||
| IF -> "if"
|
||||
| ELSE -> "else"
|
||||
| DO -> "do"
|
||||
| WHILE -> "while"
|
||||
| ALT -> "alt"
|
||||
| CASE -> "case"
|
||||
|
||||
| FAIL -> "fail"
|
||||
| DROP -> "drop"
|
||||
|
||||
| IN -> "in"
|
||||
| FOR -> "for"
|
||||
| EACH -> "each"
|
||||
| PUT -> "put"
|
||||
| RET -> "ret"
|
||||
| BE -> "be"
|
||||
|
||||
(* Type and type-state keywords *)
|
||||
| TYPE -> "type"
|
||||
| CHECK -> "check"
|
||||
| CLAIM -> "claim"
|
||||
| PROVE -> "prove"
|
||||
|
||||
(* Effect keywords *)
|
||||
| IO -> "io"
|
||||
| STATE -> "state"
|
||||
| UNSAFE -> "unsafe"
|
||||
|
||||
(* Type qualifiers *)
|
||||
| NATIVE -> "native"
|
||||
| AUTO -> "auto"
|
||||
| MUTABLE -> "mutable"
|
||||
|
||||
(* Name management *)
|
||||
| IMPORT -> "import"
|
||||
| EXPORT -> "export"
|
||||
|
||||
(* Value / stmt declarators. *)
|
||||
| LET -> "let"
|
||||
|
||||
(* Magic runtime services *)
|
||||
| LOG -> "log"
|
||||
| SPAWN -> "spawn"
|
||||
| BIND -> "bind"
|
||||
| THREAD -> "thread"
|
||||
| YIELD -> "yield"
|
||||
| JOIN -> "join"
|
||||
|
||||
(* Literals *)
|
||||
| LIT_INT (_,s) -> s
|
||||
| LIT_FLO n -> n
|
||||
| LIT_STR s -> ("\"" ^ (String.escaped s) ^ "\"")
|
||||
| LIT_CHAR c -> ("'" ^ (Common.escaped_char c) ^ "'")
|
||||
| LIT_BOOL b -> if b then "true" else "false"
|
||||
|
||||
(* Name components *)
|
||||
| IDENT s -> s
|
||||
| IDX i -> ("_" ^ (string_of_int i))
|
||||
| UNDERSCORE -> "_"
|
||||
|
||||
(* Reserved type names *)
|
||||
| BOOL -> "bool"
|
||||
| INT -> "int"
|
||||
| UINT -> "uint"
|
||||
| CHAR -> "char"
|
||||
| STR -> "str"
|
||||
| MACH m -> Common.string_of_ty_mach m
|
||||
|
||||
(* Algebraic type constructors *)
|
||||
| REC -> "rec"
|
||||
| TUP -> "tup"
|
||||
| TAG -> "tag"
|
||||
| VEC -> "vec"
|
||||
| ANY -> "any"
|
||||
|
||||
(* Callable type constructors *)
|
||||
| FN -> "fn"
|
||||
| ITER -> "fn"
|
||||
|
||||
(* Object type *)
|
||||
| OBJ -> "obj"
|
||||
|
||||
(* Ports and channels *)
|
||||
| CHAN -> "chan"
|
||||
| PORT -> "port"
|
||||
|
||||
(* Taskess types *)
|
||||
| TASK -> "task"
|
||||
|
||||
| BRACEQUOTE _ -> "{...bracequote...}"
|
||||
|
||||
| EOF -> "<EOF>"
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
69
src/boot/llvm/llabi.ml
Normal file
69
src/boot/llvm/llabi.ml
Normal file
@ -0,0 +1,69 @@
|
||||
(*
|
||||
* LLVM integration with the Rust runtime.
|
||||
*)
|
||||
|
||||
type abi = {
|
||||
crate_ty: Llvm.lltype;
|
||||
task_ty: Llvm.lltype;
|
||||
word_ty: Llvm.lltype;
|
||||
rust_start: Llvm.llvalue;
|
||||
};;
|
||||
|
||||
let declare_abi (llctx:Llvm.llcontext) (llmod:Llvm.llmodule) : abi =
|
||||
let i32 = Llvm.i32_type llctx in
|
||||
|
||||
let crate_ty =
|
||||
(* TODO: other architectures besides x86 *)
|
||||
let crate_opaque_ty = Llvm.opaque_type llctx in
|
||||
let crate_tyhandle = Llvm.handle_to_type (Llvm.struct_type llctx [|
|
||||
i32; (* ptrdiff_t image_base_off *)
|
||||
Llvm.pointer_type crate_opaque_ty;(* uintptr_t self_addr *)
|
||||
i32; (* ptrdiff_t debug_abbrev_off *)
|
||||
i32; (* size_t debug_abbrev_sz *)
|
||||
i32; (* ptrdiff_t debug_info_off *)
|
||||
i32; (* size_t debug_info_sz *)
|
||||
i32; (* size_t activate_glue_off *)
|
||||
i32; (* size_t main_exit_task_glue_off *)
|
||||
i32; (* size_t unwind_glue_off *)
|
||||
i32; (* size_t yield_glue_off *)
|
||||
i32; (* int n_rust_syms *)
|
||||
i32; (* int n_c_syms *)
|
||||
i32 (* int n_libs *)
|
||||
|])
|
||||
in
|
||||
Llvm.refine_type crate_opaque_ty (Llvm.type_of_handle crate_tyhandle);
|
||||
Llvm.type_of_handle crate_tyhandle
|
||||
in
|
||||
ignore (Llvm.define_type_name "rust_crate" crate_ty llmod);
|
||||
|
||||
let task_ty =
|
||||
(* TODO: other architectures besides x86 *)
|
||||
Llvm.struct_type llctx [|
|
||||
i32; (* size_t refcnt *)
|
||||
Llvm.pointer_type i32; (* stk_seg *stk *)
|
||||
Llvm.pointer_type i32; (* uintptr_t runtime_sp *)
|
||||
Llvm.pointer_type i32; (* uintptr_t rust_sp *)
|
||||
Llvm.pointer_type i32; (* rust_rt *rt *)
|
||||
Llvm.pointer_type i32 (* rust_crate_cache *cache *)
|
||||
|]
|
||||
in
|
||||
ignore (Llvm.define_type_name "rust_task" task_ty llmod);
|
||||
|
||||
let rust_start_ty =
|
||||
let task_ptr_ty = Llvm.pointer_type task_ty in
|
||||
let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
|
||||
let main_ty = Llvm.function_type (Llvm.void_type llctx)
|
||||
[| Llvm.pointer_type llnilty; task_ptr_ty; |]
|
||||
in
|
||||
let args_ty = Array.map Llvm.pointer_type [| main_ty; crate_ty; |] in
|
||||
let args_ty = Array.append args_ty [| i32; i32 |] in
|
||||
Llvm.function_type i32 args_ty
|
||||
in
|
||||
{
|
||||
crate_ty = crate_ty;
|
||||
task_ty = task_ty;
|
||||
word_ty = i32;
|
||||
rust_start = Llvm.declare_function "rust_start" rust_start_ty llmod
|
||||
}
|
||||
;;
|
||||
|
192
src/boot/llvm/llasm.ml
Normal file
192
src/boot/llvm/llasm.ml
Normal file
@ -0,0 +1,192 @@
|
||||
(*
|
||||
* machine-specific assembler routines.
|
||||
*)
|
||||
|
||||
open Common;;
|
||||
|
||||
type asm_glue =
|
||||
{
|
||||
asm_activate_glue : Llvm.llvalue;
|
||||
asm_yield_glue : Llvm.llvalue;
|
||||
asm_upcall_glues : Llvm.llvalue array;
|
||||
}
|
||||
;;
|
||||
|
||||
let n_upcall_glues = 7
|
||||
;;
|
||||
|
||||
(* x86-specific asm. *)
|
||||
|
||||
let x86_glue
|
||||
(llctx:Llvm.llcontext)
|
||||
(llmod:Llvm.llmodule)
|
||||
(abi:Llabi.abi)
|
||||
(sess:Session.sess)
|
||||
: asm_glue =
|
||||
let (prefix,align) =
|
||||
match sess.Session.sess_targ with
|
||||
Linux_x86_elf
|
||||
| Win32_x86_pe -> ("",4)
|
||||
| MacOS_x86_macho -> ("_", 16)
|
||||
in
|
||||
let save_callee_saves =
|
||||
["pushl %ebp";
|
||||
"pushl %edi";
|
||||
"pushl %esi";
|
||||
"pushl %ebx";]
|
||||
in
|
||||
let restore_callee_saves =
|
||||
["popl %ebx";
|
||||
"popl %esi";
|
||||
"popl %edi";
|
||||
"popl %ebp";]
|
||||
in
|
||||
let load_esp_from_rust_sp = ["movl 12(%edx), %esp"] in
|
||||
let load_esp_from_runtime_sp = ["movl 8(%edx), %esp"] in
|
||||
let store_esp_to_rust_sp = ["movl %esp, 12(%edx)"] in
|
||||
let store_esp_to_runtime_sp = ["movl %esp, 8(%edx)"] in
|
||||
let list_init i f = (Array.to_list (Array.init i f)) in
|
||||
let list_init_concat i f = List.concat (list_init i f) in
|
||||
|
||||
let glue =
|
||||
[
|
||||
("rust_activate_glue",
|
||||
String.concat "\n\t"
|
||||
(["movl 4(%esp), %edx # edx = rust_task"]
|
||||
@ save_callee_saves
|
||||
@ store_esp_to_runtime_sp
|
||||
@ load_esp_from_rust_sp
|
||||
(*
|
||||
* This 'add' instruction is a bit surprising.
|
||||
* See lengthy comment in boot/be/x86.ml activate_glue.
|
||||
*)
|
||||
@ ["addl $20, 12(%edx)"]
|
||||
@ restore_callee_saves
|
||||
@ ["ret"]));
|
||||
|
||||
("rust_yield_glue",
|
||||
String.concat "\n\t"
|
||||
|
||||
(["movl 0(%esp), %edx # edx = rust_task"]
|
||||
@ load_esp_from_rust_sp
|
||||
@ save_callee_saves
|
||||
@ store_esp_to_rust_sp
|
||||
@ load_esp_from_runtime_sp
|
||||
@ restore_callee_saves
|
||||
@ ["ret"]))
|
||||
]
|
||||
@ list_init n_upcall_glues
|
||||
begin
|
||||
fun i ->
|
||||
(*
|
||||
* 0, 4, 8, 12 are callee-saves
|
||||
* 16 is retpc
|
||||
* 20 is taskptr
|
||||
* 24 is callee
|
||||
* 28 .. (7+i) * 4 are args
|
||||
*)
|
||||
|
||||
((Printf.sprintf "rust_upcall_%d" i),
|
||||
String.concat "\n\t"
|
||||
(save_callee_saves
|
||||
@ ["movl %esp, %ebp # ebp = rust_sp";
|
||||
"movl 20(%esp), %edx # edx = rust_task"]
|
||||
@ store_esp_to_rust_sp
|
||||
@ load_esp_from_runtime_sp
|
||||
@ [Printf.sprintf
|
||||
"subl $%d, %%esp # esp -= args" ((i+1)*4);
|
||||
"andl $~0xf, %esp # align esp down";
|
||||
"movl %edx, (%esp) # arg[0] = rust_task "]
|
||||
|
||||
@ (list_init_concat i
|
||||
begin
|
||||
fun j ->
|
||||
[ Printf.sprintf "movl %d(%%ebp),%%edx" ((j+7)*4);
|
||||
Printf.sprintf "movl %%edx,%d(%%esp)" ((j+1)*4) ]
|
||||
end)
|
||||
|
||||
@ ["movl 24(%ebp), %edx # edx = callee";
|
||||
"call *%edx # call *%edx";
|
||||
"movl 20(%ebp), %edx # edx = rust_task"]
|
||||
@ load_esp_from_rust_sp
|
||||
@ restore_callee_saves
|
||||
@ ["ret"]))
|
||||
end
|
||||
in
|
||||
|
||||
let _ =
|
||||
Llvm.set_module_inline_asm llmod
|
||||
begin
|
||||
String.concat "\n"
|
||||
begin
|
||||
List.map
|
||||
begin
|
||||
fun (sym,asm) ->
|
||||
Printf.sprintf
|
||||
"\t.globl %s%s\n\t.balign %d\n%s%s:\n\t%s"
|
||||
prefix sym align prefix sym asm
|
||||
end
|
||||
glue
|
||||
end
|
||||
end
|
||||
in
|
||||
|
||||
let decl_cdecl_fn name out_ty arg_tys =
|
||||
let ty = Llvm.function_type out_ty arg_tys in
|
||||
let fn = Llvm.declare_function name ty llmod in
|
||||
Llvm.set_function_call_conv Llvm.CallConv.c fn;
|
||||
fn
|
||||
in
|
||||
|
||||
let decl_glue s =
|
||||
let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
|
||||
let void_ty = Llvm.void_type llctx in
|
||||
decl_cdecl_fn s void_ty [| task_ptr_ty |]
|
||||
in
|
||||
|
||||
let decl_upcall n =
|
||||
let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
|
||||
let word_ty = abi.Llabi.word_ty in
|
||||
let callee_ty = word_ty in
|
||||
let args_ty =
|
||||
Array.append
|
||||
[| task_ptr_ty; callee_ty |]
|
||||
(Array.init n (fun _ -> word_ty))
|
||||
in
|
||||
let name = Printf.sprintf "rust_upcall_%d" n in
|
||||
decl_cdecl_fn name word_ty args_ty
|
||||
in
|
||||
{
|
||||
asm_activate_glue = decl_glue "rust_activate_glue";
|
||||
asm_yield_glue = decl_glue "rust_yield_glue";
|
||||
asm_upcall_glues = Array.init n_upcall_glues decl_upcall;
|
||||
}
|
||||
;;
|
||||
|
||||
(* x64-specific asm. *)
|
||||
(* arm-specific asm. *)
|
||||
(* ... *)
|
||||
|
||||
|
||||
let get_glue
|
||||
(llctx:Llvm.llcontext)
|
||||
(llmod:Llvm.llmodule)
|
||||
(abi:Llabi.abi)
|
||||
(sess:Session.sess)
|
||||
: asm_glue =
|
||||
match sess.Session.sess_targ with
|
||||
Linux_x86_elf
|
||||
| Win32_x86_pe
|
||||
| MacOS_x86_macho ->
|
||||
x86_glue llctx llmod abi sess
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
36
src/boot/llvm/llemit.ml
Normal file
36
src/boot/llvm/llemit.ml
Normal file
@ -0,0 +1,36 @@
|
||||
(*
|
||||
* LLVM emitter.
|
||||
*)
|
||||
|
||||
(* The top-level interface to the LLVM translation subsystem. *)
|
||||
let trans_and_process_crate
|
||||
(sess:Session.sess)
|
||||
(sem_cx:Semant.ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let llcontext = Llvm.create_context () in
|
||||
let emit_file (llmod:Llvm.llmodule) : unit =
|
||||
let filename = Session.filename_of sess.Session.sess_out in
|
||||
if not (Llvm_bitwriter.write_bitcode_file llmod filename)
|
||||
then raise (Failure ("failed to write the LLVM bitcode '" ^ filename
|
||||
^ "'"))
|
||||
in
|
||||
let llmod = Lltrans.trans_crate sem_cx llcontext sess crate in
|
||||
begin
|
||||
try
|
||||
emit_file llmod
|
||||
with e -> Llvm.dispose_module llmod; raise e
|
||||
end;
|
||||
Llvm.dispose_module llmod;
|
||||
Llvm.dispose_context llcontext
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
96
src/boot/llvm/llfinal.ml
Normal file
96
src/boot/llvm/llfinal.ml
Normal file
@ -0,0 +1,96 @@
|
||||
(*
|
||||
* LLVM ABI-level stuff that needs to happen after modules have been
|
||||
* translated.
|
||||
*)
|
||||
|
||||
let finalize_module
|
||||
(llctx:Llvm.llcontext)
|
||||
(llmod:Llvm.llmodule)
|
||||
(abi:Llabi.abi)
|
||||
(asm_glue:Llasm.asm_glue)
|
||||
(exit_task_glue:Llvm.llvalue)
|
||||
(crate_ptr:Llvm.llvalue)
|
||||
: unit =
|
||||
let i32 = Llvm.i32_type llctx in
|
||||
|
||||
(*
|
||||
* Count the number of Rust functions and the number of C functions by
|
||||
* simply (and crudely) testing whether each function in the module begins
|
||||
* with "_rust_".
|
||||
*)
|
||||
|
||||
let (rust_fn_count, c_fn_count) =
|
||||
let count (rust_fn_count, c_fn_count) fn =
|
||||
let begins_with prefix str =
|
||||
let (str_len, prefix_len) =
|
||||
(String.length str, String.length prefix)
|
||||
in
|
||||
prefix_len <= str_len && (String.sub str 0 prefix_len) = prefix
|
||||
in
|
||||
if begins_with "_rust_" (Llvm.value_name fn) then
|
||||
(rust_fn_count + 1, c_fn_count)
|
||||
else
|
||||
(rust_fn_count, c_fn_count + 1)
|
||||
in
|
||||
Llvm.fold_left_functions count (0, 0) llmod
|
||||
in
|
||||
|
||||
let crate_val =
|
||||
let crate_addr = Llvm.const_ptrtoint crate_ptr i32 in
|
||||
let glue_off glue =
|
||||
let addr = Llvm.const_ptrtoint glue i32 in
|
||||
Llvm.const_sub addr crate_addr
|
||||
in
|
||||
let activate_glue_off = glue_off asm_glue.Llasm.asm_activate_glue in
|
||||
let yield_glue_off = glue_off asm_glue.Llasm.asm_yield_glue in
|
||||
let exit_task_glue_off = glue_off exit_task_glue in
|
||||
|
||||
Llvm.const_struct llctx [|
|
||||
Llvm.const_int i32 0; (* ptrdiff_t image_base_off *)
|
||||
crate_ptr; (* uintptr_t self_addr *)
|
||||
Llvm.const_int i32 0; (* ptrdiff_t debug_abbrev_off *)
|
||||
Llvm.const_int i32 0; (* size_t debug_abbrev_sz *)
|
||||
Llvm.const_int i32 0; (* ptrdiff_t debug_info_off *)
|
||||
Llvm.const_int i32 0; (* size_t debug_info_sz *)
|
||||
activate_glue_off; (* size_t activate_glue_off *)
|
||||
exit_task_glue_off; (* size_t main_exit_task_glue_off *)
|
||||
Llvm.const_int i32 0; (* size_t unwind_glue_off *)
|
||||
yield_glue_off; (* size_t yield_glue_off *)
|
||||
Llvm.const_int i32 rust_fn_count; (* int n_rust_syms *)
|
||||
Llvm.const_int i32 c_fn_count; (* int n_c_syms *)
|
||||
Llvm.const_int i32 0 (* int n_libs *)
|
||||
|]
|
||||
in
|
||||
|
||||
Llvm.set_initializer crate_val crate_ptr;
|
||||
|
||||
(* Define the main function for crt0 to call. *)
|
||||
let main_fn =
|
||||
let main_ty = Llvm.function_type i32 [| i32; i32 |] in
|
||||
Llvm.define_function "main" main_ty llmod
|
||||
in
|
||||
let argc = Llvm.param main_fn 0 in
|
||||
let argv = Llvm.param main_fn 1 in
|
||||
let main_builder = Llvm.builder_at_end llctx (Llvm.entry_block main_fn) in
|
||||
let rust_main_fn =
|
||||
match Llvm.lookup_function "_rust_main" llmod with
|
||||
None -> raise (Failure "no main function found")
|
||||
| Some fn -> fn
|
||||
in
|
||||
let rust_start = abi.Llabi.rust_start in
|
||||
let rust_start_args = [| rust_main_fn; crate_ptr; argc; argv |] in
|
||||
ignore (Llvm.build_call
|
||||
rust_start rust_start_args "start_rust" main_builder);
|
||||
ignore (Llvm.build_ret (Llvm.const_int i32 0) main_builder)
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
938
src/boot/llvm/lltrans.ml
Normal file
938
src/boot/llvm/lltrans.ml
Normal file
@ -0,0 +1,938 @@
|
||||
(*
|
||||
* LLVM translator.
|
||||
*)
|
||||
|
||||
open Common;;
|
||||
open Transutil;;
|
||||
|
||||
let log cx = Session.log "trans"
|
||||
cx.Semant.ctxt_sess.Session.sess_log_trans
|
||||
cx.Semant.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let trans_crate
|
||||
(sem_cx:Semant.ctxt)
|
||||
(llctx:Llvm.llcontext)
|
||||
(sess:Session.sess)
|
||||
(crate:Ast.crate)
|
||||
: Llvm.llmodule =
|
||||
|
||||
let iflog thunk =
|
||||
if sess.Session.sess_log_trans
|
||||
then thunk ()
|
||||
else ()
|
||||
in
|
||||
|
||||
(* Helpers for adding metadata. *)
|
||||
let (dbg_mdkind:int) = Llvm.mdkind_id llctx "dbg" in
|
||||
let set_dbg_metadata (inst:Llvm.llvalue) (md:Llvm.llvalue) : unit =
|
||||
Llvm.set_metadata inst dbg_mdkind md
|
||||
in
|
||||
let md_str (s:string) : Llvm.llvalue = Llvm.mdstring llctx s in
|
||||
let md_node (vals:Llvm.llvalue array) : Llvm.llvalue =
|
||||
Llvm.mdnode llctx vals
|
||||
in
|
||||
let const_i32 (i:int) : Llvm.llvalue =
|
||||
Llvm.const_int (Llvm.i32_type llctx) i
|
||||
in
|
||||
let const_i1 (i:int) : Llvm.llvalue =
|
||||
Llvm.const_int (Llvm.i1_type llctx) i
|
||||
in
|
||||
let llvm_debug_version : int = 0x8 lsl 16 in
|
||||
let const_dw_tag (tag:Dwarf.dw_tag) : Llvm.llvalue =
|
||||
const_i32 (llvm_debug_version lor (Dwarf.dw_tag_to_int tag))
|
||||
in
|
||||
|
||||
(* Translation of our node_ids into LLVM identifiers, which are strings. *)
|
||||
let next_anon_llid = ref 0 in
|
||||
let num_llid num klass = Printf.sprintf "%s%d" klass num in
|
||||
let anon_llid klass =
|
||||
let llid = num_llid !next_anon_llid klass in
|
||||
next_anon_llid := !next_anon_llid + 1;
|
||||
llid
|
||||
in
|
||||
let node_llid (node_id_opt:node_id option) : (string -> string) =
|
||||
match node_id_opt with
|
||||
None -> anon_llid
|
||||
| Some (Node num) -> num_llid num
|
||||
in
|
||||
|
||||
(*
|
||||
* Returns a bogus value for use in stub code that hasn't been implemented
|
||||
* yet.
|
||||
*
|
||||
* TODO: On some joyous day, remove me.
|
||||
*)
|
||||
let bogus = Llvm.const_null (Llvm.i32_type llctx) in
|
||||
let bogus_ptr = Llvm.const_null (Llvm.pointer_type (Llvm.i32_type llctx)) in
|
||||
|
||||
let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
|
||||
let llnil = Llvm.const_array (Llvm.i1_type llctx) [| |] in
|
||||
|
||||
let ty_of_item = Hashtbl.find sem_cx.Semant.ctxt_all_item_types in
|
||||
let ty_of_slot n = Semant.slot_ty (Semant.get_slot sem_cx n) in
|
||||
|
||||
let filename = Session.filename_of sess.Session.sess_in in
|
||||
let llmod = Llvm.create_module llctx filename in
|
||||
|
||||
let (abi:Llabi.abi) = Llabi.declare_abi llctx llmod in
|
||||
let (crate_ptr:Llvm.llvalue) =
|
||||
Llvm.declare_global abi.Llabi.crate_ty "rust_crate" llmod
|
||||
in
|
||||
|
||||
let (void_ty:Llvm.lltype) = Llvm.void_type llctx in
|
||||
let (word_ty:Llvm.lltype) = abi.Llabi.word_ty in
|
||||
let (wordptr_ty:Llvm.lltype) = Llvm.pointer_type word_ty in
|
||||
let (task_ty:Llvm.lltype) = abi.Llabi.task_ty in
|
||||
let (task_ptr_ty:Llvm.lltype) = Llvm.pointer_type task_ty in
|
||||
let fn_ty (out:Llvm.lltype) (args:Llvm.lltype array) : Llvm.lltype =
|
||||
Llvm.function_type out args
|
||||
in
|
||||
|
||||
let imm (i:int64) : Llvm.llvalue =
|
||||
Llvm.const_int word_ty (Int64.to_int i)
|
||||
in
|
||||
|
||||
let asm_glue = Llasm.get_glue llctx llmod abi sess in
|
||||
|
||||
let llty_str llty =
|
||||
Llvm.string_of_lltype llty
|
||||
in
|
||||
|
||||
let llval_str llv =
|
||||
let ts = llty_str (Llvm.type_of llv) in
|
||||
match Llvm.value_name llv with
|
||||
"" ->
|
||||
Printf.sprintf "<anon=%s>" ts
|
||||
| s -> Printf.sprintf "<%s=%s>" s ts
|
||||
in
|
||||
|
||||
let llvals_str llvals =
|
||||
(String.concat ", "
|
||||
(Array.to_list
|
||||
(Array.map llval_str llvals)))
|
||||
in
|
||||
|
||||
let build_call callee args rvid builder =
|
||||
iflog
|
||||
begin
|
||||
fun _ ->
|
||||
let name = Llvm.value_name callee in
|
||||
log sem_cx "build_call: %s(%s)" name (llvals_str args);
|
||||
log sem_cx "build_call: typeof(%s) = %s"
|
||||
name (llty_str (Llvm.type_of callee))
|
||||
end;
|
||||
Llvm.build_call callee args rvid builder
|
||||
in
|
||||
|
||||
(* Upcall translation *)
|
||||
|
||||
let extern_upcalls = Hashtbl.create 0 in
|
||||
let trans_upcall
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(lltask:Llvm.llvalue)
|
||||
(name:string)
|
||||
(lldest:Llvm.llvalue option)
|
||||
(llargs:Llvm.llvalue array) =
|
||||
let n = Array.length llargs in
|
||||
let llglue = asm_glue.Llasm.asm_upcall_glues.(n) in
|
||||
let llupcall = htab_search_or_add extern_upcalls name
|
||||
begin
|
||||
fun _ ->
|
||||
let args_ty =
|
||||
Array.append
|
||||
[| task_ptr_ty |]
|
||||
(Array.init n (fun i -> Llvm.type_of llargs.(i)))
|
||||
in
|
||||
let out_ty = match lldest with
|
||||
None -> void_ty
|
||||
| Some v -> Llvm.type_of v
|
||||
in
|
||||
let fty = fn_ty out_ty args_ty in
|
||||
(*
|
||||
* NB: At this point it actually doesn't matter what type
|
||||
* we gave the upcall function, as we're just going to
|
||||
* pointercast it to a word and pass it to the upcall-glue
|
||||
* for now. But possibly in the future it might matter if
|
||||
* we develop a proper upcall calling convention.
|
||||
*)
|
||||
Llvm.declare_function name fty llmod
|
||||
end
|
||||
in
|
||||
(* Cast everything to plain words so we can hand off to the glue. *)
|
||||
let llupcall = Llvm.const_pointercast llupcall word_ty in
|
||||
let llargs =
|
||||
Array.map
|
||||
(fun arg ->
|
||||
Llvm.build_pointercast arg word_ty
|
||||
(anon_llid "arg") llbuilder)
|
||||
llargs
|
||||
in
|
||||
let llallargs = Array.append [| lltask; llupcall |] llargs in
|
||||
let llid = anon_llid "rv" in
|
||||
let llrv = build_call llglue llallargs llid llbuilder in
|
||||
Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
|
||||
match lldest with
|
||||
None -> ()
|
||||
| Some lldest ->
|
||||
let lldest =
|
||||
Llvm.build_pointercast lldest wordptr_ty "" llbuilder
|
||||
in
|
||||
ignore (Llvm.build_store llrv lldest llbuilder);
|
||||
in
|
||||
|
||||
let upcall
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(lltask:Llvm.llvalue)
|
||||
(name:string)
|
||||
(lldest:Llvm.llvalue option)
|
||||
(llargs:Llvm.llvalue array)
|
||||
: unit =
|
||||
trans_upcall llbuilder lltask name lldest llargs
|
||||
in
|
||||
|
||||
let trans_free
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(lltask:Llvm.llvalue)
|
||||
(src:Llvm.llvalue)
|
||||
: unit =
|
||||
upcall llbuilder lltask "upcall_free" None [| src |]
|
||||
in
|
||||
|
||||
(*
|
||||
* let trans_malloc (llbuilder:Llvm.llbuilder)
|
||||
* (dst:Llvm.llvalue) (nbytes:int64) : unit =
|
||||
* upcall llbuilder "upcall_malloc" (Some dst) [| imm nbytes |]
|
||||
* in
|
||||
*)
|
||||
|
||||
(* Type translation *)
|
||||
|
||||
let lltys = Hashtbl.create 0 in
|
||||
|
||||
let trans_mach_ty (mty:ty_mach) : Llvm.lltype =
|
||||
let tycon =
|
||||
match mty with
|
||||
TY_u8 | TY_i8 -> Llvm.i8_type
|
||||
| TY_u16 | TY_i16 -> Llvm.i16_type
|
||||
| TY_u32 | TY_i32 -> Llvm.i32_type
|
||||
| TY_u64 | TY_i64 -> Llvm.i64_type
|
||||
| TY_f32 -> Llvm.float_type
|
||||
| TY_f64 -> Llvm.double_type
|
||||
in
|
||||
tycon llctx
|
||||
in
|
||||
|
||||
|
||||
let rec trans_ty_full (ty:Ast.ty) : Llvm.lltype =
|
||||
let p t = Llvm.pointer_type t in
|
||||
let s ts = Llvm.struct_type llctx ts in
|
||||
let opaque _ = Llvm.opaque_type llctx in
|
||||
let vec_body_ty _ =
|
||||
s [| word_ty; word_ty; word_ty; (opaque()) |]
|
||||
in
|
||||
let rc_opaque_ty =
|
||||
s [| word_ty; (opaque()) |]
|
||||
in
|
||||
match ty with
|
||||
Ast.TY_any -> opaque ()
|
||||
| Ast.TY_nil -> llnilty
|
||||
| Ast.TY_bool -> Llvm.i1_type llctx
|
||||
| Ast.TY_mach mty -> trans_mach_ty mty
|
||||
| Ast.TY_int -> word_ty
|
||||
| Ast.TY_uint -> word_ty
|
||||
| Ast.TY_char -> Llvm.i32_type llctx
|
||||
| Ast.TY_vec _
|
||||
| Ast.TY_str -> p (vec_body_ty())
|
||||
|
||||
| Ast.TY_fn tfn ->
|
||||
let (tsig, _) = tfn in
|
||||
let lloutptr = p (trans_slot None tsig.Ast.sig_output_slot) in
|
||||
let lltaskty = p abi.Llabi.task_ty in
|
||||
let llins = Array.map (trans_slot None) tsig.Ast.sig_input_slots in
|
||||
fn_ty void_ty (Array.append [| lloutptr; lltaskty |] llins)
|
||||
|
||||
| Ast.TY_tup slots ->
|
||||
s (Array.map (trans_slot None) slots)
|
||||
|
||||
| Ast.TY_rec entries ->
|
||||
s (Array.map (fun e -> trans_slot None (snd e)) entries)
|
||||
|
||||
| Ast.TY_constrained (ty', _) -> trans_ty ty'
|
||||
|
||||
| Ast.TY_chan _ | Ast.TY_port _ | Ast.TY_task ->
|
||||
p rc_opaque_ty
|
||||
|
||||
| Ast.TY_native _ ->
|
||||
word_ty
|
||||
|
||||
| Ast.TY_tag _ | Ast.TY_iso _ | Ast.TY_idx _
|
||||
| Ast.TY_obj _ | Ast.TY_type -> (opaque()) (* TODO *)
|
||||
|
||||
| Ast.TY_param _ | Ast.TY_named _ ->
|
||||
bug () "unresolved type in lltrans"
|
||||
|
||||
and trans_ty t =
|
||||
htab_search_or_add lltys t (fun _ -> trans_ty_full t)
|
||||
|
||||
(* Translates the type of a slot into the corresponding LLVM type. If the
|
||||
* id_opt parameter is specified, then the type will be fetched from the
|
||||
* context. *)
|
||||
and trans_slot (id_opt:node_id option) (slot:Ast.slot) : Llvm.lltype =
|
||||
let ty =
|
||||
match id_opt with
|
||||
Some id -> ty_of_slot id
|
||||
| None -> Semant.slot_ty slot
|
||||
in
|
||||
let base_llty = trans_ty ty in
|
||||
match slot.Ast.slot_mode with
|
||||
Ast.MODE_exterior _
|
||||
| Ast.MODE_alias _ ->
|
||||
Llvm.pointer_type base_llty
|
||||
| Ast.MODE_interior _ -> base_llty
|
||||
in
|
||||
|
||||
let get_element_ptr
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(ptr:Llvm.llvalue)
|
||||
(i:int)
|
||||
: Llvm.llvalue =
|
||||
(*
|
||||
* GEP takes a first-index of zero. Because it must! And this is
|
||||
* sufficiently surprising that the GEP FAQ exists. And you must
|
||||
* read it.
|
||||
*)
|
||||
let deref_ptr = Llvm.const_int (Llvm.i32_type llctx) 0 in
|
||||
let idx = Llvm.const_int (Llvm.i32_type llctx) i in
|
||||
Llvm.build_gep ptr [| deref_ptr; idx |] (anon_llid "gep") llbuilder
|
||||
in
|
||||
|
||||
let free_ty
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(lltask:Llvm.llvalue)
|
||||
(ty:Ast.ty)
|
||||
(ptr:Llvm.llvalue)
|
||||
: unit =
|
||||
match ty with
|
||||
Ast.TY_port _
|
||||
| Ast.TY_chan _
|
||||
| Ast.TY_task -> bug () "unimplemented ty in Lltrans.free_ty"
|
||||
| _ -> trans_free llbuilder lltask ptr
|
||||
in
|
||||
|
||||
let rec iter_ty_slots_full
|
||||
(llbuilder:Llvm.llbuilder ref)
|
||||
(ty:Ast.ty)
|
||||
(dst_ptr:Llvm.llvalue)
|
||||
(src_ptr:Llvm.llvalue)
|
||||
(f:(Llvm.llvalue
|
||||
-> Llvm.llvalue
|
||||
-> Ast.slot
|
||||
-> (Ast.ty_iso option)
|
||||
-> unit))
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
|
||||
(* NB: must deref llbuilder at call-time; don't curry this. *)
|
||||
let gep p i = get_element_ptr (!llbuilder) p i in
|
||||
|
||||
match ty with
|
||||
Ast.TY_rec entries ->
|
||||
iter_rec_slots gep dst_ptr src_ptr entries f curr_iso
|
||||
|
||||
| Ast.TY_tup slots ->
|
||||
iter_tup_slots gep dst_ptr src_ptr slots f curr_iso
|
||||
|
||||
| Ast.TY_tag _
|
||||
| Ast.TY_iso _
|
||||
| Ast.TY_fn _
|
||||
| Ast.TY_obj _ ->
|
||||
bug () "unimplemented ty in Lltrans.iter_ty_slots_full"
|
||||
|
||||
| _ -> ()
|
||||
|
||||
and iter_ty_slots
|
||||
(llbuilder:Llvm.llbuilder ref)
|
||||
(ty:Ast.ty)
|
||||
(ptr:Llvm.llvalue)
|
||||
(f:Llvm.llvalue -> Ast.slot -> (Ast.ty_iso option) -> unit)
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
iter_ty_slots_full llbuilder ty ptr ptr
|
||||
(fun _ src_ptr slot curr_iso -> f src_ptr slot curr_iso)
|
||||
curr_iso
|
||||
|
||||
and drop_ty
|
||||
(llbuilder:Llvm.llbuilder ref)
|
||||
(lltask:Llvm.llvalue)
|
||||
(ty:Ast.ty)
|
||||
(ptr:Llvm.llvalue)
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
iter_ty_slots llbuilder ty ptr (drop_slot llbuilder lltask) curr_iso
|
||||
|
||||
and drop_slot
|
||||
(llbuilder:Llvm.llbuilder ref)
|
||||
(lltask:Llvm.llvalue)
|
||||
(slot_ptr:Llvm.llvalue)
|
||||
(slot:Ast.slot)
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
|
||||
let llfn = Llvm.block_parent (Llvm.insertion_block (!llbuilder)) in
|
||||
let llty = trans_slot None slot in
|
||||
let ty = Semant.slot_ty slot in
|
||||
|
||||
let new_block klass =
|
||||
let llblock = Llvm.append_block llctx (anon_llid klass) llfn in
|
||||
let llbuilder = Llvm.builder_at_end llctx llblock in
|
||||
(llblock, llbuilder)
|
||||
in
|
||||
|
||||
let if_ptr_in_slot_not_null
|
||||
(inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
: Llvm.llbuilder =
|
||||
let ptr = Llvm.build_load slot_ptr (anon_llid "tmp") llbuilder in
|
||||
let null = Llvm.const_pointer_null llty in
|
||||
let test =
|
||||
Llvm.build_icmp Llvm.Icmp.Ne null ptr (anon_llid "nullp") llbuilder
|
||||
in
|
||||
let (llthen, llthen_builder) = new_block "then" in
|
||||
let (llnext, llnext_builder) = new_block "next" in
|
||||
ignore (Llvm.build_cond_br test llthen llnext llbuilder);
|
||||
let llthen_builder = inner ptr llthen_builder in
|
||||
ignore (Llvm.build_br llnext llthen_builder);
|
||||
llnext_builder
|
||||
in
|
||||
|
||||
let decr_refcnt_and_if_zero
|
||||
(rc_elt:int)
|
||||
(inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
|
||||
(ptr:Llvm.llvalue)
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
: Llvm.llbuilder =
|
||||
let rc_ptr = get_element_ptr llbuilder ptr rc_elt in
|
||||
let rc = Llvm.build_load rc_ptr (anon_llid "rc") llbuilder in
|
||||
let rc = Llvm.build_sub rc (imm 1L) (anon_llid "tmp") llbuilder in
|
||||
let _ = Llvm.build_store rc rc_ptr llbuilder in
|
||||
log sem_cx "rc type: %s" (llval_str rc);
|
||||
let test =
|
||||
Llvm.build_icmp Llvm.Icmp.Eq
|
||||
rc (imm 0L) (anon_llid "zerop") llbuilder
|
||||
in
|
||||
let (llthen, llthen_builder) = new_block "then" in
|
||||
let (llnext, llnext_builder) = new_block "next" in
|
||||
ignore (Llvm.build_cond_br test llthen llnext llbuilder);
|
||||
let llthen_builder = inner ptr llthen_builder in
|
||||
ignore (Llvm.build_br llnext llthen_builder);
|
||||
llnext_builder
|
||||
in
|
||||
|
||||
let free_and_null_out_slot
|
||||
(ptr:Llvm.llvalue)
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
: Llvm.llbuilder =
|
||||
free_ty llbuilder lltask ty ptr;
|
||||
let null = Llvm.const_pointer_null llty in
|
||||
ignore (Llvm.build_store null slot_ptr llbuilder);
|
||||
llbuilder
|
||||
in
|
||||
|
||||
begin
|
||||
match slot_mem_ctrl slot with
|
||||
MEM_rc_struct
|
||||
| MEM_gc ->
|
||||
llbuilder :=
|
||||
if_ptr_in_slot_not_null
|
||||
(decr_refcnt_and_if_zero
|
||||
Abi.exterior_rc_slot_field_refcnt
|
||||
free_and_null_out_slot)
|
||||
(!llbuilder)
|
||||
|
||||
| MEM_rc_opaque ->
|
||||
llbuilder :=
|
||||
if_ptr_in_slot_not_null
|
||||
(decr_refcnt_and_if_zero
|
||||
Abi.exterior_rc_slot_field_refcnt
|
||||
free_and_null_out_slot)
|
||||
(!llbuilder)
|
||||
|
||||
| MEM_interior when Semant.type_is_structured ty ->
|
||||
(* FIXME: to handle recursive types, need to call drop
|
||||
glue here, not inline. *)
|
||||
drop_ty llbuilder lltask ty slot_ptr curr_iso
|
||||
|
||||
| _ -> ()
|
||||
end
|
||||
in
|
||||
|
||||
let (llitems:(node_id, Llvm.llvalue) Hashtbl.t) = Hashtbl.create 0 in
|
||||
let declare_mod_item
|
||||
(name:Ast.ident)
|
||||
{ node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
|
||||
: unit =
|
||||
let full_name = Semant.item_str sem_cx id in
|
||||
let line_num =
|
||||
match Session.get_span sess id with
|
||||
None -> 0
|
||||
| Some span ->
|
||||
let (_, line, _) = span.lo in
|
||||
line
|
||||
in
|
||||
match item with
|
||||
Ast.MOD_ITEM_fn _ ->
|
||||
let llty = trans_ty (ty_of_item id) in
|
||||
let llfn = Llvm.declare_function ("_rust_" ^ name) llty llmod in
|
||||
let meta =
|
||||
md_node
|
||||
[|
|
||||
const_dw_tag Dwarf.DW_TAG_subprogram;
|
||||
const_i32 0; (* unused *)
|
||||
const_i32 0; (* context metadata llvalue *)
|
||||
md_str name;
|
||||
md_str full_name;
|
||||
md_str full_name;
|
||||
const_i32 0; (* file metadata llvalue *)
|
||||
const_i32 line_num;
|
||||
const_i32 0; (* type descriptor metadata llvalue *)
|
||||
const_i1 1; (* flag: local to compile unit? *)
|
||||
const_i1 1; (* flag: defined in compile unit? *)
|
||||
|]
|
||||
in
|
||||
Llvm.set_function_call_conv Llvm.CallConv.c llfn;
|
||||
Hashtbl.add llitems id llfn;
|
||||
|
||||
(* FIXME: Adding metadata does not work yet. . *)
|
||||
let _ = fun _ -> set_dbg_metadata llfn meta in
|
||||
()
|
||||
|
||||
| _ -> () (* TODO *)
|
||||
in
|
||||
|
||||
let trans_fn
|
||||
({
|
||||
Ast.fn_input_slots = (header_slots:Ast.header_slots);
|
||||
Ast.fn_body = (body:Ast.block)
|
||||
}:Ast.fn)
|
||||
(fn_id:node_id)
|
||||
: unit =
|
||||
let llfn = Hashtbl.find llitems fn_id in
|
||||
let lloutptr = Llvm.param llfn 0 in
|
||||
let lltask = Llvm.param llfn 1 in
|
||||
|
||||
(* LLVM requires that functions be grouped into basic blocks terminated by
|
||||
* terminator instructions, while our AST is less strict. So we have to do
|
||||
* a little trickery here to wrangle the statement sequence into LLVM's
|
||||
* format. *)
|
||||
|
||||
let new_block id_opt klass =
|
||||
let llblock = Llvm.append_block llctx (node_llid id_opt klass) llfn in
|
||||
let llbuilder = Llvm.builder_at_end llctx llblock in
|
||||
(llblock, llbuilder)
|
||||
in
|
||||
|
||||
(* Build up the slot-to-llvalue mapping, allocating space along the
|
||||
* way. *)
|
||||
let slot_to_llvalue = Hashtbl.create 0 in
|
||||
let (_, llinitbuilder) = new_block None "init" in
|
||||
|
||||
(* Allocate space for arguments (needed because arguments are lvalues in
|
||||
* Rust), and store them in the slot-to-llvalue mapping. *)
|
||||
let n_implicit_args = 2 in
|
||||
let build_arg idx llargval =
|
||||
if idx >= n_implicit_args
|
||||
then
|
||||
let ({ id = id }, ident) = header_slots.(idx - 2) in
|
||||
Llvm.set_value_name ident llargval;
|
||||
let llarg =
|
||||
let llty = Llvm.type_of llargval in
|
||||
Llvm.build_alloca llty (ident ^ "_ptr") llinitbuilder
|
||||
in
|
||||
ignore (Llvm.build_store llargval llarg llinitbuilder);
|
||||
Hashtbl.add slot_to_llvalue id llarg
|
||||
in
|
||||
Array.iteri build_arg (Llvm.params llfn);
|
||||
|
||||
(* Allocate space for all the blocks' slots.
|
||||
* and zero the exteriors. *)
|
||||
let init_block (block_id:node_id) : unit =
|
||||
let init_slot
|
||||
(key:Ast.slot_key)
|
||||
(slot_id:node_id)
|
||||
(slot:Ast.slot)
|
||||
: unit =
|
||||
let name = Ast.sprintf_slot_key () key in
|
||||
let llty = trans_slot (Some slot_id) slot in
|
||||
let llptr = Llvm.build_alloca llty name llinitbuilder in
|
||||
begin
|
||||
match slot_mem_ctrl slot with
|
||||
MEM_rc_struct
|
||||
| MEM_rc_opaque
|
||||
| MEM_gc ->
|
||||
ignore (Llvm.build_store
|
||||
(Llvm.const_pointer_null llty)
|
||||
llptr llinitbuilder);
|
||||
| _ -> ()
|
||||
end;
|
||||
Hashtbl.add slot_to_llvalue slot_id llptr
|
||||
in
|
||||
iter_block_slots sem_cx block_id init_slot
|
||||
in
|
||||
|
||||
let exit_block
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(block_id:node_id)
|
||||
: Llvm.llbuilder =
|
||||
let r = ref llbuilder in
|
||||
iter_block_slots sem_cx block_id
|
||||
begin
|
||||
fun _ slot_id slot ->
|
||||
if (not (Semant.slot_is_obj_state sem_cx slot_id))
|
||||
then
|
||||
let ptr = Hashtbl.find slot_to_llvalue slot_id in
|
||||
drop_slot r lltask ptr slot None
|
||||
end;
|
||||
!r
|
||||
in
|
||||
|
||||
List.iter init_block (Hashtbl.find sem_cx.Semant.ctxt_frame_blocks fn_id);
|
||||
|
||||
let static_str (s:string) : Llvm.llvalue =
|
||||
Llvm.define_global (anon_llid "str") (Llvm.const_stringz llctx s) llmod
|
||||
in
|
||||
|
||||
|
||||
(* Translates a list of AST statements to a sequence of LLVM instructions.
|
||||
* The supplied "terminate" function appends the appropriate terminator
|
||||
* instruction to the instruction stream. It may or may not be called,
|
||||
* depending on whether the AST contains a terminating instruction
|
||||
* explicitly. *)
|
||||
let rec trans_stmts
|
||||
(block_id:node_id)
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(stmts:Ast.stmt list)
|
||||
(terminate:(Llvm.llbuilder -> node_id -> unit))
|
||||
: unit =
|
||||
let trans_literal
|
||||
(lit:Ast.lit)
|
||||
: Llvm.llvalue =
|
||||
match lit with
|
||||
Ast.LIT_nil -> llnil
|
||||
| Ast.LIT_bool value ->
|
||||
Llvm.const_int (Llvm.i1_type llctx) (if value then 1 else 0)
|
||||
| Ast.LIT_mach (mty, value, _) ->
|
||||
let llty = trans_mach_ty mty in
|
||||
Llvm.const_of_int64 llty value (mach_is_signed mty)
|
||||
| Ast.LIT_int (value, _) ->
|
||||
Llvm.const_of_int64 (Llvm.i32_type llctx) value true
|
||||
| Ast.LIT_uint (value, _) ->
|
||||
Llvm.const_of_int64 (Llvm.i32_type llctx) value false
|
||||
| Ast.LIT_char ch ->
|
||||
Llvm.const_int (Llvm.i32_type llctx) ch
|
||||
in
|
||||
|
||||
(* Translates an lval by reference into the appropriate pointer
|
||||
* value. *)
|
||||
let trans_lval (lval:Ast.lval) : Llvm.llvalue =
|
||||
iflog (fun _ -> log sem_cx "trans_lval: %a" Ast.sprintf_lval lval);
|
||||
match lval with
|
||||
Ast.LVAL_base { id = base_id } ->
|
||||
let id =
|
||||
Hashtbl.find sem_cx.Semant.ctxt_lval_to_referent base_id
|
||||
in
|
||||
let referent = Hashtbl.find sem_cx.Semant.ctxt_all_defns id in
|
||||
begin
|
||||
match referent with
|
||||
Semant.DEFN_slot _ -> Hashtbl.find slot_to_llvalue id
|
||||
| Semant.DEFN_item _ -> Hashtbl.find llitems id
|
||||
| _ -> bogus_ptr (* TODO *)
|
||||
end
|
||||
| Ast.LVAL_ext _ -> bogus_ptr (* TODO *)
|
||||
in
|
||||
|
||||
let trans_atom (atom:Ast.atom) : Llvm.llvalue =
|
||||
iflog (fun _ -> log sem_cx "trans_atom: %a" Ast.sprintf_atom atom);
|
||||
match atom with
|
||||
Ast.ATOM_literal { node = lit } -> trans_literal lit
|
||||
| Ast.ATOM_lval lval ->
|
||||
Llvm.build_load (trans_lval lval) (anon_llid "tmp") llbuilder
|
||||
in
|
||||
|
||||
let trans_binary_expr
|
||||
((op:Ast.binop), (lhs:Ast.atom), (rhs:Ast.atom))
|
||||
: Llvm.llvalue =
|
||||
(* Evaluate the operands in the proper order. *)
|
||||
let (lllhs, llrhs) =
|
||||
match op with
|
||||
Ast.BINOP_or | Ast.BINOP_and | Ast.BINOP_eq | Ast.BINOP_ne
|
||||
| Ast.BINOP_lt | Ast.BINOP_le | Ast.BINOP_ge | Ast.BINOP_gt
|
||||
| Ast.BINOP_lsl | Ast.BINOP_lsr | Ast.BINOP_asr
|
||||
| Ast.BINOP_add | Ast.BINOP_sub | Ast.BINOP_mul
|
||||
| Ast.BINOP_div | Ast.BINOP_mod | Ast.BINOP_xor ->
|
||||
(trans_atom lhs, trans_atom rhs)
|
||||
| Ast.BINOP_send ->
|
||||
let llrhs = trans_atom rhs in
|
||||
let lllhs = trans_atom lhs in
|
||||
(lllhs, llrhs)
|
||||
in
|
||||
let llid = anon_llid "expr" in
|
||||
match op with
|
||||
Ast.BINOP_eq ->
|
||||
(* TODO: equality works on more than just integers *)
|
||||
Llvm.build_icmp Llvm.Icmp.Eq lllhs llrhs llid llbuilder
|
||||
|
||||
(* TODO: signed/unsigned distinction, floating point *)
|
||||
| Ast.BINOP_add -> Llvm.build_add lllhs llrhs llid llbuilder
|
||||
| Ast.BINOP_sub -> Llvm.build_sub lllhs llrhs llid llbuilder
|
||||
| Ast.BINOP_mul -> Llvm.build_mul lllhs llrhs llid llbuilder
|
||||
| Ast.BINOP_div -> Llvm.build_sdiv lllhs llrhs llid llbuilder
|
||||
| Ast.BINOP_mod -> Llvm.build_srem lllhs llrhs llid llbuilder
|
||||
|
||||
| _ -> bogus (* TODO *)
|
||||
in
|
||||
|
||||
let trans_unary_expr _ = bogus in (* TODO *)
|
||||
|
||||
let trans_expr (expr:Ast.expr) : Llvm.llvalue =
|
||||
iflog (fun _ -> log sem_cx "trans_expr: %a" Ast.sprintf_expr expr);
|
||||
match expr with
|
||||
Ast.EXPR_binary binexp -> trans_binary_expr binexp
|
||||
| Ast.EXPR_unary unexp -> trans_unary_expr unexp
|
||||
| Ast.EXPR_atom atom -> trans_atom atom
|
||||
in
|
||||
|
||||
let trans_log_str (atom:Ast.atom) : unit =
|
||||
upcall llbuilder lltask "upcall_log_str" None [| trans_atom atom |]
|
||||
in
|
||||
|
||||
let trans_log_int (atom:Ast.atom) : unit =
|
||||
upcall llbuilder lltask "upcall_log_int" None [| trans_atom atom |]
|
||||
in
|
||||
|
||||
let trans_fail
|
||||
(llbuilder:Llvm.llbuilder)
|
||||
(lltask:Llvm.llvalue)
|
||||
(reason:string)
|
||||
(stmt_id:node_id)
|
||||
: unit =
|
||||
let (file, line, _) =
|
||||
match Session.get_span sem_cx.Semant.ctxt_sess stmt_id with
|
||||
None -> ("<none>", 0, 0)
|
||||
| Some sp -> sp.lo
|
||||
in
|
||||
upcall llbuilder lltask "upcall_fail" None [|
|
||||
static_str reason;
|
||||
static_str file;
|
||||
Llvm.const_int (Llvm.i32_type llctx) line
|
||||
|];
|
||||
ignore (Llvm.build_unreachable llbuilder)
|
||||
in
|
||||
|
||||
(* FIXME: this may be irrelevant; possibly LLVM will wind up
|
||||
* using GOT and such wherever it needs to to achieve PIC
|
||||
* data.
|
||||
*)
|
||||
(*
|
||||
let crate_rel (v:Llvm.llvalue) : Llvm.llvalue =
|
||||
let v_int = Llvm.const_pointercast v word_ty in
|
||||
let c_int = Llvm.const_pointercast crate_ptr word_ty in
|
||||
Llvm.const_sub v_int c_int
|
||||
in
|
||||
*)
|
||||
|
||||
match stmts with
|
||||
[] -> terminate llbuilder block_id
|
||||
| head::tail ->
|
||||
|
||||
iflog (fun _ ->
|
||||
log sem_cx "trans_stmt: %a" Ast.sprintf_stmt head);
|
||||
|
||||
let trans_tail_with_builder llbuilder' : unit =
|
||||
trans_stmts block_id llbuilder' tail terminate
|
||||
in
|
||||
let trans_tail () = trans_tail_with_builder llbuilder in
|
||||
|
||||
match head.node with
|
||||
Ast.STMT_init_tup (dest, atoms) ->
|
||||
let zero = const_i32 0 in
|
||||
let lldest = trans_lval dest in
|
||||
let trans_tup_atom idx (_, _, atom) =
|
||||
let indices = [| zero; const_i32 idx |] in
|
||||
let gep_id = anon_llid "init_tup_gep" in
|
||||
let ptr =
|
||||
Llvm.build_gep lldest indices gep_id llbuilder
|
||||
in
|
||||
ignore (Llvm.build_store (trans_atom atom) ptr llbuilder)
|
||||
in
|
||||
Array.iteri trans_tup_atom atoms;
|
||||
trans_tail ()
|
||||
|
||||
| Ast.STMT_copy (dest, src) ->
|
||||
let llsrc = trans_expr src in
|
||||
let lldest = trans_lval dest in
|
||||
ignore (Llvm.build_store llsrc lldest llbuilder);
|
||||
trans_tail ()
|
||||
|
||||
| Ast.STMT_call (dest, fn, args) ->
|
||||
let llargs = Array.map trans_atom args in
|
||||
let lldest = trans_lval dest in
|
||||
let llfn = trans_lval fn in
|
||||
let llallargs = Array.append [| lldest; lltask |] llargs in
|
||||
let llrv = build_call llfn llallargs "" llbuilder in
|
||||
Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
|
||||
trans_tail ()
|
||||
|
||||
| Ast.STMT_if sif ->
|
||||
let llexpr = trans_expr sif.Ast.if_test in
|
||||
let (llnext, llnextbuilder) = new_block None "next" in
|
||||
let branch_to_next llbuilder' _ =
|
||||
ignore (Llvm.build_br llnext llbuilder')
|
||||
in
|
||||
let llthen = trans_block sif.Ast.if_then branch_to_next in
|
||||
let llelse =
|
||||
match sif.Ast.if_else with
|
||||
None -> llnext
|
||||
| Some if_else -> trans_block if_else branch_to_next
|
||||
in
|
||||
ignore (Llvm.build_cond_br llexpr llthen llelse llbuilder);
|
||||
trans_tail_with_builder llnextbuilder
|
||||
|
||||
| Ast.STMT_ret atom_opt ->
|
||||
begin
|
||||
match atom_opt with
|
||||
None -> ()
|
||||
| Some atom ->
|
||||
ignore (Llvm.build_store (trans_atom atom)
|
||||
lloutptr llbuilder)
|
||||
end;
|
||||
let llbuilder = exit_block llbuilder block_id in
|
||||
ignore (Llvm.build_ret_void llbuilder)
|
||||
|
||||
| Ast.STMT_fail ->
|
||||
trans_fail llbuilder lltask "explicit failure" head.id
|
||||
|
||||
| Ast.STMT_log a ->
|
||||
begin
|
||||
match Semant.atom_type sem_cx a with
|
||||
(* NB: If you extend this, be sure to update the
|
||||
* typechecking code in type.ml as well. *)
|
||||
Ast.TY_str -> trans_log_str a
|
||||
| Ast.TY_int | Ast.TY_uint | Ast.TY_bool | Ast.TY_char
|
||||
| Ast.TY_mach (TY_u8) | Ast.TY_mach (TY_u16)
|
||||
| Ast.TY_mach (TY_u32) | Ast.TY_mach (TY_i8)
|
||||
| Ast.TY_mach (TY_i16) | Ast.TY_mach (TY_i32) ->
|
||||
trans_log_int a
|
||||
| _ -> Semant.bugi sem_cx head.id
|
||||
"unimplemented logging type"
|
||||
end;
|
||||
trans_tail ()
|
||||
|
||||
| Ast.STMT_check_expr expr ->
|
||||
let llexpr = trans_expr expr in
|
||||
let (llfail, llfailbuilder) = new_block None "fail" in
|
||||
let reason = Ast.fmt_to_str Ast.fmt_expr expr in
|
||||
trans_fail llfailbuilder lltask reason head.id;
|
||||
let (llok, llokbuilder) = new_block None "ok" in
|
||||
ignore (Llvm.build_cond_br llexpr llok llfail llbuilder);
|
||||
trans_tail_with_builder llokbuilder
|
||||
|
||||
| Ast.STMT_init_str (dst, str) ->
|
||||
let d = trans_lval dst in
|
||||
let s = static_str str in
|
||||
let len =
|
||||
Llvm.const_int word_ty ((String.length str) + 1)
|
||||
in
|
||||
upcall llbuilder lltask "upcall_new_str"
|
||||
(Some d) [| s; len |];
|
||||
trans_tail ()
|
||||
|
||||
| _ -> trans_stmts block_id llbuilder tail terminate
|
||||
|
||||
(*
|
||||
* Translates an AST block to one or more LLVM basic blocks and returns
|
||||
* the first basic block. The supplied callback is expected to add a
|
||||
* terminator instruction.
|
||||
*)
|
||||
|
||||
and trans_block
|
||||
({ node = (stmts:Ast.stmt array); id = id }:Ast.block)
|
||||
(terminate:Llvm.llbuilder -> node_id -> unit)
|
||||
: Llvm.llbasicblock =
|
||||
let (llblock, llbuilder) = new_block (Some id) "bb" in
|
||||
trans_stmts id llbuilder (Array.to_list stmts) terminate;
|
||||
llblock
|
||||
in
|
||||
|
||||
(* "Falling off the end" of a function needs to turn into an explicit
|
||||
* return instruction. *)
|
||||
let default_terminate llbuilder block_id =
|
||||
let llbuilder = exit_block llbuilder block_id in
|
||||
ignore (Llvm.build_ret_void llbuilder)
|
||||
in
|
||||
|
||||
(* Build up the first body block, and link it to the end of the
|
||||
* initialization block. *)
|
||||
let llbodyblock = (trans_block body default_terminate) in
|
||||
ignore (Llvm.build_br llbodyblock llinitbuilder)
|
||||
in
|
||||
|
||||
let trans_mod_item
|
||||
(_:Ast.ident)
|
||||
{ node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
|
||||
: unit =
|
||||
match item with
|
||||
Ast.MOD_ITEM_fn fn -> trans_fn fn id
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let exit_task_glue =
|
||||
(* The exit-task glue does not get called.
|
||||
*
|
||||
* Rather, control arrives at it by *returning* to the first
|
||||
* instruction of it, when control falls off the end of the task's
|
||||
* root function.
|
||||
*
|
||||
* There is a "fake" frame set up by the runtime, underneath us,
|
||||
* that we find ourselves in. This frame has the shape of a frame
|
||||
* entered with 2 standard arguments (outptr + taskptr), then a
|
||||
* retpc and N callee-saves sitting on the stack; all this is under
|
||||
* ebp. Then there are 2 *outgoing* args at sp[0] and sp[1].
|
||||
*
|
||||
* All these are fake except the taskptr, which is the one bit we
|
||||
* want. So we construct an equally fake cdecl llvm signature here
|
||||
* to crudely *get* the taskptr that's sitting 2 words up from sp,
|
||||
* and pass it to upcall_exit.
|
||||
*
|
||||
* The latter never returns.
|
||||
*)
|
||||
let llty = fn_ty void_ty [| task_ptr_ty |] in
|
||||
let llfn = Llvm.declare_function "rust_exit_task_glue" llty llmod in
|
||||
let lltask = Llvm.param llfn 0 in
|
||||
let llblock = Llvm.append_block llctx "body" llfn in
|
||||
let llbuilder = Llvm.builder_at_end llctx llblock in
|
||||
trans_upcall llbuilder lltask "upcall_exit" None [||];
|
||||
ignore (Llvm.build_ret_void llbuilder);
|
||||
llfn
|
||||
in
|
||||
|
||||
try
|
||||
let crate' = crate.node in
|
||||
let items = snd (crate'.Ast.crate_items) in
|
||||
Hashtbl.iter declare_mod_item items;
|
||||
Hashtbl.iter trans_mod_item items;
|
||||
Llfinal.finalize_module
|
||||
llctx llmod abi asm_glue exit_task_glue crate_ptr;
|
||||
llmod
|
||||
with e -> Llvm.dispose_module llmod; raise e
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
134
src/boot/me/alias.ml
Normal file
134
src/boot/me/alias.ml
Normal file
@ -0,0 +1,134 @@
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "alias"
|
||||
cx.ctxt_sess.Session.sess_log_alias
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let alias_analysis_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let curr_stmt = Stack.create () in
|
||||
|
||||
let alias_slot (slot_id:node_id) : unit =
|
||||
begin
|
||||
log cx "noting slot #%d as aliased" (int_of_node slot_id);
|
||||
Hashtbl.replace cx.ctxt_slot_aliased slot_id ()
|
||||
end
|
||||
in
|
||||
|
||||
let alias lval =
|
||||
match lval with
|
||||
Ast.LVAL_base nb ->
|
||||
let referent = Hashtbl.find cx.ctxt_lval_to_referent nb.id in
|
||||
if (referent_is_slot cx referent)
|
||||
then alias_slot referent
|
||||
| _ -> err None "unhandled form of lval %a in alias analysis"
|
||||
Ast.sprintf_lval lval
|
||||
in
|
||||
|
||||
let alias_atom at =
|
||||
match at with
|
||||
Ast.ATOM_lval lv -> alias lv
|
||||
| _ -> err None "aliasing literal"
|
||||
in
|
||||
|
||||
let alias_call_args dst callee args =
|
||||
alias dst;
|
||||
let callee_ty = lval_ty cx callee in
|
||||
match callee_ty with
|
||||
Ast.TY_fn (tsig,_) ->
|
||||
Array.iteri
|
||||
begin
|
||||
fun i slot ->
|
||||
match slot.Ast.slot_mode with
|
||||
Ast.MODE_alias _ ->
|
||||
alias_atom args.(i)
|
||||
| _ -> ()
|
||||
end
|
||||
tsig.Ast.sig_input_slots
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let visit_stmt_pre s =
|
||||
Stack.push s.id curr_stmt;
|
||||
begin
|
||||
try
|
||||
match s.node with
|
||||
(* FIXME (issue #26): actually all these *existing* cases
|
||||
* can probably go now that we're using Trans.aliasing to
|
||||
* form short-term spill-based aliases. Only aliases that
|
||||
* survive 'into' a sub-block (those formed during iteration)
|
||||
* need to be handled in this module. *)
|
||||
Ast.STMT_call (dst, callee, args)
|
||||
| Ast.STMT_spawn (dst, _, callee, args)
|
||||
-> alias_call_args dst callee args
|
||||
|
||||
| Ast.STMT_send (_, src) -> alias src
|
||||
| Ast.STMT_recv (dst, _) -> alias dst
|
||||
| Ast.STMT_init_port (dst) -> alias dst
|
||||
| Ast.STMT_init_chan (dst, _) -> alias dst
|
||||
| Ast.STMT_init_vec (dst, _, _) -> alias dst
|
||||
| Ast.STMT_init_str (dst, _) -> alias dst
|
||||
| Ast.STMT_for_each sfe ->
|
||||
let (slot, _) = sfe.Ast.for_each_slot in
|
||||
alias_slot slot.id
|
||||
| _ -> () (* FIXME (issue #29): plenty more to handle here. *)
|
||||
with
|
||||
Semant_err (None, msg) ->
|
||||
raise (Semant_err ((Some s.id), msg))
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s
|
||||
in
|
||||
let visit_stmt_post s =
|
||||
inner.Walk.visit_stmt_post s;
|
||||
ignore (Stack.pop curr_stmt);
|
||||
in
|
||||
|
||||
let visit_lval_pre lv =
|
||||
let slot_id = lval_to_referent cx (lval_base_id lv) in
|
||||
if (not (Stack.is_empty curr_stmt)) && (referent_is_slot cx slot_id)
|
||||
then
|
||||
begin
|
||||
let slot_depth = get_slot_depth cx slot_id in
|
||||
let stmt_depth = get_stmt_depth cx (Stack.top curr_stmt) in
|
||||
if slot_depth <> stmt_depth
|
||||
then
|
||||
begin
|
||||
let _ = assert (slot_depth < stmt_depth) in
|
||||
alias_slot slot_id
|
||||
end
|
||||
end
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_stmt_pre = visit_stmt_pre;
|
||||
Walk.visit_stmt_post = visit_stmt_post;
|
||||
Walk.visit_lval_pre = visit_lval_pre
|
||||
}
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let path = Stack.create () in
|
||||
let passes =
|
||||
[|
|
||||
(alias_analysis_visitor cx
|
||||
Walk.empty_visitor);
|
||||
|]
|
||||
in
|
||||
run_passes cx "alias" path passes (log cx "%s") crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
121
src/boot/me/dead.ml
Normal file
121
src/boot/me/dead.ml
Normal file
@ -0,0 +1,121 @@
|
||||
(*
|
||||
* A simple dead-code analysis that rejects code following unconditional
|
||||
* 'ret' or 'be'.
|
||||
*)
|
||||
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "dead"
|
||||
cx.ctxt_sess.Session.sess_log_dead
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let dead_code_visitor
|
||||
((*cx*)_:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
(* FIXME: create separate table for each fn body for less garbage *)
|
||||
let must_exit = Hashtbl.create 100 in
|
||||
|
||||
let all_must_exit ids =
|
||||
arr_for_all (fun _ id -> Hashtbl.mem must_exit id) ids
|
||||
in
|
||||
|
||||
let visit_block_post block =
|
||||
let stmts = block.node in
|
||||
let len = Array.length stmts in
|
||||
if len > 0 then
|
||||
Array.iteri
|
||||
begin
|
||||
fun i s ->
|
||||
if (i < (len - 1)) && (Hashtbl.mem must_exit s.id) then
|
||||
err (Some stmts.(i + 1).id) "dead statement"
|
||||
end
|
||||
stmts;
|
||||
inner.Walk.visit_block_post block
|
||||
in
|
||||
|
||||
let visit_stmt_post s =
|
||||
begin
|
||||
match s.node with
|
||||
| Ast.STMT_block block ->
|
||||
if Hashtbl.mem must_exit block.id then
|
||||
Hashtbl.add must_exit s.id ()
|
||||
|
||||
| Ast.STMT_while { Ast.while_body = body }
|
||||
| Ast.STMT_do_while { Ast.while_body = body }
|
||||
| Ast.STMT_for_each { Ast.for_each_body = body }
|
||||
| Ast.STMT_for { Ast.for_body = body } ->
|
||||
if (Hashtbl.mem must_exit body.id) then
|
||||
Hashtbl.add must_exit s.id ()
|
||||
|
||||
| Ast.STMT_if { Ast.if_then = b1; Ast.if_else = Some b2 } ->
|
||||
if (Hashtbl.mem must_exit b1.id) && (Hashtbl.mem must_exit b2.id)
|
||||
then Hashtbl.add must_exit s.id ()
|
||||
|
||||
| Ast.STMT_if _ -> ()
|
||||
|
||||
| Ast.STMT_ret _
|
||||
| Ast.STMT_be _ ->
|
||||
Hashtbl.add must_exit s.id ()
|
||||
|
||||
| Ast.STMT_alt_tag { Ast.alt_tag_arms = arms } ->
|
||||
let arm_ids =
|
||||
Array.map (fun { node = (_, block) } -> block.id) arms
|
||||
in
|
||||
if all_must_exit arm_ids
|
||||
then Hashtbl.add must_exit s.id ()
|
||||
|
||||
| Ast.STMT_alt_type { Ast.alt_type_arms = arms;
|
||||
Ast.alt_type_else = alt_type_else } ->
|
||||
let arm_ids = Array.map (fun (_, _, block) -> block.id) arms in
|
||||
let else_ids =
|
||||
begin
|
||||
match alt_type_else with
|
||||
Some stmt -> [| stmt.id |]
|
||||
| None -> [| |]
|
||||
end
|
||||
in
|
||||
if all_must_exit (Array.append arm_ids else_ids) then
|
||||
Hashtbl.add must_exit s.id ()
|
||||
|
||||
(* FIXME: figure this one out *)
|
||||
| Ast.STMT_alt_port _ -> ()
|
||||
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_post s
|
||||
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_block_post = visit_block_post;
|
||||
Walk.visit_stmt_post = visit_stmt_post }
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let path = Stack.create () in
|
||||
let passes =
|
||||
[|
|
||||
(dead_code_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|]
|
||||
in
|
||||
|
||||
run_passes cx "dead" path passes (log cx "%s") crate;
|
||||
()
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
3019
src/boot/me/dwarf.ml
Normal file
3019
src/boot/me/dwarf.ml
Normal file
File diff suppressed because it is too large
Load Diff
313
src/boot/me/effect.ml
Normal file
313
src/boot/me/effect.ml
Normal file
@ -0,0 +1,313 @@
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "effect"
|
||||
cx.ctxt_sess.Session.sess_log_effect
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog cx thunk =
|
||||
if cx.ctxt_sess.Session.sess_log_effect
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
let mutability_checking_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(*
|
||||
* This visitor enforces the following rules:
|
||||
*
|
||||
* - A channel type carrying a mutable type is illegal.
|
||||
*
|
||||
* - Writing to an immutable slot is illegal.
|
||||
*
|
||||
* - Forming a mutable alias to an immutable slot is illegal.
|
||||
*
|
||||
*)
|
||||
let visit_ty_pre t =
|
||||
match t with
|
||||
Ast.TY_chan t' when type_has_state t' ->
|
||||
err None "channel of mutable type: %a " Ast.sprintf_ty t'
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let check_write id dst =
|
||||
let dst_slot = lval_slot cx dst in
|
||||
if (dst_slot.Ast.slot_mutable or
|
||||
(Hashtbl.mem cx.ctxt_copy_stmt_is_init id))
|
||||
then ()
|
||||
else err (Some id) "writing to non-mutable slot"
|
||||
in
|
||||
(* FIXME: enforce the no-write-alias-to-immutable-slot rule. *)
|
||||
let visit_stmt_pre s =
|
||||
begin
|
||||
match s.node with
|
||||
Ast.STMT_copy (dst, _) -> check_write s.id dst
|
||||
| Ast.STMT_copy_binop (dst, _, _) -> check_write s.id dst
|
||||
| Ast.STMT_call (dst, _, _) -> check_write s.id dst
|
||||
| Ast.STMT_recv (dst, _) -> check_write s.id dst
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_ty_pre = visit_ty_pre;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre }
|
||||
;;
|
||||
|
||||
let function_effect_propagation_visitor
|
||||
(item_effect:(node_id, Ast.effect) Hashtbl.t)
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(*
|
||||
* This visitor calculates the effect of each function according to
|
||||
* its statements:
|
||||
*
|
||||
* - Communication lowers to 'io'
|
||||
* - Native calls lower to 'unsafe'
|
||||
* - Calling a function with effect e lowers to e.
|
||||
*)
|
||||
let curr_fn = Stack.create () in
|
||||
let visit_mod_item_pre n p i =
|
||||
begin
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn _ -> Stack.push i.id curr_fn
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre n p i
|
||||
in
|
||||
let visit_mod_item_post n p i =
|
||||
inner.Walk.visit_mod_item_post n p i;
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn _ -> ignore (Stack.pop curr_fn)
|
||||
| _ -> ()
|
||||
in
|
||||
let visit_obj_drop_pre o b =
|
||||
Stack.push b.id curr_fn;
|
||||
inner.Walk.visit_obj_drop_pre o b
|
||||
in
|
||||
let visit_obj_drop_post o b =
|
||||
inner.Walk.visit_obj_drop_post o b;
|
||||
ignore (Stack.pop curr_fn);
|
||||
in
|
||||
|
||||
let lower_to s ne =
|
||||
let fn_id = Stack.top curr_fn in
|
||||
let e =
|
||||
match htab_search item_effect fn_id with
|
||||
None -> Ast.PURE
|
||||
| Some e -> e
|
||||
in
|
||||
let ne = lower_effect_of ne e in
|
||||
if ne <> e
|
||||
then
|
||||
begin
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
let name = Hashtbl.find cx.ctxt_all_item_names fn_id in
|
||||
log cx "lowering calculated effect on '%a': '%a' -> '%a'"
|
||||
Ast.sprintf_name name
|
||||
Ast.sprintf_effect e
|
||||
Ast.sprintf_effect ne;
|
||||
log cx "at stmt %a" Ast.sprintf_stmt s
|
||||
end;
|
||||
Hashtbl.replace item_effect fn_id ne
|
||||
end;
|
||||
in
|
||||
|
||||
let visit_stmt_pre s =
|
||||
begin
|
||||
match s.node with
|
||||
Ast.STMT_send _
|
||||
| Ast.STMT_recv _ -> lower_to s Ast.IO
|
||||
|
||||
| Ast.STMT_call (_, fn, _) ->
|
||||
let lower_to_callee_ty t =
|
||||
match t with
|
||||
Ast.TY_fn (_, taux) ->
|
||||
lower_to s taux.Ast.fn_effect;
|
||||
| _ -> bug () "non-fn callee"
|
||||
in
|
||||
if lval_is_slot cx fn
|
||||
then
|
||||
let t = lval_slot cx fn in
|
||||
lower_to_callee_ty (slot_ty t)
|
||||
else
|
||||
begin
|
||||
let item = lval_item cx fn in
|
||||
let t = Hashtbl.find cx.ctxt_all_item_types item.id in
|
||||
lower_to_callee_ty t;
|
||||
match htab_search cx.ctxt_required_items item.id with
|
||||
None -> ()
|
||||
| Some (REQUIRED_LIB_rust _, _) -> ()
|
||||
| Some _ -> lower_to s Ast.UNSAFE
|
||||
end
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_mod_item_post = visit_mod_item_post;
|
||||
Walk.visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
Walk.visit_obj_drop_post = visit_obj_drop_post;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre }
|
||||
;;
|
||||
|
||||
let binding_effect_propagation_visitor
|
||||
((*cx*)_:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(* This visitor lowers the effect of an object or binding according
|
||||
* to its slots: holding a 'state' slot lowers any obj item, or
|
||||
* bind-stmt LHS, to 'state'.
|
||||
*
|
||||
* Binding (or implicitly just making a native 1st-class) makes the LHS
|
||||
* unsafe.
|
||||
*)
|
||||
inner
|
||||
;;
|
||||
|
||||
let effect_checking_visitor
|
||||
(item_auth:(node_id, Ast.effect) Hashtbl.t)
|
||||
(item_effect:(node_id, Ast.effect) Hashtbl.t)
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(*
|
||||
* This visitor checks that each type, item and obj declares
|
||||
* effects consistent with what we calculated.
|
||||
*)
|
||||
let auth_stack = Stack.create () in
|
||||
let visit_mod_item_pre n p i =
|
||||
begin
|
||||
match htab_search item_auth i.id with
|
||||
None -> ()
|
||||
| Some e ->
|
||||
let curr =
|
||||
if Stack.is_empty auth_stack
|
||||
then Ast.PURE
|
||||
else Stack.top auth_stack
|
||||
in
|
||||
let next = lower_effect_of e curr in
|
||||
Stack.push next auth_stack;
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
let name = Hashtbl.find cx.ctxt_all_item_names i.id in
|
||||
log cx
|
||||
"entering '%a', adjusting auth effect: '%a' -> '%a'"
|
||||
Ast.sprintf_name name
|
||||
Ast.sprintf_effect curr
|
||||
Ast.sprintf_effect next
|
||||
end
|
||||
end;
|
||||
begin
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn f ->
|
||||
let e =
|
||||
match htab_search item_effect i.id with
|
||||
None -> Ast.PURE
|
||||
| Some e -> e
|
||||
in
|
||||
let fe = f.Ast.fn_aux.Ast.fn_effect in
|
||||
let ae =
|
||||
if Stack.is_empty auth_stack
|
||||
then None
|
||||
else Some (Stack.top auth_stack)
|
||||
in
|
||||
if e <> fe && (ae <> (Some e))
|
||||
then
|
||||
begin
|
||||
let name = Hashtbl.find cx.ctxt_all_item_names i.id in
|
||||
err (Some i.id)
|
||||
"%a claims effect '%a' but calculated effect is '%a'%s"
|
||||
Ast.sprintf_name name
|
||||
Ast.sprintf_effect fe
|
||||
Ast.sprintf_effect e
|
||||
begin
|
||||
match ae with
|
||||
Some ae when ae <> fe ->
|
||||
Printf.sprintf " (auth effect is '%a')"
|
||||
Ast.sprintf_effect ae
|
||||
| _ -> ""
|
||||
end
|
||||
end
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre n p i
|
||||
in
|
||||
let visit_mod_item_post n p i =
|
||||
inner.Walk.visit_mod_item_post n p i;
|
||||
match htab_search item_auth i.id with
|
||||
None -> ()
|
||||
| Some _ ->
|
||||
let curr = Stack.pop auth_stack in
|
||||
let next =
|
||||
if Stack.is_empty auth_stack
|
||||
then Ast.PURE
|
||||
else Stack.top auth_stack
|
||||
in
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
let name = Hashtbl.find cx.ctxt_all_item_names i.id in
|
||||
log cx
|
||||
"leaving '%a', restoring auth effect: '%a' -> '%a'"
|
||||
Ast.sprintf_name name
|
||||
Ast.sprintf_effect curr
|
||||
Ast.sprintf_effect next
|
||||
end
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_mod_item_post = visit_mod_item_post; }
|
||||
;;
|
||||
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let path = Stack.create () in
|
||||
let item_auth = Hashtbl.create 0 in
|
||||
let item_effect = Hashtbl.create 0 in
|
||||
let passes =
|
||||
[|
|
||||
(mutability_checking_visitor cx
|
||||
Walk.empty_visitor);
|
||||
(function_effect_propagation_visitor item_effect cx
|
||||
Walk.empty_visitor);
|
||||
(binding_effect_propagation_visitor cx
|
||||
Walk.empty_visitor);
|
||||
(effect_checking_visitor item_auth item_effect cx
|
||||
Walk.empty_visitor);
|
||||
|]
|
||||
in
|
||||
let root_scope = [ SCOPE_crate crate ] in
|
||||
let auth_effect name eff =
|
||||
match lookup_by_name cx root_scope name with
|
||||
None -> ()
|
||||
| Some (_, id) ->
|
||||
if referent_is_item cx id
|
||||
then htab_put item_auth id eff
|
||||
else err (Some id) "auth clause in crate refers to non-item"
|
||||
in
|
||||
Hashtbl.iter auth_effect crate.node.Ast.crate_auth;
|
||||
run_passes cx "effect" path passes (log cx "%s") crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
470
src/boot/me/layout.ml
Normal file
470
src/boot/me/layout.ml
Normal file
@ -0,0 +1,470 @@
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "layout"
|
||||
cx.ctxt_sess.Session.sess_log_layout
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
type slot_stack = Il.referent_ty Stack.t;;
|
||||
type frame_blocks = slot_stack Stack.t;;
|
||||
|
||||
let layout_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(*
|
||||
* - Frames look, broadly, like this (growing downward):
|
||||
*
|
||||
* +----------------------------+ <-- Rewind tail calls to here.
|
||||
* |caller args |
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp + abi_frame_base_sz
|
||||
* |task ptr (implicit arg) | + abi_implicit_args_sz
|
||||
* |output ptr (implicit arg) |
|
||||
* +----------------------------+ <-- fp + abi_frame_base_sz
|
||||
* |return pc |
|
||||
* |callee-save registers |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp
|
||||
* |crate ptr |
|
||||
* |crate-rel frame info disp |
|
||||
* +----------------------------+ <-- fp - abi_frame_info_sz
|
||||
* |spills determined in ra |
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - (abi_frame_info_sz
|
||||
* |... | + spillsz)
|
||||
* |frame-allocated stuff |
|
||||
* |determined in resolve |
|
||||
* |laid out in layout |
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - framesz
|
||||
* |call space | == sp + callsz
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - (framesz + callsz) == sp
|
||||
*
|
||||
* - Slot offsets fall into three classes:
|
||||
*
|
||||
* #1 frame-locals are negative offsets from fp
|
||||
* (beneath the frame-info and spills)
|
||||
*
|
||||
* #2 incoming arg slots are positive offsets from fp
|
||||
* (above the frame-base)
|
||||
*
|
||||
* #3 outgoing arg slots are positive offsets from sp
|
||||
*
|
||||
* - Slots are split into two classes:
|
||||
*
|
||||
* #1 those that are never aliased and fit in a word, so are
|
||||
* vreg-allocated
|
||||
*
|
||||
* #2 all others
|
||||
*
|
||||
* - Non-aliased, word-fitting slots consume no frame space
|
||||
* *yet*; they are given a generic value that indicates "try a
|
||||
* vreg". The register allocator may spill them later, if it
|
||||
* needs to, but that's not our concern.
|
||||
*
|
||||
* - Aliased / too-big slots are frame-allocated, need to be
|
||||
* laid out in the frame at fixed offsets.
|
||||
*
|
||||
* - The frame size is the maximum of all the block sizes contained
|
||||
* within it. Though at the moment it's the sum of them, due to
|
||||
* the blood-curdling hack we use to ensure proper unwind/drop
|
||||
* behavior in absence of CFI or similar precise frame-evolution
|
||||
* tracking. See visit_block_post below (issue #27).
|
||||
*
|
||||
* - Each call is examined and the size of the call tuple required
|
||||
* for that call is calculated. The call size is the maximum of all
|
||||
* such call tuples.
|
||||
*
|
||||
* - In frames that have a tail call (in fact, currently, all frames
|
||||
* because we're lazy) we double the call size in order to handle
|
||||
* the possible need to *execute* a call (to drop glue) while
|
||||
* destroying the frame, after we've built the outgoing args. This is
|
||||
* done in the backend though; the logic in this file is ignorant of the
|
||||
* doubling (some platforms may not require it? Hard to guess)
|
||||
*
|
||||
*)
|
||||
|
||||
let force_slot_to_mem (slot:Ast.slot) : bool =
|
||||
(* FIXME (issue #26): For the time being we force any slot that
|
||||
* points into memory or is of opaque/code type to be stored in the
|
||||
* frame rather than in a vreg. This can probably be relaxed in the
|
||||
* future.
|
||||
*)
|
||||
let rec st_in_mem st =
|
||||
match st with
|
||||
Il.ValTy _ -> false
|
||||
| Il.AddrTy _ -> true
|
||||
|
||||
and rt_in_mem rt =
|
||||
match rt with
|
||||
Il.ScalarTy st -> st_in_mem st
|
||||
| Il.StructTy rts
|
||||
| Il.UnionTy rts -> List.exists rt_in_mem (Array.to_list rts)
|
||||
| Il.OpaqueTy
|
||||
| Il.ParamTy _
|
||||
| Il.CodeTy -> true
|
||||
| Il.NilTy -> false
|
||||
in
|
||||
rt_in_mem (slot_referent_type cx.ctxt_abi slot)
|
||||
in
|
||||
|
||||
let rty_sz rty = Il.referent_ty_size cx.ctxt_abi.Abi.abi_word_bits rty in
|
||||
let rty_layout rty =
|
||||
Il.referent_ty_layout cx.ctxt_abi.Abi.abi_word_bits rty
|
||||
in
|
||||
|
||||
let is_subword_size sz =
|
||||
match sz with
|
||||
SIZE_fixed i -> i64_le i cx.ctxt_abi.Abi.abi_word_sz
|
||||
| _ -> false
|
||||
in
|
||||
|
||||
let iflog thunk =
|
||||
if cx.ctxt_sess.Session.sess_log_layout
|
||||
then thunk ()
|
||||
else ()
|
||||
in
|
||||
|
||||
let layout_slot_ids
|
||||
(slot_accum:slot_stack)
|
||||
(upwards:bool)
|
||||
(vregs_ok:bool)
|
||||
(offset:size)
|
||||
(slots:node_id array)
|
||||
: unit =
|
||||
let accum (off,align) id : (size * size) =
|
||||
let slot = referent_to_slot cx id in
|
||||
let rt = slot_referent_type cx.ctxt_abi slot in
|
||||
let (elt_size, elt_align) = rty_layout rt in
|
||||
if vregs_ok
|
||||
&& (is_subword_size elt_size)
|
||||
&& (not (type_is_structured (slot_ty slot)))
|
||||
&& (not (force_slot_to_mem slot))
|
||||
&& (not (Hashtbl.mem cx.ctxt_slot_aliased id))
|
||||
then
|
||||
begin
|
||||
iflog
|
||||
begin
|
||||
fun _ ->
|
||||
let k = Hashtbl.find cx.ctxt_slot_keys id in
|
||||
log cx "assigning slot #%d = %a to vreg"
|
||||
(int_of_node id)
|
||||
Ast.sprintf_slot_key k;
|
||||
end;
|
||||
htab_put cx.ctxt_slot_vregs id (ref None);
|
||||
(off,align)
|
||||
end
|
||||
else
|
||||
begin
|
||||
let elt_off = align_sz elt_align off in
|
||||
let frame_off =
|
||||
if upwards
|
||||
then elt_off
|
||||
else neg_sz (add_sz elt_off elt_size)
|
||||
in
|
||||
Stack.push (slot_referent_type cx.ctxt_abi slot) slot_accum;
|
||||
iflog
|
||||
begin
|
||||
fun _ ->
|
||||
let k = Hashtbl.find cx.ctxt_slot_keys id in
|
||||
log cx "assigning slot #%d = %a frame-offset %s"
|
||||
(int_of_node id)
|
||||
Ast.sprintf_slot_key k
|
||||
(string_of_size frame_off);
|
||||
end;
|
||||
if (not (Hashtbl.mem cx.ctxt_slot_offsets id))
|
||||
then htab_put cx.ctxt_slot_offsets id frame_off;
|
||||
(add_sz elt_off elt_size, max_sz elt_align align)
|
||||
end
|
||||
in
|
||||
ignore (Array.fold_left accum (offset, SIZE_fixed 0L) slots)
|
||||
in
|
||||
|
||||
let layout_block
|
||||
(slot_accum:slot_stack)
|
||||
(offset:size)
|
||||
(block:Ast.block)
|
||||
: unit =
|
||||
log cx "laying out block #%d at fp offset %s"
|
||||
(int_of_node block.id) (string_of_size offset);
|
||||
let block_slot_ids =
|
||||
Array.of_list (htab_vals (Hashtbl.find cx.ctxt_block_slots block.id))
|
||||
in
|
||||
layout_slot_ids slot_accum false true offset block_slot_ids
|
||||
in
|
||||
|
||||
let layout_header (id:node_id) (input_slot_ids:node_id array) : unit =
|
||||
let rty = direct_call_args_referent_type cx id in
|
||||
let offset =
|
||||
match rty with
|
||||
Il.StructTy elts ->
|
||||
(add_sz
|
||||
(SIZE_fixed cx.ctxt_abi.Abi.abi_frame_base_sz)
|
||||
(Il.get_element_offset
|
||||
cx.ctxt_abi.Abi.abi_word_bits
|
||||
elts Abi.calltup_elt_args))
|
||||
| _ -> bug () "call tuple has non-StructTy"
|
||||
in
|
||||
log cx "laying out header for node #%d at fp offset %s"
|
||||
(int_of_node id) (string_of_size offset);
|
||||
layout_slot_ids (Stack.create()) true false offset input_slot_ids
|
||||
in
|
||||
|
||||
let layout_obj_state (id:node_id) (state_slot_ids:node_id array) : unit =
|
||||
let offset =
|
||||
let word_sz = cx.ctxt_abi.Abi.abi_word_sz in
|
||||
let word_n (n:int) = Int64.mul word_sz (Int64.of_int n) in
|
||||
SIZE_fixed (word_n (Abi.exterior_rc_slot_field_body
|
||||
+ 1 (* the state tydesc. *)))
|
||||
in
|
||||
log cx "laying out object-state for node #%d at offset %s"
|
||||
(int_of_node id) (string_of_size offset);
|
||||
layout_slot_ids (Stack.create()) true false offset state_slot_ids
|
||||
in
|
||||
|
||||
let (frame_stack:(node_id * frame_blocks) Stack.t) = Stack.create() in
|
||||
|
||||
let block_rty (block:slot_stack) : Il.referent_ty =
|
||||
Il.StructTy (Array.of_list (stk_elts_from_bot block))
|
||||
in
|
||||
|
||||
let frame_rty (frame:frame_blocks) : Il.referent_ty =
|
||||
Il.StructTy (Array.of_list (List.map block_rty (stk_elts_from_bot frame)))
|
||||
in
|
||||
|
||||
let update_frame_size _ =
|
||||
let (frame_id, frame_blocks) = Stack.top frame_stack in
|
||||
let frame_spill = Hashtbl.find cx.ctxt_spill_fixups frame_id in
|
||||
let sz =
|
||||
add_sz
|
||||
(add_sz
|
||||
(rty_sz (frame_rty frame_blocks))
|
||||
(SIZE_fixup_mem_sz frame_spill))
|
||||
(SIZE_fixed cx.ctxt_abi.Abi.abi_frame_info_sz)
|
||||
in
|
||||
let curr = Hashtbl.find cx.ctxt_frame_sizes frame_id in
|
||||
let sz = max_sz curr sz in
|
||||
log cx "extending frame #%d frame to size %s"
|
||||
(int_of_node frame_id) (string_of_size sz);
|
||||
Hashtbl.replace cx.ctxt_frame_sizes frame_id sz
|
||||
in
|
||||
|
||||
(*
|
||||
* FIXME: this is a little aggressive for default callsz; it can be
|
||||
* narrowed in frames with no drop glue and/or no indirect drop glue.
|
||||
*)
|
||||
|
||||
let glue_callsz =
|
||||
let word = interior_slot Ast.TY_int in
|
||||
let glue_fn =
|
||||
mk_simple_ty_fn
|
||||
(Array.init Abi.worst_case_glue_call_args (fun _ -> word))
|
||||
in
|
||||
rty_sz (indirect_call_args_referent_type cx 0 glue_fn Il.OpaqueTy)
|
||||
in
|
||||
|
||||
let enter_frame id =
|
||||
Stack.push (id, (Stack.create())) frame_stack;
|
||||
htab_put cx.ctxt_frame_sizes id (SIZE_fixed 0L);
|
||||
htab_put cx.ctxt_call_sizes id glue_callsz;
|
||||
htab_put cx.ctxt_spill_fixups id (new_fixup "frame spill fixup");
|
||||
htab_put cx.ctxt_frame_blocks id [];
|
||||
update_frame_size ();
|
||||
in
|
||||
|
||||
let leave_frame _ =
|
||||
ignore (Stack.pop frame_stack);
|
||||
in
|
||||
|
||||
let header_slot_ids hdr = Array.map (fun (sid,_) -> sid.id) hdr in
|
||||
|
||||
let visit_mod_item_pre n p i =
|
||||
begin
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn f ->
|
||||
enter_frame i.id;
|
||||
layout_header i.id
|
||||
(header_slot_ids f.Ast.fn_input_slots)
|
||||
|
||||
| Ast.MOD_ITEM_tag (header_slots, _, _) ->
|
||||
enter_frame i.id;
|
||||
layout_header i.id
|
||||
(Array.map (fun sid -> sid.id) header_slots)
|
||||
|
||||
| Ast.MOD_ITEM_obj obj ->
|
||||
enter_frame i.id;
|
||||
let ids = header_slot_ids obj.Ast.obj_state in
|
||||
layout_obj_state i.id ids;
|
||||
Array.iter
|
||||
(fun id -> htab_put cx.ctxt_slot_is_obj_state id ())
|
||||
ids
|
||||
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre n p i
|
||||
in
|
||||
|
||||
let visit_mod_item_post n p i =
|
||||
inner.Walk.visit_mod_item_post n p i;
|
||||
begin
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn _
|
||||
| Ast.MOD_ITEM_tag _
|
||||
| Ast.MOD_ITEM_obj _ -> leave_frame ()
|
||||
| _ -> ()
|
||||
end
|
||||
in
|
||||
|
||||
let visit_obj_fn_pre obj ident fn =
|
||||
enter_frame fn.id;
|
||||
layout_header fn.id
|
||||
(header_slot_ids fn.node.Ast.fn_input_slots);
|
||||
inner.Walk.visit_obj_fn_pre obj ident fn
|
||||
in
|
||||
|
||||
let visit_obj_fn_post obj ident fn =
|
||||
inner.Walk.visit_obj_fn_post obj ident fn;
|
||||
leave_frame ()
|
||||
in
|
||||
|
||||
let visit_obj_drop_pre obj b =
|
||||
enter_frame b.id;
|
||||
inner.Walk.visit_obj_drop_pre obj b
|
||||
in
|
||||
|
||||
let visit_obj_drop_post obj b =
|
||||
inner.Walk.visit_obj_drop_post obj b;
|
||||
leave_frame ()
|
||||
in
|
||||
|
||||
let visit_block_pre b =
|
||||
if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
|
||||
then enter_frame b.id;
|
||||
let (frame_id, frame_blocks) = Stack.top frame_stack in
|
||||
let frame_spill = Hashtbl.find cx.ctxt_spill_fixups frame_id in
|
||||
let spill_sz = SIZE_fixup_mem_sz frame_spill in
|
||||
let info_sz = SIZE_fixed cx.ctxt_abi.Abi.abi_frame_info_sz in
|
||||
let locals_off = add_sz spill_sz info_sz in
|
||||
let off =
|
||||
if Stack.is_empty frame_blocks
|
||||
then locals_off
|
||||
else
|
||||
add_sz locals_off (rty_sz (frame_rty frame_blocks))
|
||||
in
|
||||
let block_slots = Stack.create() in
|
||||
let frame_block_ids = Hashtbl.find cx.ctxt_frame_blocks frame_id in
|
||||
Hashtbl.replace cx.ctxt_frame_blocks frame_id (b.id :: frame_block_ids);
|
||||
layout_block block_slots off b;
|
||||
Stack.push block_slots frame_blocks;
|
||||
update_frame_size ();
|
||||
inner.Walk.visit_block_pre b
|
||||
in
|
||||
|
||||
let visit_block_post b =
|
||||
inner.Walk.visit_block_post b;
|
||||
if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
|
||||
then leave_frame();
|
||||
(* FIXME (issue #27): In earlier versions of this file, multiple
|
||||
* lexical blocks in the same frame would reuse space from one to
|
||||
* the next so long as they were not nested; The (commented-out)
|
||||
* code here supports that logic. Unfortunately since our marking
|
||||
* and unwinding strategy is very simplistic for now (analogous to
|
||||
* shadow stacks) we're going to give each lexical block in a frame
|
||||
* its own space in the frame, even if they seem like they *should*
|
||||
* be able to reuse space. This makes it possible to arrive at the
|
||||
* frame and work out which variables are live (and which frame
|
||||
* memory corresponds to them) w/o paying attention to the current
|
||||
* pc in the function; a greatly-simplifying assumption.
|
||||
*
|
||||
* This is of course not optimal for the long term, but in the
|
||||
* longer term we'll have time to form proper DWARF CFI
|
||||
* records. We're in a hurry at the moment. *)
|
||||
(*
|
||||
let stk = Stack.top block_stacks in
|
||||
ignore (Stack.pop stk)
|
||||
*)
|
||||
in
|
||||
|
||||
let visit_stmt_pre (s:Ast.stmt) : unit =
|
||||
|
||||
(* Call-size calculation. *)
|
||||
begin
|
||||
let callees =
|
||||
match s.node with
|
||||
Ast.STMT_call (_, lv, _)
|
||||
| Ast.STMT_spawn (_, _, lv, _) -> [| lv |]
|
||||
| Ast.STMT_check (_, calls) -> Array.map (fun (lv, _) -> lv) calls
|
||||
| _ -> [| |]
|
||||
in
|
||||
Array.iter
|
||||
begin
|
||||
fun (callee:Ast.lval) ->
|
||||
let lv_ty = lval_ty cx callee in
|
||||
let abi = cx.ctxt_abi in
|
||||
let static = lval_is_static cx callee in
|
||||
let closure = if static then None else Some Il.OpaqueTy in
|
||||
let n_ty_params =
|
||||
match resolve_lval cx callee with
|
||||
DEFN_item i -> Array.length i.Ast.decl_params
|
||||
| _ -> 0
|
||||
in
|
||||
let rty =
|
||||
call_args_referent_type cx n_ty_params lv_ty closure
|
||||
in
|
||||
let sz = Il.referent_ty_size abi.Abi.abi_word_bits rty in
|
||||
let frame_id = fst (Stack.top frame_stack) in
|
||||
let curr = Hashtbl.find cx.ctxt_call_sizes frame_id in
|
||||
log cx "extending frame #%d call size to %s"
|
||||
(int_of_node frame_id) (string_of_size (max_sz curr sz));
|
||||
Hashtbl.replace cx.ctxt_call_sizes frame_id (max_sz curr sz)
|
||||
end
|
||||
callees
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s
|
||||
in
|
||||
|
||||
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_mod_item_post = visit_mod_item_post;
|
||||
|
||||
Walk.visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
Walk.visit_obj_fn_post = visit_obj_fn_post;
|
||||
Walk.visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
Walk.visit_obj_drop_post = visit_obj_drop_post;
|
||||
|
||||
Walk.visit_stmt_pre = visit_stmt_pre;
|
||||
Walk.visit_block_pre = visit_block_pre;
|
||||
Walk.visit_block_post = visit_block_post }
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let path = Stack.create () in
|
||||
let passes =
|
||||
[|
|
||||
(layout_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|];
|
||||
in
|
||||
run_passes cx "layout" path passes (log cx "%s") crate
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
163
src/boot/me/loop.ml
Normal file
163
src/boot/me/loop.ml
Normal file
@ -0,0 +1,163 @@
|
||||
(*
|
||||
* Computes iterator-loop nesting depths and max depth of each function.
|
||||
*)
|
||||
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "loop"
|
||||
cx.ctxt_sess.Session.sess_log_loop
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
type fn_ctxt = { current_depth: int; }
|
||||
;;
|
||||
|
||||
let incr_depth (fcx:fn_ctxt) =
|
||||
{ current_depth = fcx.current_depth + 1; }
|
||||
;;
|
||||
|
||||
let decr_depth (fcx:fn_ctxt) =
|
||||
{ current_depth = fcx.current_depth - 1; }
|
||||
;;
|
||||
|
||||
let top_fcx = { current_depth = 0; }
|
||||
;;
|
||||
|
||||
let loop_depth_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let (fcxs : fn_ctxt Stack.t) = Stack.create () in
|
||||
|
||||
let push_loop () =
|
||||
let fcx = Stack.pop fcxs in
|
||||
Stack.push (incr_depth fcx) fcxs
|
||||
in
|
||||
|
||||
let pop_loop () =
|
||||
let fcx = Stack.pop fcxs in
|
||||
Stack.push (decr_depth fcx) fcxs
|
||||
in
|
||||
|
||||
let visit_mod_item_pre
|
||||
(ident:Ast.ident)
|
||||
(ty_params:(Ast.ty_param identified) array)
|
||||
(item:Ast.mod_item)
|
||||
: unit =
|
||||
Stack.push top_fcx fcxs;
|
||||
inner.Walk.visit_mod_item_pre ident ty_params item
|
||||
in
|
||||
|
||||
let visit_mod_item_post
|
||||
(ident:Ast.ident)
|
||||
(ty_params:(Ast.ty_param identified) array)
|
||||
(item:Ast.mod_item)
|
||||
: unit =
|
||||
inner.Walk.visit_mod_item_post ident ty_params item;
|
||||
ignore (Stack.pop fcxs);
|
||||
in
|
||||
|
||||
let visit_obj_fn_pre
|
||||
(obj:Ast.obj identified)
|
||||
(ident:Ast.ident)
|
||||
(fn:Ast.fn identified)
|
||||
: unit =
|
||||
Stack.push top_fcx fcxs;
|
||||
inner.Walk.visit_obj_fn_pre obj ident fn
|
||||
in
|
||||
|
||||
let visit_obj_fn_post
|
||||
(obj:Ast.obj identified)
|
||||
(ident:Ast.ident)
|
||||
(fn:Ast.fn identified)
|
||||
: unit =
|
||||
inner.Walk.visit_obj_fn_pre obj ident fn;
|
||||
ignore (Stack.pop fcxs)
|
||||
in
|
||||
|
||||
let visit_obj_drop_pre
|
||||
(obj:Ast.obj identified)
|
||||
(b:Ast.block)
|
||||
: unit =
|
||||
Stack.push top_fcx fcxs;
|
||||
inner.Walk.visit_obj_drop_pre obj b
|
||||
in
|
||||
|
||||
let visit_obj_drop_post
|
||||
(obj:Ast.obj identified)
|
||||
(b:Ast.block)
|
||||
: unit =
|
||||
inner.Walk.visit_obj_drop_post obj b;
|
||||
ignore (Stack.pop fcxs)
|
||||
in
|
||||
|
||||
let visit_slot_identified_pre sloti =
|
||||
let fcx = Stack.top fcxs in
|
||||
htab_put cx.ctxt_slot_loop_depths sloti.id fcx.current_depth;
|
||||
inner.Walk.visit_slot_identified_pre sloti
|
||||
in
|
||||
|
||||
let visit_stmt_pre s =
|
||||
let fcx = Stack.top fcxs in
|
||||
htab_put cx.ctxt_stmt_loop_depths s.id fcx.current_depth;
|
||||
begin
|
||||
match s.node with
|
||||
| Ast.STMT_for_each fe ->
|
||||
htab_put cx.ctxt_block_is_loop_body fe.Ast.for_each_body.id ();
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s
|
||||
in
|
||||
|
||||
let visit_block_pre b =
|
||||
if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
|
||||
then push_loop ();
|
||||
inner.Walk.visit_block_pre b
|
||||
in
|
||||
|
||||
let visit_block_post b =
|
||||
inner.Walk.visit_block_post b;
|
||||
if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
|
||||
then pop_loop ()
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_mod_item_post = visit_mod_item_post;
|
||||
Walk.visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
Walk.visit_obj_fn_post = visit_obj_fn_post;
|
||||
Walk.visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
Walk.visit_obj_drop_post = visit_obj_drop_post;
|
||||
Walk.visit_slot_identified_pre = visit_slot_identified_pre;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre;
|
||||
Walk.visit_block_pre = visit_block_pre;
|
||||
Walk.visit_block_post = visit_block_post }
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let path = Stack.create () in
|
||||
let passes =
|
||||
[|
|
||||
(loop_depth_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|]
|
||||
in
|
||||
|
||||
run_passes cx "loop" path passes (log cx "%s") crate;
|
||||
()
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
959
src/boot/me/resolve.ml
Normal file
959
src/boot/me/resolve.ml
Normal file
@ -0,0 +1,959 @@
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
(*
|
||||
* Resolution passes:
|
||||
*
|
||||
* - build multiple 'scope' hashtables mapping slot_key -> node_id
|
||||
* - build single 'type inference' hashtable mapping node_id -> slot
|
||||
*
|
||||
* (note: not every slot is identified; only those that are declared
|
||||
* in statements and/or can participate in local type inference.
|
||||
* Those in function signatures are not, f.e. Also no type values
|
||||
* are identified, though module items are. )
|
||||
*
|
||||
*)
|
||||
|
||||
|
||||
let log cx = Session.log "resolve"
|
||||
cx.ctxt_sess.Session.sess_log_resolve
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog cx thunk =
|
||||
if cx.ctxt_sess.Session.sess_log_resolve
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
|
||||
let block_scope_forming_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let visit_block_pre b =
|
||||
if not (Hashtbl.mem cx.ctxt_block_items b.id)
|
||||
then htab_put cx.ctxt_block_items b.id (Hashtbl.create 0);
|
||||
if not (Hashtbl.mem cx.ctxt_block_slots b.id)
|
||||
then htab_put cx.ctxt_block_slots b.id (Hashtbl.create 0);
|
||||
inner.Walk.visit_block_pre b
|
||||
in
|
||||
{ inner with Walk.visit_block_pre = visit_block_pre }
|
||||
;;
|
||||
|
||||
|
||||
let stmt_collecting_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let block_ids = Stack.create () in
|
||||
let visit_block_pre (b:Ast.block) =
|
||||
Stack.push b.id block_ids;
|
||||
inner.Walk.visit_block_pre b
|
||||
in
|
||||
let visit_block_post (b:Ast.block) =
|
||||
inner.Walk.visit_block_post b;
|
||||
ignore (Stack.pop block_ids)
|
||||
in
|
||||
|
||||
let visit_for_block
|
||||
((si:Ast.slot identified),(ident:Ast.ident))
|
||||
(block_id:node_id)
|
||||
: unit =
|
||||
let slots = Hashtbl.find cx.ctxt_block_slots block_id in
|
||||
let key = Ast.KEY_ident ident in
|
||||
log cx "found decl of '%s' in for-loop block header" ident;
|
||||
htab_put slots key si.id;
|
||||
htab_put cx.ctxt_slot_keys si.id key
|
||||
in
|
||||
|
||||
let visit_stmt_pre stmt =
|
||||
begin
|
||||
htab_put cx.ctxt_all_stmts stmt.id stmt;
|
||||
match stmt.node with
|
||||
Ast.STMT_decl d ->
|
||||
begin
|
||||
let bid = Stack.top block_ids in
|
||||
let items = Hashtbl.find cx.ctxt_block_items bid in
|
||||
let slots = Hashtbl.find cx.ctxt_block_slots bid in
|
||||
let check_and_log_ident id ident =
|
||||
if Hashtbl.mem items ident ||
|
||||
Hashtbl.mem slots (Ast.KEY_ident ident)
|
||||
then
|
||||
err (Some id)
|
||||
"duplicate declaration '%s' in block" ident
|
||||
else
|
||||
log cx "found decl of '%s' in block" ident
|
||||
in
|
||||
let check_and_log_tmp id tmp =
|
||||
if Hashtbl.mem slots (Ast.KEY_temp tmp)
|
||||
then
|
||||
err (Some id)
|
||||
"duplicate declaration of temp #%d in block"
|
||||
(int_of_temp tmp)
|
||||
else
|
||||
log cx "found decl of temp #%d in block" (int_of_temp tmp)
|
||||
in
|
||||
let check_and_log_key id key =
|
||||
match key with
|
||||
Ast.KEY_ident i -> check_and_log_ident id i
|
||||
| Ast.KEY_temp t -> check_and_log_tmp id t
|
||||
in
|
||||
match d with
|
||||
Ast.DECL_mod_item (ident, item) ->
|
||||
check_and_log_ident item.id ident;
|
||||
htab_put items ident item.id
|
||||
| Ast.DECL_slot (key, sid) ->
|
||||
check_and_log_key sid.id key;
|
||||
htab_put slots key sid.id;
|
||||
htab_put cx.ctxt_slot_keys sid.id key
|
||||
end
|
||||
| Ast.STMT_for f ->
|
||||
visit_for_block f.Ast.for_slot f.Ast.for_body.id
|
||||
| Ast.STMT_for_each f ->
|
||||
visit_for_block f.Ast.for_each_slot f.Ast.for_each_head.id
|
||||
| Ast.STMT_alt_tag { Ast.alt_tag_arms = arms } ->
|
||||
let rec resolve_pat block pat =
|
||||
match pat with
|
||||
Ast.PAT_slot ({ id = slot_id }, ident) ->
|
||||
let slots = Hashtbl.find cx.ctxt_block_slots block.id in
|
||||
let key = Ast.KEY_ident ident in
|
||||
htab_put slots key slot_id;
|
||||
htab_put cx.ctxt_slot_keys slot_id key
|
||||
| Ast.PAT_tag (_, pats) -> Array.iter (resolve_pat block) pats
|
||||
| Ast.PAT_lit _ | Ast.PAT_wild -> ()
|
||||
in
|
||||
Array.iter (fun { node = (p, b) } -> resolve_pat b p) arms
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre stmt
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_block_pre = visit_block_pre;
|
||||
Walk.visit_block_post = visit_block_post;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre }
|
||||
;;
|
||||
|
||||
|
||||
let all_item_collecting_visitor
|
||||
(cx:ctxt)
|
||||
(path:Ast.name_component Stack.t)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let items = Stack.create () in
|
||||
|
||||
let push_on_item_arg_list item_id arg_id =
|
||||
let existing =
|
||||
match htab_search cx.ctxt_frame_args item_id with
|
||||
None -> []
|
||||
| Some x -> x
|
||||
in
|
||||
htab_put cx.ctxt_slot_is_arg arg_id ();
|
||||
Hashtbl.replace cx.ctxt_frame_args item_id (arg_id :: existing)
|
||||
in
|
||||
|
||||
let note_header item_id header =
|
||||
Array.iter
|
||||
(fun (sloti,ident) ->
|
||||
let key = Ast.KEY_ident ident in
|
||||
htab_put cx.ctxt_slot_keys sloti.id key;
|
||||
push_on_item_arg_list item_id sloti.id)
|
||||
header;
|
||||
in
|
||||
|
||||
let visit_mod_item_pre n p i =
|
||||
Stack.push i.id items;
|
||||
Array.iter (fun p -> htab_put cx.ctxt_all_defns p.id
|
||||
(DEFN_ty_param p.node)) p;
|
||||
htab_put cx.ctxt_all_defns i.id (DEFN_item i.node);
|
||||
htab_put cx.ctxt_all_item_names i.id (Walk.path_to_name path);
|
||||
log cx "collected item #%d: %s" (int_of_node i.id) n;
|
||||
begin
|
||||
(* FIXME: this is incomplete. *)
|
||||
match i.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_fn f ->
|
||||
note_header i.id f.Ast.fn_input_slots;
|
||||
| Ast.MOD_ITEM_obj ob ->
|
||||
note_header i.id ob.Ast.obj_state;
|
||||
| Ast.MOD_ITEM_tag (header_slots, _, _) ->
|
||||
let skey i = Printf.sprintf "_%d" i in
|
||||
note_header i.id
|
||||
(Array.mapi (fun i s -> (s, skey i)) header_slots)
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre n p i
|
||||
in
|
||||
|
||||
let visit_mod_item_post n p i =
|
||||
inner.Walk.visit_mod_item_post n p i;
|
||||
ignore (Stack.pop items)
|
||||
in
|
||||
|
||||
let visit_obj_fn_pre obj ident fn =
|
||||
htab_put cx.ctxt_all_defns fn.id (DEFN_obj_fn (obj.id, fn.node));
|
||||
htab_put cx.ctxt_all_item_names fn.id (Walk.path_to_name path);
|
||||
note_header fn.id fn.node.Ast.fn_input_slots;
|
||||
inner.Walk.visit_obj_fn_pre obj ident fn
|
||||
in
|
||||
|
||||
let visit_obj_drop_pre obj b =
|
||||
htab_put cx.ctxt_all_defns b.id (DEFN_obj_drop obj.id);
|
||||
htab_put cx.ctxt_all_item_names b.id (Walk.path_to_name path);
|
||||
inner.Walk.visit_obj_drop_pre obj b
|
||||
in
|
||||
|
||||
let visit_stmt_pre s =
|
||||
begin
|
||||
match s.node with
|
||||
Ast.STMT_for_each fe ->
|
||||
let id = fe.Ast.for_each_body.id in
|
||||
htab_put cx.ctxt_all_defns id
|
||||
(DEFN_loop_body (Stack.top items));
|
||||
htab_put cx.ctxt_all_item_names id
|
||||
(Walk.path_to_name path);
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s;
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_mod_item_post = visit_mod_item_post;
|
||||
Walk.visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
Walk.visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre; }
|
||||
;;
|
||||
|
||||
|
||||
let lookup_type_node_by_name
|
||||
(cx:ctxt)
|
||||
(scopes:scope list)
|
||||
(name:Ast.name)
|
||||
: node_id =
|
||||
iflog cx (fun _ ->
|
||||
log cx "lookup_simple_type_by_name %a"
|
||||
Ast.sprintf_name name);
|
||||
match lookup_by_name cx scopes name with
|
||||
None -> err None "unknown name: %a" Ast.sprintf_name name
|
||||
| Some (_, id) ->
|
||||
match htab_search cx.ctxt_all_defns id with
|
||||
Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_type _ })
|
||||
| Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_obj _ })
|
||||
| Some (DEFN_ty_param _) -> id
|
||||
| _ ->
|
||||
err None "Found non-type binding for %a"
|
||||
Ast.sprintf_name name
|
||||
;;
|
||||
|
||||
|
||||
let get_ty_references
|
||||
(t:Ast.ty)
|
||||
(cx:ctxt)
|
||||
(scopes:scope list)
|
||||
: node_id list =
|
||||
let base = ty_fold_list_concat () in
|
||||
let ty_fold_named n =
|
||||
[ lookup_type_node_by_name cx scopes n ]
|
||||
in
|
||||
let fold = { base with ty_fold_named = ty_fold_named } in
|
||||
fold_ty fold t
|
||||
;;
|
||||
|
||||
|
||||
let type_reference_and_tag_extracting_visitor
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list) ref)
|
||||
(node_to_references:(node_id,node_id list) Hashtbl.t)
|
||||
(all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let visit_mod_item_pre id params item =
|
||||
begin
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_type ty ->
|
||||
begin
|
||||
log cx "extracting references for type node %d"
|
||||
(int_of_node item.id);
|
||||
let referenced = get_ty_references ty cx (!scopes) in
|
||||
List.iter
|
||||
(fun i -> log cx "type %d references type %d"
|
||||
(int_of_node item.id) (int_of_node i)) referenced;
|
||||
htab_put node_to_references item.id referenced;
|
||||
match ty with
|
||||
Ast.TY_tag ttag ->
|
||||
htab_put all_tags item.id (ttag, (!scopes))
|
||||
| _ -> ()
|
||||
end
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre id params item
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre }
|
||||
;;
|
||||
|
||||
|
||||
type recur_info =
|
||||
{ recur_all_nodes: node_id list;
|
||||
recur_curr_iso: (node_id array) option; }
|
||||
;;
|
||||
|
||||
let empty_recur_info =
|
||||
{ recur_all_nodes = [];
|
||||
recur_curr_iso = None }
|
||||
;;
|
||||
|
||||
let push_node r n =
|
||||
{ r with recur_all_nodes = n :: r.recur_all_nodes }
|
||||
;;
|
||||
|
||||
let set_iso r i =
|
||||
{ r with recur_curr_iso = Some i }
|
||||
;;
|
||||
|
||||
|
||||
let index_in_curr_iso (recur:recur_info) (node:node_id) : int option =
|
||||
match recur.recur_curr_iso with
|
||||
None -> None
|
||||
| Some iso ->
|
||||
let rec search i =
|
||||
if i >= (Array.length iso)
|
||||
then None
|
||||
else
|
||||
if iso.(i) = node
|
||||
then Some i
|
||||
else search (i+1)
|
||||
in
|
||||
search 0
|
||||
;;
|
||||
|
||||
let need_ty_tag t =
|
||||
match t with
|
||||
Ast.TY_tag ttag -> ttag
|
||||
| _ -> err None "needed ty_tag"
|
||||
;;
|
||||
|
||||
|
||||
let rec ty_iso_of
|
||||
(cx:ctxt)
|
||||
(recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
|
||||
(all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
|
||||
(n:node_id)
|
||||
: Ast.ty =
|
||||
let _ = iflog cx (fun _ -> log cx "+++ ty_iso_of #%d" (int_of_node n)) in
|
||||
let group_table = Hashtbl.find recursive_tag_groups n in
|
||||
let group_array = Array.of_list (htab_keys group_table) in
|
||||
let compare_nodes a_id b_id =
|
||||
(* FIXME: this should sort by the sorted name-lists of the
|
||||
*constructors* of the tag, not the tag type name. *)
|
||||
let a_name = Hashtbl.find cx.ctxt_all_item_names a_id in
|
||||
let b_name = Hashtbl.find cx.ctxt_all_item_names b_id in
|
||||
compare a_name b_name
|
||||
in
|
||||
let recur = set_iso (push_node empty_recur_info n) group_array in
|
||||
let resolve_member member =
|
||||
let (tag, scopes) = Hashtbl.find all_tags member in
|
||||
let ty = Ast.TY_tag tag in
|
||||
let ty = resolve_type cx scopes recursive_tag_groups all_tags recur ty in
|
||||
need_ty_tag ty
|
||||
in
|
||||
Array.sort compare_nodes group_array;
|
||||
log cx "resolving node %d, %d-member iso group"
|
||||
(int_of_node n) (Array.length group_array);
|
||||
Array.iteri (fun i n -> log cx "member %d: %d" i
|
||||
(int_of_node n)) group_array;
|
||||
let group = Array.map resolve_member group_array in
|
||||
let rec search i =
|
||||
if i >= (Array.length group_array)
|
||||
then err None "node is not a member of its own iso group"
|
||||
else
|
||||
if group_array.(i) = n
|
||||
then i
|
||||
else search (i+1)
|
||||
in
|
||||
let iso =
|
||||
Ast.TY_iso { Ast.iso_index = (search 0);
|
||||
Ast.iso_group = group }
|
||||
in
|
||||
iflog cx (fun _ ->
|
||||
log cx "--- ty_iso_of #%d ==> %a"
|
||||
(int_of_node n) Ast.sprintf_ty iso);
|
||||
iso
|
||||
|
||||
|
||||
and lookup_type_by_name
|
||||
(cx:ctxt)
|
||||
(scopes:scope list)
|
||||
(recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
|
||||
(all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
|
||||
(recur:recur_info)
|
||||
(name:Ast.name)
|
||||
: ((scope list) * node_id * Ast.ty) =
|
||||
iflog cx (fun _ ->
|
||||
log cx "+++ lookup_type_by_name %a"
|
||||
Ast.sprintf_name name);
|
||||
match lookup_by_name cx scopes name with
|
||||
None -> err None "unknown name: %a" Ast.sprintf_name name
|
||||
| Some (scopes', id) ->
|
||||
let ty, params =
|
||||
match htab_search cx.ctxt_all_defns id with
|
||||
Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_type t;
|
||||
Ast.decl_params = params }) ->
|
||||
(t, Array.map (fun p -> p.node) params)
|
||||
| Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_obj ob;
|
||||
Ast.decl_params = params }) ->
|
||||
(Ast.TY_obj (ty_obj_of_obj ob),
|
||||
Array.map (fun p -> p.node) params)
|
||||
| Some (DEFN_ty_param (_, x)) ->
|
||||
(Ast.TY_param x, [||])
|
||||
| _ ->
|
||||
err None "Found non-type binding for %a"
|
||||
Ast.sprintf_name name
|
||||
in
|
||||
let args =
|
||||
match name with
|
||||
Ast.NAME_ext (_, Ast.COMP_app (_, args)) -> args
|
||||
| Ast.NAME_base (Ast.BASE_app (_, args)) -> args
|
||||
| _ -> [| |]
|
||||
in
|
||||
let args =
|
||||
iflog cx (fun _ -> log cx
|
||||
"lookup_type_by_name %a resolving %d type args"
|
||||
Ast.sprintf_name name
|
||||
(Array.length args));
|
||||
Array.mapi
|
||||
begin
|
||||
fun i t ->
|
||||
let t =
|
||||
resolve_type cx scopes recursive_tag_groups
|
||||
all_tags recur t
|
||||
in
|
||||
iflog cx (fun _ -> log cx
|
||||
"lookup_type_by_name resolved arg %d to %a" i
|
||||
Ast.sprintf_ty t);
|
||||
t
|
||||
end
|
||||
args
|
||||
in
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
log cx
|
||||
"lookup_type_by_name %a found ty %a"
|
||||
Ast.sprintf_name name Ast.sprintf_ty ty;
|
||||
log cx "applying %d type args to %d params"
|
||||
(Array.length args) (Array.length params);
|
||||
log cx "params: %s"
|
||||
(Ast.fmt_to_str Ast.fmt_decl_params params);
|
||||
log cx "args: %s"
|
||||
(Ast.fmt_to_str Ast.fmt_app_args args);
|
||||
end;
|
||||
let ty = rebuild_ty_under_params ty params args true in
|
||||
iflog cx (fun _ -> log cx "--- lookup_type_by_name %a ==> %a"
|
||||
Ast.sprintf_name name
|
||||
Ast.sprintf_ty ty);
|
||||
(scopes', id, ty)
|
||||
|
||||
and resolve_type
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list))
|
||||
(recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
|
||||
(all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
|
||||
(recur:recur_info)
|
||||
(t:Ast.ty)
|
||||
: Ast.ty =
|
||||
let _ = iflog cx (fun _ -> log cx "+++ resolve_type %a" Ast.sprintf_ty t) in
|
||||
let base = ty_fold_rebuild (fun t -> t) in
|
||||
let ty_fold_named name =
|
||||
let (scopes, node, t) =
|
||||
lookup_type_by_name cx scopes recursive_tag_groups all_tags recur name
|
||||
in
|
||||
iflog cx (fun _ ->
|
||||
log cx "resolved type name '%a' to item %d with ty %a"
|
||||
Ast.sprintf_name name (int_of_node node) Ast.sprintf_ty t);
|
||||
match index_in_curr_iso recur node with
|
||||
Some i -> Ast.TY_idx i
|
||||
| None ->
|
||||
if Hashtbl.mem recursive_tag_groups node
|
||||
then
|
||||
begin
|
||||
let ttag = need_ty_tag t in
|
||||
Hashtbl.replace all_tags node (ttag, scopes);
|
||||
ty_iso_of cx recursive_tag_groups all_tags node
|
||||
end
|
||||
else
|
||||
if List.mem node recur.recur_all_nodes
|
||||
then (err (Some node) "infinite recursive type definition: '%a'"
|
||||
Ast.sprintf_name name)
|
||||
else
|
||||
let recur = push_node recur node in
|
||||
iflog cx (fun _ -> log cx "recursively resolving type %a"
|
||||
Ast.sprintf_ty t);
|
||||
resolve_type cx scopes recursive_tag_groups all_tags recur t
|
||||
in
|
||||
let fold =
|
||||
{ base with
|
||||
ty_fold_named = ty_fold_named; }
|
||||
in
|
||||
let t' = fold_ty fold t in
|
||||
iflog cx (fun _ ->
|
||||
log cx "--- resolve_type %a ==> %a"
|
||||
Ast.sprintf_ty t Ast.sprintf_ty t');
|
||||
t'
|
||||
;;
|
||||
|
||||
|
||||
let type_resolving_visitor
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list) ref)
|
||||
(recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
|
||||
(all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let resolve_ty (t:Ast.ty) : Ast.ty =
|
||||
resolve_type cx (!scopes) recursive_tag_groups all_tags empty_recur_info t
|
||||
in
|
||||
|
||||
let resolve_slot (s:Ast.slot) : Ast.slot =
|
||||
match s.Ast.slot_ty with
|
||||
None -> s
|
||||
| Some ty -> { s with Ast.slot_ty = Some (resolve_ty ty) }
|
||||
in
|
||||
|
||||
let resolve_slot_identified
|
||||
(s:Ast.slot identified)
|
||||
: (Ast.slot identified) =
|
||||
try
|
||||
let slot = resolve_slot s.node in
|
||||
{ s with node = slot }
|
||||
with
|
||||
Semant_err (None, e) -> raise (Semant_err ((Some s.id), e))
|
||||
in
|
||||
|
||||
let visit_slot_identified_pre slot =
|
||||
let slot = resolve_slot_identified slot in
|
||||
htab_put cx.ctxt_all_defns slot.id (DEFN_slot slot.node);
|
||||
log cx "collected resolved slot #%d with type %s" (int_of_node slot.id)
|
||||
(match slot.node.Ast.slot_ty with
|
||||
None -> "??"
|
||||
| Some t -> (Ast.fmt_to_str Ast.fmt_ty t));
|
||||
inner.Walk.visit_slot_identified_pre slot
|
||||
in
|
||||
|
||||
let visit_mod_item_pre id params item =
|
||||
begin
|
||||
try
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_type ty ->
|
||||
let ty =
|
||||
resolve_type cx (!scopes) recursive_tag_groups
|
||||
all_tags empty_recur_info ty
|
||||
in
|
||||
log cx "resolved item %s, defining type %a"
|
||||
id Ast.sprintf_ty ty;
|
||||
htab_put cx.ctxt_all_type_items item.id ty;
|
||||
htab_put cx.ctxt_all_item_types item.id Ast.TY_type
|
||||
|
||||
(*
|
||||
* Don't resolve the "type" of a mod item; just resolve its
|
||||
* members.
|
||||
*)
|
||||
| Ast.MOD_ITEM_mod _ -> ()
|
||||
|
||||
| Ast.MOD_ITEM_tag (header_slots, _, nid)
|
||||
when Hashtbl.mem recursive_tag_groups nid ->
|
||||
begin
|
||||
match ty_of_mod_item true item with
|
||||
Ast.TY_fn (tsig, taux) ->
|
||||
let input_slots =
|
||||
Array.map
|
||||
(fun sloti -> resolve_slot sloti.node)
|
||||
header_slots
|
||||
in
|
||||
let output_slot =
|
||||
interior_slot (ty_iso_of cx recursive_tag_groups
|
||||
all_tags nid)
|
||||
in
|
||||
let ty =
|
||||
Ast.TY_fn
|
||||
({tsig with
|
||||
Ast.sig_input_slots = input_slots;
|
||||
Ast.sig_output_slot = output_slot }, taux)
|
||||
in
|
||||
log cx "resolved recursive tag %s, type as %a"
|
||||
id Ast.sprintf_ty ty;
|
||||
htab_put cx.ctxt_all_item_types item.id ty
|
||||
| _ -> bug () "recursive tag with non-function type"
|
||||
end
|
||||
|
||||
| _ ->
|
||||
let t = ty_of_mod_item true item in
|
||||
let ty =
|
||||
resolve_type cx (!scopes) recursive_tag_groups
|
||||
all_tags empty_recur_info t
|
||||
in
|
||||
log cx "resolved item %s, type as %a" id Ast.sprintf_ty ty;
|
||||
htab_put cx.ctxt_all_item_types item.id ty;
|
||||
with
|
||||
Semant_err (None, e) -> raise (Semant_err ((Some item.id), e))
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre id params item
|
||||
in
|
||||
|
||||
let visit_obj_fn_pre obj ident fn =
|
||||
let fty =
|
||||
resolve_type cx (!scopes) recursive_tag_groups all_tags
|
||||
empty_recur_info (Ast.TY_fn (ty_fn_of_fn fn.node))
|
||||
in
|
||||
log cx "resolved obj fn %s as %a" ident Ast.sprintf_ty fty;
|
||||
htab_put cx.ctxt_all_item_types fn.id fty;
|
||||
inner.Walk.visit_obj_fn_pre obj ident fn
|
||||
in
|
||||
|
||||
let visit_obj_drop_pre obj b =
|
||||
let fty = mk_simple_ty_fn [| |] in
|
||||
htab_put cx.ctxt_all_item_types b.id fty;
|
||||
inner.Walk.visit_obj_drop_pre obj b
|
||||
in
|
||||
|
||||
let visit_stmt_pre stmt =
|
||||
begin
|
||||
match stmt.node with
|
||||
Ast.STMT_for_each fe ->
|
||||
let id = fe.Ast.for_each_body.id in
|
||||
let fty = mk_simple_ty_iter [| |] in
|
||||
htab_put cx.ctxt_all_item_types id fty;
|
||||
| Ast.STMT_copy (_, Ast.EXPR_unary (Ast.UNOP_cast t, _)) ->
|
||||
let ty = resolve_ty t.node in
|
||||
htab_put cx.ctxt_all_cast_types t.id ty
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre stmt
|
||||
in
|
||||
|
||||
let visit_lval_pre lv =
|
||||
let rec rebuild_lval' lv =
|
||||
match lv with
|
||||
Ast.LVAL_ext (base, ext) ->
|
||||
let ext =
|
||||
match ext with
|
||||
Ast.COMP_named (Ast.COMP_ident _)
|
||||
| Ast.COMP_named (Ast.COMP_idx _)
|
||||
| Ast.COMP_atom (Ast.ATOM_literal _) -> ext
|
||||
| Ast.COMP_atom (Ast.ATOM_lval lv) ->
|
||||
Ast.COMP_atom (Ast.ATOM_lval (rebuild_lval lv))
|
||||
| Ast.COMP_named (Ast.COMP_app (ident, params)) ->
|
||||
Ast.COMP_named
|
||||
(Ast.COMP_app (ident, Array.map resolve_ty params))
|
||||
in
|
||||
Ast.LVAL_ext (rebuild_lval' base, ext)
|
||||
|
||||
| Ast.LVAL_base nb ->
|
||||
let node =
|
||||
match nb.node with
|
||||
Ast.BASE_ident _
|
||||
| Ast.BASE_temp _ -> nb.node
|
||||
| Ast.BASE_app (ident, params) ->
|
||||
Ast.BASE_app (ident, Array.map resolve_ty params)
|
||||
in
|
||||
Ast.LVAL_base {nb with node = node}
|
||||
|
||||
and rebuild_lval lv =
|
||||
let id = lval_base_id lv in
|
||||
let lv' = rebuild_lval' lv in
|
||||
iflog cx (fun _ -> log cx "rebuilt lval %a as %a (#%d)"
|
||||
Ast.sprintf_lval lv Ast.sprintf_lval lv'
|
||||
(int_of_node id));
|
||||
htab_put cx.ctxt_all_lvals id lv';
|
||||
lv'
|
||||
in
|
||||
ignore (rebuild_lval lv);
|
||||
inner.Walk.visit_lval_pre lv
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_slot_identified_pre = visit_slot_identified_pre;
|
||||
Walk.visit_mod_item_pre = visit_mod_item_pre;
|
||||
Walk.visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
Walk.visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
Walk.visit_stmt_pre = visit_stmt_pre;
|
||||
Walk.visit_lval_pre = visit_lval_pre; }
|
||||
;;
|
||||
|
||||
|
||||
let lval_base_resolving_visitor
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list) ref)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let lookup_referent_by_ident id ident =
|
||||
log cx "looking up slot or item with ident '%s'" ident;
|
||||
match lookup cx (!scopes) (Ast.KEY_ident ident) with
|
||||
None -> err (Some id) "unresolved identifier '%s'" ident
|
||||
| Some (_, id) -> (log cx "resolved to node id #%d"
|
||||
(int_of_node id); id)
|
||||
in
|
||||
let lookup_slot_by_temp id temp =
|
||||
log cx "looking up temp slot #%d" (int_of_temp temp);
|
||||
let res = lookup cx (!scopes) (Ast.KEY_temp temp) in
|
||||
match res with
|
||||
None -> err
|
||||
(Some id) "unresolved temp node #%d" (int_of_temp temp)
|
||||
| Some (_, id) ->
|
||||
(log cx "resolved to node id #%d" (int_of_node id); id)
|
||||
in
|
||||
let lookup_referent_by_name_base id nb =
|
||||
match nb with
|
||||
Ast.BASE_ident ident
|
||||
| Ast.BASE_app (ident, _) -> lookup_referent_by_ident id ident
|
||||
| Ast.BASE_temp temp -> lookup_slot_by_temp id temp
|
||||
in
|
||||
|
||||
let visit_lval_pre lv =
|
||||
let rec lookup_lval lv =
|
||||
iflog cx (fun _ ->
|
||||
log cx "looking up lval #%d"
|
||||
(int_of_node (lval_base_id lv)));
|
||||
match lv with
|
||||
Ast.LVAL_ext (base, ext) ->
|
||||
begin
|
||||
lookup_lval base;
|
||||
match ext with
|
||||
Ast.COMP_atom (Ast.ATOM_lval lv') -> lookup_lval lv'
|
||||
| _ -> ()
|
||||
end
|
||||
| Ast.LVAL_base nb ->
|
||||
let referent_id = lookup_referent_by_name_base nb.id nb.node in
|
||||
iflog cx (fun _ -> log cx "resolved lval #%d to referent #%d"
|
||||
(int_of_node nb.id) (int_of_node referent_id));
|
||||
htab_put cx.ctxt_lval_to_referent nb.id referent_id
|
||||
in
|
||||
lookup_lval lv;
|
||||
inner.Walk.visit_lval_pre lv
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_lval_pre = visit_lval_pre }
|
||||
;;
|
||||
|
||||
|
||||
|
||||
(*
|
||||
* iso-recursion groups are very complicated.
|
||||
*
|
||||
* - iso groups are always rooted at *named* ty_tag nodes
|
||||
*
|
||||
* - consider:
|
||||
*
|
||||
* type colour = tag(red, green, blue);
|
||||
* type list = tag(cons(colour, @list), nil())
|
||||
*
|
||||
* this should include list as an iso but not colour,
|
||||
* should result in:
|
||||
*
|
||||
* type list = iso[<0>:tag(cons(tag(red,green,blue),@#1))]
|
||||
*
|
||||
* - consider:
|
||||
*
|
||||
* type colour = tag(red, green, blue);
|
||||
* type tree = tag(children(@list), leaf(colour))
|
||||
* type list = tag(cons(@tree, @list), nil())
|
||||
*
|
||||
* this should result in:
|
||||
*
|
||||
* type list = iso[<0>:tag(cons(@#2, @#1),nil());
|
||||
* 1: tag(children(@#1),leaf(tag(red,green,blue)))]
|
||||
*
|
||||
* - how can you calculate these?
|
||||
*
|
||||
* - start by making a map from named-tag-node-id -> referenced-other-nodes
|
||||
* - for each member in the set, if you can get from itself to itself, keep
|
||||
* it, otherwise it's non-recursive => non-interesting, delete it.
|
||||
* - group the members (now all recursive) by dependency
|
||||
* - assign index-number to each elt of group
|
||||
* - fully resolve each elt of group, turning names into numbers or chasing
|
||||
* through to fully-resolving targets as necessary
|
||||
* - place group in iso, store differently-indexed value in table for each
|
||||
*
|
||||
*
|
||||
* - what are the illegal forms?
|
||||
* - recursion that takes indefinite storage to form a tag, eg.
|
||||
*
|
||||
* type t = tag(foo(t));
|
||||
*
|
||||
* - recursion that makes a tag unconstructable, eg:
|
||||
*
|
||||
* type t = tag(foo(@t));
|
||||
*)
|
||||
|
||||
let resolve_recursion
|
||||
(cx:ctxt)
|
||||
(node_to_references:(node_id,node_id list) Hashtbl.t)
|
||||
(recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
|
||||
: unit =
|
||||
|
||||
let recursive_tag_types = Hashtbl.create 0 in
|
||||
|
||||
let rec can_reach
|
||||
(target:node_id)
|
||||
(visited:node_id list)
|
||||
(curr:node_id)
|
||||
: bool =
|
||||
if List.mem curr visited
|
||||
then false
|
||||
else
|
||||
match htab_search node_to_references curr with
|
||||
None -> false
|
||||
| Some referenced ->
|
||||
if List.mem target referenced
|
||||
then true
|
||||
else List.exists (can_reach target (curr :: visited)) referenced
|
||||
in
|
||||
|
||||
let extract_recursive_tags _ =
|
||||
Hashtbl.iter
|
||||
begin fun id _ ->
|
||||
if can_reach id [] id
|
||||
then begin
|
||||
match Hashtbl.find cx.ctxt_all_defns id with
|
||||
DEFN_item
|
||||
{ Ast.decl_item = Ast.MOD_ITEM_type (Ast.TY_tag _) } ->
|
||||
log cx "type %d is a recursive tag" (int_of_node id);
|
||||
Hashtbl.replace recursive_tag_types id ()
|
||||
| _ ->
|
||||
log cx "type %d is recursive, but not a tag" (int_of_node id);
|
||||
end
|
||||
else log cx "type %d is non-recursive" (int_of_node id);
|
||||
end
|
||||
node_to_references
|
||||
in
|
||||
|
||||
let group_recursive_tags _ =
|
||||
while (Hashtbl.length recursive_tag_types) != 0 do
|
||||
let keys = htab_keys recursive_tag_types in
|
||||
let root = List.hd keys in
|
||||
let group = Hashtbl.create 0 in
|
||||
let rec walk visited node =
|
||||
if List.mem node visited
|
||||
then ()
|
||||
else
|
||||
begin
|
||||
if Hashtbl.mem recursive_tag_types node
|
||||
then
|
||||
begin
|
||||
Hashtbl.remove recursive_tag_types node;
|
||||
htab_put recursive_tag_groups node group;
|
||||
htab_put group node ();
|
||||
log cx "recursion group rooted at tag %d contains tag %d"
|
||||
(int_of_node root) (int_of_node node);
|
||||
end;
|
||||
match htab_search node_to_references node with
|
||||
None -> ()
|
||||
| Some referenced ->
|
||||
List.iter (walk (node :: visited)) referenced
|
||||
end
|
||||
in
|
||||
walk [] root;
|
||||
done
|
||||
in
|
||||
|
||||
begin
|
||||
extract_recursive_tags ();
|
||||
group_recursive_tags ();
|
||||
log cx "found %d independent type-recursion groups"
|
||||
(Hashtbl.length recursive_tag_groups);
|
||||
end
|
||||
;;
|
||||
|
||||
let pattern_resolving_visitor
|
||||
(cx:ctxt)
|
||||
(scopes:scope list ref)
|
||||
(inner:Walk.visitor) : Walk.visitor =
|
||||
let visit_stmt_pre stmt =
|
||||
begin
|
||||
match stmt.node with
|
||||
Ast.STMT_alt_tag { Ast.alt_tag_lval = _; Ast.alt_tag_arms = arms } ->
|
||||
let resolve_arm { node = arm } =
|
||||
match fst arm with
|
||||
Ast.PAT_tag (ident, _) ->
|
||||
begin
|
||||
match lookup_by_ident cx !scopes ident with
|
||||
None ->
|
||||
err None "unresolved tag constructor '%s'" ident
|
||||
| Some (_, tag_id) ->
|
||||
match Hashtbl.find cx.ctxt_all_defns tag_id with
|
||||
DEFN_item {
|
||||
Ast.decl_item = Ast.MOD_ITEM_tag _
|
||||
} -> ()
|
||||
| _ ->
|
||||
err None "'%s' is not a tag constructor" ident
|
||||
end
|
||||
| _ -> ()
|
||||
|
||||
in
|
||||
Array.iter resolve_arm arms
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre stmt
|
||||
in
|
||||
{ inner with Walk.visit_stmt_pre = visit_stmt_pre }
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let (scopes:(scope list) ref) = ref [] in
|
||||
let path = Stack.create () in
|
||||
|
||||
let node_to_references = Hashtbl.create 0 in
|
||||
let all_tags = Hashtbl.create 0 in
|
||||
let recursive_tag_groups = Hashtbl.create 0 in
|
||||
|
||||
let passes_0 =
|
||||
[|
|
||||
(block_scope_forming_visitor cx Walk.empty_visitor);
|
||||
(stmt_collecting_visitor cx
|
||||
(all_item_collecting_visitor cx path
|
||||
Walk.empty_visitor));
|
||||
(scope_stack_managing_visitor scopes
|
||||
(type_reference_and_tag_extracting_visitor
|
||||
cx scopes node_to_references all_tags
|
||||
Walk.empty_visitor))
|
||||
|]
|
||||
in
|
||||
let passes_1 =
|
||||
[|
|
||||
(scope_stack_managing_visitor scopes
|
||||
(type_resolving_visitor cx scopes
|
||||
recursive_tag_groups all_tags
|
||||
(lval_base_resolving_visitor cx scopes
|
||||
Walk.empty_visitor)));
|
||||
|]
|
||||
in
|
||||
let passes_2 =
|
||||
[|
|
||||
(scope_stack_managing_visitor scopes
|
||||
(pattern_resolving_visitor cx scopes
|
||||
Walk.empty_visitor))
|
||||
|]
|
||||
in
|
||||
log cx "running primary resolve passes";
|
||||
run_passes cx "resolve collect" path passes_0 (log cx "%s") crate;
|
||||
resolve_recursion cx node_to_references recursive_tag_groups;
|
||||
log cx "running secondary resolve passes";
|
||||
run_passes cx "resolve bind" path passes_1 (log cx "%s") crate;
|
||||
log cx "running tertiary resolve passes";
|
||||
run_passes cx "resolve patterns" path passes_2 (log cx "%s") crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
1969
src/boot/me/semant.ml
Normal file
1969
src/boot/me/semant.ml
Normal file
File diff suppressed because it is too large
Load Diff
5031
src/boot/me/trans.ml
Normal file
5031
src/boot/me/trans.ml
Normal file
File diff suppressed because it is too large
Load Diff
238
src/boot/me/transutil.ml
Normal file
238
src/boot/me/transutil.ml
Normal file
@ -0,0 +1,238 @@
|
||||
open Common;;
|
||||
open Semant;;
|
||||
|
||||
(* A note on GC:
|
||||
*
|
||||
* We employ -- or "will employ" when the last few pieces of it are done -- a
|
||||
* "simple" precise, mark-sweep, single-generation, per-task (thereby
|
||||
* preemptable and relatively quick) GC scheme on mutable memory.
|
||||
*
|
||||
* - For the sake of this note, call any exterior of 'state' effect a gc_val.
|
||||
*
|
||||
* - gc_vals come from the same malloc as all other values but undergo
|
||||
* different storage management.
|
||||
*
|
||||
* - Every frame has a frame_glue_fns pointer in its fp[-1] slot, written on
|
||||
* function-entry.
|
||||
*
|
||||
* - gc_vals have *three* extra words at their head, not one.
|
||||
*
|
||||
* - A pointer to a gc_val, however, points to the third of these three
|
||||
* words. So a certain quantity of code can treat gc_vals the same way it
|
||||
* would treat refcounted exterior vals.
|
||||
*
|
||||
* - The first word at the head of a gc_val is used as a refcount, as in
|
||||
* non-gc allocations.
|
||||
*
|
||||
* - The (-1)st word at the head of a gc_val is a pointer to a tydesc,
|
||||
* with the low bit of that pointer used as a mark bit.
|
||||
*
|
||||
* - The (-2)nd word at the head of a gc_val is a linked-list pointer to the
|
||||
* gc_val that was allocated (temporally) just before it. Following this
|
||||
* list traces through all the currently active gc_vals in a task.
|
||||
*
|
||||
* - The task has a gc_alloc_chain field that points to the most-recent
|
||||
* gc_val allocated.
|
||||
*
|
||||
* - GC glue has two phases, mark and sweep:
|
||||
*
|
||||
* - The mark phase walks down the frame chain, like the unwinder. It calls
|
||||
* each frame's mark glue as it's passing through. This will mark all the
|
||||
* reachable parts of the task's gc_vals.
|
||||
*
|
||||
* - The sweep phase walks down the task's gc_alloc_chain checking to see
|
||||
* if each allocation has been marked. If marked, it has its mark-bit
|
||||
* reset and the sweep passes it by. If unmarked, it has its tydesc
|
||||
* free_glue called on its body, and is unlinked from the chain. The
|
||||
* free-glue will cause the allocation to (recursively) drop all of its
|
||||
* references and/or run dtors.
|
||||
*
|
||||
* - Note that there is no "special gc state" at work here; the task looks
|
||||
* like it's running normal code that happens to not perform any gc_val
|
||||
* allocation. Mark-bit twiddling is open-coded into all the mark
|
||||
* functions, which know their contents; we only have to do O(frames)
|
||||
* indirect calls to mark, the rest are static. Sweeping costs O(gc-heap)
|
||||
* indirect calls, unfortunately, because the set of sweep functions to
|
||||
* call is arbitrary based on allocation order.
|
||||
*)
|
||||
|
||||
|
||||
type mem_ctrl =
|
||||
MEM_rc_opaque
|
||||
| MEM_rc_struct
|
||||
| MEM_gc
|
||||
| MEM_interior
|
||||
;;
|
||||
|
||||
type clone_ctrl =
|
||||
CLONE_none
|
||||
| CLONE_chan of Il.cell
|
||||
| CLONE_all of Il.cell
|
||||
;;
|
||||
|
||||
type call_ctrl =
|
||||
CALL_direct
|
||||
| CALL_vtbl
|
||||
| CALL_indirect
|
||||
;;
|
||||
|
||||
type for_each_ctrl =
|
||||
{
|
||||
for_each_fixup: fixup;
|
||||
for_each_depth: int;
|
||||
}
|
||||
;;
|
||||
|
||||
let word_sz (abi:Abi.abi) : int64 =
|
||||
abi.Abi.abi_word_sz
|
||||
;;
|
||||
|
||||
let word_n (abi:Abi.abi) (n:int) : int64 =
|
||||
Int64.mul (word_sz abi) (Int64.of_int n)
|
||||
;;
|
||||
|
||||
let word_bits (abi:Abi.abi) : Il.bits =
|
||||
abi.Abi.abi_word_bits
|
||||
;;
|
||||
|
||||
let word_ty_mach (abi:Abi.abi) : ty_mach =
|
||||
match word_bits abi with
|
||||
Il.Bits8 -> TY_u8
|
||||
| Il.Bits16 -> TY_u16
|
||||
| Il.Bits32 -> TY_u32
|
||||
| Il.Bits64 -> TY_u64
|
||||
;;
|
||||
|
||||
let word_ty_signed_mach (abi:Abi.abi) : ty_mach =
|
||||
match word_bits abi with
|
||||
Il.Bits8 -> TY_i8
|
||||
| Il.Bits16 -> TY_i16
|
||||
| Il.Bits32 -> TY_i32
|
||||
| Il.Bits64 -> TY_i64
|
||||
;;
|
||||
|
||||
|
||||
let slot_mem_ctrl (slot:Ast.slot) : mem_ctrl =
|
||||
let ty = slot_ty slot in
|
||||
match ty with
|
||||
Ast.TY_port _
|
||||
| Ast.TY_chan _
|
||||
| Ast.TY_task
|
||||
| Ast.TY_vec _
|
||||
| Ast.TY_str -> MEM_rc_opaque
|
||||
| _ ->
|
||||
match slot.Ast.slot_mode with
|
||||
Ast.MODE_exterior _ when type_is_structured ty ->
|
||||
if type_has_state ty
|
||||
then MEM_gc
|
||||
else MEM_rc_struct
|
||||
| Ast.MODE_exterior _ ->
|
||||
MEM_rc_opaque
|
||||
| _ ->
|
||||
MEM_interior
|
||||
;;
|
||||
|
||||
|
||||
let iter_block_slots
|
||||
(cx:Semant.ctxt)
|
||||
(block_id:node_id)
|
||||
(fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
|
||||
: unit =
|
||||
let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
|
||||
Hashtbl.iter
|
||||
begin
|
||||
fun key slot_id ->
|
||||
let slot = referent_to_slot cx slot_id in
|
||||
fn key slot_id slot
|
||||
end
|
||||
block_slots
|
||||
;;
|
||||
|
||||
let iter_frame_slots
|
||||
(cx:Semant.ctxt)
|
||||
(frame_id:node_id)
|
||||
(fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
|
||||
: unit =
|
||||
let blocks = Hashtbl.find cx.ctxt_frame_blocks frame_id in
|
||||
List.iter (fun block -> iter_block_slots cx block fn) blocks
|
||||
;;
|
||||
|
||||
let iter_arg_slots
|
||||
(cx:Semant.ctxt)
|
||||
(frame_id:node_id)
|
||||
(fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
|
||||
: unit =
|
||||
match htab_search cx.ctxt_frame_args frame_id with
|
||||
None -> ()
|
||||
| Some ls ->
|
||||
List.iter
|
||||
begin
|
||||
fun slot_id ->
|
||||
let key = Hashtbl.find cx.ctxt_slot_keys slot_id in
|
||||
let slot = referent_to_slot cx slot_id in
|
||||
fn key slot_id slot
|
||||
end
|
||||
ls
|
||||
;;
|
||||
|
||||
let iter_frame_and_arg_slots
|
||||
(cx:Semant.ctxt)
|
||||
(frame_id:node_id)
|
||||
(fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
|
||||
: unit =
|
||||
iter_frame_slots cx frame_id fn;
|
||||
iter_arg_slots cx frame_id fn;
|
||||
;;
|
||||
|
||||
let next_power_of_two (x:int64) : int64 =
|
||||
let xr = ref (Int64.sub x 1L) in
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 1);
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 2);
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 4);
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 8);
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 16);
|
||||
xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 32);
|
||||
Int64.add 1L (!xr)
|
||||
;;
|
||||
|
||||
let iter_tup_slots
|
||||
(get_element_ptr:'a -> int -> 'a)
|
||||
(dst_ptr:'a)
|
||||
(src_ptr:'a)
|
||||
(slots:Ast.ty_tup)
|
||||
(f:'a -> 'a -> Ast.slot -> (Ast.ty_iso option) -> unit)
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
Array.iteri
|
||||
begin
|
||||
fun i slot ->
|
||||
f (get_element_ptr dst_ptr i)
|
||||
(get_element_ptr src_ptr i)
|
||||
slot curr_iso
|
||||
end
|
||||
slots
|
||||
;;
|
||||
|
||||
let iter_rec_slots
|
||||
(get_element_ptr:'a -> int -> 'a)
|
||||
(dst_ptr:'a)
|
||||
(src_ptr:'a)
|
||||
(entries:Ast.ty_rec)
|
||||
(f:'a -> 'a -> Ast.slot -> (Ast.ty_iso option) -> unit)
|
||||
(curr_iso:Ast.ty_iso option)
|
||||
: unit =
|
||||
iter_tup_slots get_element_ptr dst_ptr src_ptr
|
||||
(Array.map snd entries) f curr_iso
|
||||
;;
|
||||
|
||||
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1294
src/boot/me/type.ml
Normal file
1294
src/boot/me/type.ml
Normal file
File diff suppressed because it is too large
Load Diff
1089
src/boot/me/typestate.ml
Normal file
1089
src/boot/me/typestate.ml
Normal file
File diff suppressed because it is too large
Load Diff
687
src/boot/me/walk.ml
Normal file
687
src/boot/me/walk.ml
Normal file
@ -0,0 +1,687 @@
|
||||
|
||||
open Common;;
|
||||
|
||||
(*
|
||||
* The purpose of this module is just to decouple the AST from the
|
||||
* various passes that are interested in visiting "parts" of it.
|
||||
* If the AST shifts, we have better odds of the shift only affecting
|
||||
* this module rather than all of its clients. Similarly if the
|
||||
* clients only need to visit part, they only have to define the
|
||||
* part of the walk they're interested in, making it cheaper to define
|
||||
* multiple passes.
|
||||
*)
|
||||
|
||||
type visitor =
|
||||
{
|
||||
visit_stmt_pre: Ast.stmt -> unit;
|
||||
visit_stmt_post: Ast.stmt -> unit;
|
||||
visit_slot_identified_pre: (Ast.slot identified) -> unit;
|
||||
visit_slot_identified_post: (Ast.slot identified) -> unit;
|
||||
visit_expr_pre: Ast.expr -> unit;
|
||||
visit_expr_post: Ast.expr -> unit;
|
||||
visit_ty_pre: Ast.ty -> unit;
|
||||
visit_ty_post: Ast.ty -> unit;
|
||||
visit_constr_pre: node_id option -> Ast.constr -> unit;
|
||||
visit_constr_post: node_id option -> Ast.constr -> unit;
|
||||
visit_pat_pre: Ast.pat -> unit;
|
||||
visit_pat_post: Ast.pat -> unit;
|
||||
visit_block_pre: Ast.block -> unit;
|
||||
visit_block_post: Ast.block -> unit;
|
||||
|
||||
visit_lit_pre: Ast.lit -> unit;
|
||||
visit_lit_post: Ast.lit -> unit;
|
||||
visit_lval_pre: Ast.lval -> unit;
|
||||
visit_lval_post: Ast.lval -> unit;
|
||||
visit_mod_item_pre:
|
||||
(Ast.ident
|
||||
-> ((Ast.ty_param identified) array)
|
||||
-> Ast.mod_item
|
||||
-> unit);
|
||||
visit_mod_item_post:
|
||||
(Ast.ident
|
||||
-> ((Ast.ty_param identified) array)
|
||||
-> Ast.mod_item
|
||||
-> unit);
|
||||
visit_obj_fn_pre:
|
||||
(Ast.obj identified) -> Ast.ident -> (Ast.fn identified) -> unit;
|
||||
visit_obj_fn_post:
|
||||
(Ast.obj identified) -> Ast.ident -> (Ast.fn identified) -> unit;
|
||||
visit_obj_drop_pre:
|
||||
(Ast.obj identified) -> Ast.block -> unit;
|
||||
visit_obj_drop_post:
|
||||
(Ast.obj identified) -> Ast.block -> unit;
|
||||
visit_crate_pre: Ast.crate -> unit;
|
||||
visit_crate_post: Ast.crate -> unit;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
let empty_visitor =
|
||||
{ visit_stmt_pre = (fun _ -> ());
|
||||
visit_stmt_post = (fun _ -> ());
|
||||
visit_slot_identified_pre = (fun _ -> ());
|
||||
visit_slot_identified_post = (fun _ -> ());
|
||||
visit_expr_pre = (fun _ -> ());
|
||||
visit_expr_post = (fun _ -> ());
|
||||
visit_ty_pre = (fun _ -> ());
|
||||
visit_ty_post = (fun _ -> ());
|
||||
visit_constr_pre = (fun _ _ -> ());
|
||||
visit_constr_post = (fun _ _ -> ());
|
||||
visit_pat_pre = (fun _ -> ());
|
||||
visit_pat_post = (fun _ -> ());
|
||||
visit_block_pre = (fun _ -> ());
|
||||
visit_block_post = (fun _ -> ());
|
||||
visit_lit_pre = (fun _ -> ());
|
||||
visit_lit_post = (fun _ -> ());
|
||||
visit_lval_pre = (fun _ -> ());
|
||||
visit_lval_post = (fun _ -> ());
|
||||
visit_mod_item_pre = (fun _ _ _ -> ());
|
||||
visit_mod_item_post = (fun _ _ _ -> ());
|
||||
visit_obj_fn_pre = (fun _ _ _ -> ());
|
||||
visit_obj_fn_post = (fun _ _ _ -> ());
|
||||
visit_obj_drop_pre = (fun _ _ -> ());
|
||||
visit_obj_drop_post = (fun _ _ -> ());
|
||||
visit_crate_pre = (fun _ -> ());
|
||||
visit_crate_post = (fun _ -> ()); }
|
||||
;;
|
||||
|
||||
let path_managing_visitor
|
||||
(path:Ast.name_component Stack.t)
|
||||
(inner:visitor)
|
||||
: visitor =
|
||||
let visit_mod_item_pre ident params item =
|
||||
Stack.push (Ast.COMP_ident ident) path;
|
||||
inner.visit_mod_item_pre ident params item
|
||||
in
|
||||
let visit_mod_item_post ident params item =
|
||||
inner.visit_mod_item_post ident params item;
|
||||
ignore (Stack.pop path)
|
||||
in
|
||||
let visit_obj_fn_pre obj ident fn =
|
||||
Stack.push (Ast.COMP_ident ident) path;
|
||||
inner.visit_obj_fn_pre obj ident fn
|
||||
in
|
||||
let visit_obj_fn_post obj ident fn =
|
||||
inner.visit_obj_fn_post obj ident fn;
|
||||
ignore (Stack.pop path)
|
||||
in
|
||||
let visit_obj_drop_pre obj b =
|
||||
Stack.push (Ast.COMP_ident "drop") path;
|
||||
inner.visit_obj_drop_pre obj b
|
||||
in
|
||||
let visit_obj_drop_post obj b =
|
||||
inner.visit_obj_drop_post obj b;
|
||||
ignore (Stack.pop path)
|
||||
in
|
||||
{ inner with
|
||||
visit_mod_item_pre = visit_mod_item_pre;
|
||||
visit_mod_item_post = visit_mod_item_post;
|
||||
visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
visit_obj_fn_post = visit_obj_fn_post;
|
||||
visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
visit_obj_drop_post = visit_obj_drop_post;
|
||||
}
|
||||
;;
|
||||
|
||||
let rec name_of ncs =
|
||||
match ncs with
|
||||
[] -> bug () "Walk.name_of_ncs: empty path"
|
||||
| [(Ast.COMP_ident i)] -> Ast.NAME_base (Ast.BASE_ident i)
|
||||
| [(Ast.COMP_app x)] -> Ast.NAME_base (Ast.BASE_app x)
|
||||
| [(Ast.COMP_idx _)] ->
|
||||
bug () "Walk.name_of_ncs: path-name contains COMP_idx"
|
||||
| nc::ncs -> Ast.NAME_ext (name_of ncs, nc)
|
||||
;;
|
||||
|
||||
let path_to_name
|
||||
(path:Ast.name_component Stack.t)
|
||||
: Ast.name =
|
||||
name_of (stk_elts_from_top path)
|
||||
;;
|
||||
|
||||
|
||||
let mod_item_logging_visitor
|
||||
(logfn:string->unit)
|
||||
(path:Ast.name_component Stack.t)
|
||||
(inner:visitor)
|
||||
: visitor =
|
||||
let path_name _ = Ast.fmt_to_str Ast.fmt_name (path_to_name path) in
|
||||
let visit_mod_item_pre name params item =
|
||||
logfn (Printf.sprintf "entering %s" (path_name()));
|
||||
inner.visit_mod_item_pre name params item;
|
||||
logfn (Printf.sprintf "entered %s" (path_name()));
|
||||
in
|
||||
let visit_mod_item_post name params item =
|
||||
logfn (Printf.sprintf "leaving %s" (path_name()));
|
||||
inner.visit_mod_item_post name params item;
|
||||
logfn (Printf.sprintf "left %s" (path_name()));
|
||||
in
|
||||
let visit_obj_fn_pre obj ident fn =
|
||||
logfn (Printf.sprintf "entering %s" (path_name()));
|
||||
inner.visit_obj_fn_pre obj ident fn;
|
||||
logfn (Printf.sprintf "entered %s" (path_name()));
|
||||
in
|
||||
let visit_obj_fn_post obj ident fn =
|
||||
logfn (Printf.sprintf "leaving %s" (path_name()));
|
||||
inner.visit_obj_fn_post obj ident fn;
|
||||
logfn (Printf.sprintf "left %s" (path_name()));
|
||||
in
|
||||
let visit_obj_drop_pre obj b =
|
||||
logfn (Printf.sprintf "entering %s" (path_name()));
|
||||
inner.visit_obj_drop_pre obj b;
|
||||
logfn (Printf.sprintf "entered %s" (path_name()));
|
||||
in
|
||||
let visit_obj_drop_post obj fn =
|
||||
logfn (Printf.sprintf "leaving %s" (path_name()));
|
||||
inner.visit_obj_drop_post obj fn;
|
||||
logfn (Printf.sprintf "left %s" (path_name()));
|
||||
in
|
||||
{ inner with
|
||||
visit_mod_item_pre = visit_mod_item_pre;
|
||||
visit_mod_item_post = visit_mod_item_post;
|
||||
visit_obj_fn_pre = visit_obj_fn_pre;
|
||||
visit_obj_fn_post = visit_obj_fn_post;
|
||||
visit_obj_drop_pre = visit_obj_drop_pre;
|
||||
visit_obj_drop_post = visit_obj_drop_post;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
let walk_bracketed
|
||||
(pre:'a -> unit)
|
||||
(children:unit -> unit)
|
||||
(post:'a -> unit)
|
||||
(x:'a)
|
||||
: unit =
|
||||
begin
|
||||
pre x;
|
||||
children ();
|
||||
post x
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
let walk_option
|
||||
(walker:'a -> unit)
|
||||
(opt:'a option)
|
||||
: unit =
|
||||
match opt with
|
||||
None -> ()
|
||||
| Some v -> walker v
|
||||
;;
|
||||
|
||||
|
||||
let rec walk_crate
|
||||
(v:visitor)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
v.visit_crate_pre
|
||||
(fun _ -> walk_mod_items v (snd crate.node.Ast.crate_items))
|
||||
v.visit_crate_post
|
||||
crate
|
||||
|
||||
and walk_mod_items
|
||||
(v:visitor)
|
||||
(items:Ast.mod_items)
|
||||
: unit =
|
||||
Hashtbl.iter (walk_mod_item v) items
|
||||
|
||||
|
||||
and walk_mod_item
|
||||
(v:visitor)
|
||||
(name:Ast.ident)
|
||||
(item:Ast.mod_item)
|
||||
: unit =
|
||||
let children _ =
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_type ty -> walk_ty v ty
|
||||
| Ast.MOD_ITEM_fn f -> walk_fn v f item.id
|
||||
| Ast.MOD_ITEM_tag (htup, ttag, _) ->
|
||||
walk_header_tup v htup;
|
||||
walk_ty_tag v ttag
|
||||
| Ast.MOD_ITEM_mod (_, items) ->
|
||||
walk_mod_items v items
|
||||
| Ast.MOD_ITEM_obj ob ->
|
||||
walk_header_slots v ob.Ast.obj_state;
|
||||
walk_constrs v (Some item.id) ob.Ast.obj_constrs;
|
||||
let oid = { node = ob; id = item.id } in
|
||||
Hashtbl.iter (walk_obj_fn v oid) ob.Ast.obj_fns;
|
||||
match ob.Ast.obj_drop with
|
||||
None -> ()
|
||||
| Some d ->
|
||||
v.visit_obj_drop_pre oid d;
|
||||
walk_block v d;
|
||||
v.visit_obj_drop_post oid d
|
||||
|
||||
in
|
||||
walk_bracketed
|
||||
(v.visit_mod_item_pre name item.node.Ast.decl_params)
|
||||
children
|
||||
(v.visit_mod_item_post name item.node.Ast.decl_params)
|
||||
item
|
||||
|
||||
|
||||
and walk_ty_tup v ttup = Array.iter (walk_slot v) ttup
|
||||
|
||||
and walk_ty_tag v ttag = Hashtbl.iter (fun _ t -> walk_ty_tup v t) ttag
|
||||
|
||||
and walk_ty
|
||||
(v:visitor)
|
||||
(ty:Ast.ty)
|
||||
: unit =
|
||||
let children _ =
|
||||
match ty with
|
||||
Ast.TY_tup ttup -> walk_ty_tup v ttup
|
||||
| Ast.TY_vec s -> walk_slot v s
|
||||
| Ast.TY_rec trec -> Array.iter (fun (_, s) -> walk_slot v s) trec
|
||||
| Ast.TY_tag ttag -> walk_ty_tag v ttag
|
||||
| Ast.TY_iso tiso -> Array.iter (walk_ty_tag v) tiso.Ast.iso_group
|
||||
| Ast.TY_fn tfn -> walk_ty_fn v tfn
|
||||
| Ast.TY_obj (_, fns) ->
|
||||
Hashtbl.iter (fun _ tfn -> walk_ty_fn v tfn) fns
|
||||
| Ast.TY_chan t -> walk_ty v t
|
||||
| Ast.TY_port t -> walk_ty v t
|
||||
| Ast.TY_constrained (t,cs) ->
|
||||
begin
|
||||
walk_ty v t;
|
||||
walk_constrs v None cs
|
||||
end
|
||||
| Ast.TY_named _ -> ()
|
||||
| Ast.TY_param _ -> ()
|
||||
| Ast.TY_native _ -> ()
|
||||
| Ast.TY_idx _ -> ()
|
||||
| Ast.TY_mach _ -> ()
|
||||
| Ast.TY_type -> ()
|
||||
| Ast.TY_str -> ()
|
||||
| Ast.TY_char -> ()
|
||||
| Ast.TY_int -> ()
|
||||
| Ast.TY_uint -> ()
|
||||
| Ast.TY_bool -> ()
|
||||
| Ast.TY_nil -> ()
|
||||
| Ast.TY_task -> ()
|
||||
| Ast.TY_any -> ()
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_ty_pre
|
||||
children
|
||||
v.visit_ty_post
|
||||
ty
|
||||
|
||||
|
||||
and walk_ty_sig
|
||||
(v:visitor)
|
||||
(s:Ast.ty_sig)
|
||||
: unit =
|
||||
begin
|
||||
Array.iter (walk_slot v) s.Ast.sig_input_slots;
|
||||
walk_constrs v None s.Ast.sig_input_constrs;
|
||||
walk_slot v s.Ast.sig_output_slot;
|
||||
end
|
||||
|
||||
|
||||
and walk_ty_fn
|
||||
(v:visitor)
|
||||
(tfn:Ast.ty_fn)
|
||||
: unit =
|
||||
let (tsig, _) = tfn in
|
||||
walk_ty_sig v tsig
|
||||
|
||||
|
||||
and walk_constrs
|
||||
(v:visitor)
|
||||
(formal_base:node_id option)
|
||||
(cs:Ast.constrs)
|
||||
: unit =
|
||||
Array.iter (walk_constr v formal_base) cs
|
||||
|
||||
and walk_check_calls
|
||||
(v:visitor)
|
||||
(calls:Ast.check_calls)
|
||||
: unit =
|
||||
Array.iter
|
||||
begin
|
||||
fun (f, args) ->
|
||||
walk_lval v f;
|
||||
Array.iter (walk_atom v) args
|
||||
end
|
||||
calls
|
||||
|
||||
|
||||
and walk_constr
|
||||
(v:visitor)
|
||||
(formal_base:node_id option)
|
||||
(c:Ast.constr)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
(v.visit_constr_pre formal_base)
|
||||
(fun _ -> ())
|
||||
(v.visit_constr_post formal_base)
|
||||
c
|
||||
|
||||
and walk_header_slots
|
||||
(v:visitor)
|
||||
(hslots:Ast.header_slots)
|
||||
: unit =
|
||||
Array.iter (fun (s,_) -> walk_slot_identified v s) hslots
|
||||
|
||||
and walk_header_tup
|
||||
(v:visitor)
|
||||
(htup:Ast.header_tup)
|
||||
: unit =
|
||||
Array.iter (walk_slot_identified v) htup
|
||||
|
||||
and walk_obj_fn
|
||||
(v:visitor)
|
||||
(obj:Ast.obj identified)
|
||||
(ident:Ast.ident)
|
||||
(f:Ast.fn identified)
|
||||
: unit =
|
||||
v.visit_obj_fn_pre obj ident f;
|
||||
walk_fn v f.node f.id;
|
||||
v.visit_obj_fn_post obj ident f
|
||||
|
||||
and walk_fn
|
||||
(v:visitor)
|
||||
(f:Ast.fn)
|
||||
(id:node_id)
|
||||
: unit =
|
||||
walk_header_slots v f.Ast.fn_input_slots;
|
||||
walk_constrs v (Some id) f.Ast.fn_input_constrs;
|
||||
walk_slot_identified v f.Ast.fn_output_slot;
|
||||
walk_block v f.Ast.fn_body
|
||||
|
||||
and walk_slot_identified
|
||||
(v:visitor)
|
||||
(s:Ast.slot identified)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
v.visit_slot_identified_pre
|
||||
(fun _ -> walk_slot v s.node)
|
||||
v.visit_slot_identified_post
|
||||
s
|
||||
|
||||
|
||||
and walk_slot
|
||||
(v:visitor)
|
||||
(s:Ast.slot)
|
||||
: unit =
|
||||
walk_option (walk_ty v) s.Ast.slot_ty
|
||||
|
||||
|
||||
and walk_stmt
|
||||
(v:visitor)
|
||||
(s:Ast.stmt)
|
||||
: unit =
|
||||
let walk_stmt_for
|
||||
(s:Ast.stmt_for)
|
||||
: unit =
|
||||
let (si,_) = s.Ast.for_slot in
|
||||
let (ss,lv) = s.Ast.for_seq in
|
||||
walk_slot_identified v si;
|
||||
Array.iter (walk_stmt v) ss;
|
||||
walk_lval v lv;
|
||||
walk_block v s.Ast.for_body
|
||||
in
|
||||
let walk_stmt_for_each
|
||||
(s:Ast.stmt_for_each)
|
||||
: unit =
|
||||
let (si,_) = s.Ast.for_each_slot in
|
||||
let (f,az) = s.Ast.for_each_call in
|
||||
walk_slot_identified v si;
|
||||
walk_lval v f;
|
||||
Array.iter (walk_atom v) az;
|
||||
walk_block v s.Ast.for_each_head
|
||||
in
|
||||
let walk_stmt_while
|
||||
(s:Ast.stmt_while)
|
||||
: unit =
|
||||
let (ss,e) = s.Ast.while_lval in
|
||||
Array.iter (walk_stmt v) ss;
|
||||
walk_expr v e;
|
||||
walk_block v s.Ast.while_body
|
||||
in
|
||||
let children _ =
|
||||
match s.node with
|
||||
Ast.STMT_log a ->
|
||||
walk_atom v a
|
||||
|
||||
| Ast.STMT_init_rec (lv, atab, base) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (fun (_, _, _, a) -> walk_atom v a) atab;
|
||||
walk_option (walk_lval v) base;
|
||||
|
||||
| Ast.STMT_init_vec (lv, _, atoms) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (walk_atom v) atoms
|
||||
|
||||
| Ast.STMT_init_tup (lv, mut_atoms) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (fun (_, _, a) -> walk_atom v a) mut_atoms
|
||||
|
||||
| Ast.STMT_init_str (lv, _) ->
|
||||
walk_lval v lv
|
||||
|
||||
| Ast.STMT_init_port lv ->
|
||||
walk_lval v lv
|
||||
|
||||
| Ast.STMT_init_chan (chan,port) ->
|
||||
walk_option (walk_lval v) port;
|
||||
walk_lval v chan;
|
||||
|
||||
| Ast.STMT_for f ->
|
||||
walk_stmt_for f
|
||||
|
||||
| Ast.STMT_for_each f ->
|
||||
walk_stmt_for_each f
|
||||
|
||||
| Ast.STMT_while w ->
|
||||
walk_stmt_while w
|
||||
|
||||
| Ast.STMT_do_while w ->
|
||||
walk_stmt_while w
|
||||
|
||||
| Ast.STMT_if i ->
|
||||
begin
|
||||
walk_expr v i.Ast.if_test;
|
||||
walk_block v i.Ast.if_then;
|
||||
walk_option (walk_block v) i.Ast.if_else
|
||||
end
|
||||
|
||||
| Ast.STMT_block b ->
|
||||
walk_block v b
|
||||
|
||||
| Ast.STMT_copy (lv,e) ->
|
||||
walk_lval v lv;
|
||||
walk_expr v e
|
||||
|
||||
| Ast.STMT_copy_binop (lv,_,a) ->
|
||||
walk_lval v lv;
|
||||
walk_atom v a
|
||||
|
||||
| Ast.STMT_call (dst,f,az) ->
|
||||
walk_lval v dst;
|
||||
walk_lval v f;
|
||||
Array.iter (walk_atom v) az
|
||||
|
||||
| Ast.STMT_bind (dst, f, az) ->
|
||||
walk_lval v dst;
|
||||
walk_lval v f;
|
||||
Array.iter (walk_opt_atom v) az
|
||||
|
||||
| Ast.STMT_spawn (dst,_,p,az) ->
|
||||
walk_lval v dst;
|
||||
walk_lval v p;
|
||||
Array.iter (walk_atom v) az
|
||||
|
||||
| Ast.STMT_ret ao ->
|
||||
walk_option (walk_atom v) ao
|
||||
|
||||
| Ast.STMT_put at ->
|
||||
walk_option (walk_atom v) at
|
||||
|
||||
| Ast.STMT_put_each (lv, ats) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (walk_atom v) ats
|
||||
|
||||
(* FIXME: this should have a param array, and invoke the visitors. *)
|
||||
| Ast.STMT_decl (Ast.DECL_mod_item (id, mi)) ->
|
||||
walk_mod_item v id mi
|
||||
|
||||
| Ast.STMT_decl (Ast.DECL_slot (_, slot)) ->
|
||||
walk_slot_identified v slot
|
||||
|
||||
| Ast.STMT_yield
|
||||
| Ast.STMT_fail ->
|
||||
()
|
||||
|
||||
| Ast.STMT_join task ->
|
||||
walk_lval v task
|
||||
|
||||
| Ast.STMT_send (dst,src) ->
|
||||
walk_lval v dst;
|
||||
walk_lval v src
|
||||
|
||||
| Ast.STMT_recv (dst,src) ->
|
||||
walk_lval v dst;
|
||||
walk_lval v src
|
||||
|
||||
| Ast.STMT_be (lv, ats) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (walk_atom v) ats
|
||||
|
||||
| Ast.STMT_check_expr e ->
|
||||
walk_expr v e
|
||||
|
||||
| Ast.STMT_check (cs, calls) ->
|
||||
walk_constrs v None cs;
|
||||
walk_check_calls v calls
|
||||
|
||||
| Ast.STMT_check_if (cs,calls,b) ->
|
||||
walk_constrs v None cs;
|
||||
walk_check_calls v calls;
|
||||
walk_block v b
|
||||
|
||||
| Ast.STMT_prove cs ->
|
||||
walk_constrs v None cs
|
||||
|
||||
| Ast.STMT_alt_tag
|
||||
{ Ast.alt_tag_lval = lval; Ast.alt_tag_arms = arms } ->
|
||||
walk_lval v lval;
|
||||
let walk_arm { node = (pat, block) } =
|
||||
walk_pat v pat;
|
||||
walk_block v block
|
||||
in
|
||||
Array.iter walk_arm arms
|
||||
|
||||
(* FIXME (issue #20): finish this as needed. *)
|
||||
| Ast.STMT_slice _
|
||||
| Ast.STMT_note _
|
||||
| Ast.STMT_alt_type _
|
||||
| Ast.STMT_alt_port _ ->
|
||||
bug () "unimplemented statement type in Walk.walk_stmt"
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_stmt_pre
|
||||
children
|
||||
v.visit_stmt_post
|
||||
s
|
||||
|
||||
|
||||
and walk_expr
|
||||
(v:visitor)
|
||||
(e:Ast.expr)
|
||||
: unit =
|
||||
let children _ =
|
||||
match e with
|
||||
Ast.EXPR_binary (_,aa,ab) ->
|
||||
walk_atom v aa;
|
||||
walk_atom v ab
|
||||
| Ast.EXPR_unary (_,a) ->
|
||||
walk_atom v a
|
||||
| Ast.EXPR_atom a ->
|
||||
walk_atom v a
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_expr_pre
|
||||
children
|
||||
v.visit_expr_post
|
||||
e
|
||||
|
||||
and walk_atom
|
||||
(v:visitor)
|
||||
(a:Ast.atom)
|
||||
: unit =
|
||||
match a with
|
||||
Ast.ATOM_literal ls -> walk_lit v ls.node
|
||||
| Ast.ATOM_lval lv -> walk_lval v lv
|
||||
|
||||
|
||||
and walk_opt_atom
|
||||
(v:visitor)
|
||||
(ao:Ast.atom option)
|
||||
: unit =
|
||||
match ao with
|
||||
None -> ()
|
||||
| Some a -> walk_atom v a
|
||||
|
||||
|
||||
and walk_lit
|
||||
(v:visitor)
|
||||
(li:Ast.lit)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
v.visit_lit_pre
|
||||
(fun _ -> ())
|
||||
v.visit_lit_post
|
||||
li
|
||||
|
||||
|
||||
and walk_lval
|
||||
(v:visitor)
|
||||
(lv:Ast.lval)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
v.visit_lval_pre
|
||||
(fun _ -> ())
|
||||
v.visit_lval_post
|
||||
lv
|
||||
|
||||
|
||||
and walk_pat
|
||||
(v:visitor)
|
||||
(p:Ast.pat)
|
||||
: unit =
|
||||
let rec walk p =
|
||||
match p with
|
||||
Ast.PAT_lit lit -> walk_lit v lit
|
||||
| Ast.PAT_tag (_, pats) -> Array.iter walk pats
|
||||
| Ast.PAT_slot (si, _) -> walk_slot_identified v si
|
||||
| Ast.PAT_wild -> ()
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_pat_pre
|
||||
(fun _ -> walk p)
|
||||
v.visit_pat_post
|
||||
p
|
||||
|
||||
|
||||
and walk_block
|
||||
(v:visitor)
|
||||
(b:Ast.block)
|
||||
: unit =
|
||||
walk_bracketed
|
||||
v.visit_block_pre
|
||||
(fun _ -> (Array.iter (walk_stmt v) b.node))
|
||||
v.visit_block_post
|
||||
b
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
107
src/boot/util/bits.ml
Normal file
107
src/boot/util/bits.ml
Normal file
@ -0,0 +1,107 @@
|
||||
type t = {
|
||||
storage: int array;
|
||||
nbits: int;
|
||||
}
|
||||
;;
|
||||
|
||||
let int_bits =
|
||||
if max_int = (1 lsl 30) - 1
|
||||
then 31
|
||||
else 63
|
||||
;;
|
||||
|
||||
let create nbits flag =
|
||||
{ storage = Array.make (nbits / int_bits + 1) (if flag then lnot 0 else 0);
|
||||
nbits = nbits }
|
||||
;;
|
||||
|
||||
(*
|
||||
* mutate v0 in place: v0.(i) <- v0.(i) op v1.(i), returning bool indicating
|
||||
* whether any bits in v0 changed in the process.
|
||||
*)
|
||||
let process (op:int -> int -> int) (v0:t) (v1:t) : bool =
|
||||
let changed = ref false in
|
||||
assert (v0.nbits = v1.nbits);
|
||||
assert ((Array.length v0.storage) = (Array.length v1.storage));
|
||||
Array.iteri
|
||||
begin
|
||||
fun i w1 ->
|
||||
let w0 = v0.storage.(i) in
|
||||
let w0' = op w0 w1 in
|
||||
if not (w0' = w0)
|
||||
then changed := true;
|
||||
v0.storage.(i) <- w0';
|
||||
end
|
||||
v1.storage;
|
||||
!changed
|
||||
;;
|
||||
|
||||
let union = process (lor) ;;
|
||||
let intersect = process (land) ;;
|
||||
let copy = process (fun _ w1 -> w1) ;;
|
||||
|
||||
let get (v:t) (i:int) : bool =
|
||||
assert (i >= 0);
|
||||
assert (i < v.nbits);
|
||||
let w = i / int_bits in
|
||||
let b = i mod int_bits in
|
||||
let x = 1 land (v.storage.(w) lsr b) in
|
||||
x = 1
|
||||
;;
|
||||
|
||||
let equal (v1:t) (v0:t) : bool =
|
||||
v0 = v1
|
||||
;;
|
||||
|
||||
let clear (v:t) : unit =
|
||||
for i = 0 to (Array.length v.storage) - 1
|
||||
do
|
||||
v.storage.(i) <- 0
|
||||
done
|
||||
;;
|
||||
|
||||
let invert (v:t) : unit =
|
||||
for i = 0 to (Array.length v.storage) - 1
|
||||
do
|
||||
v.storage.(i) <- lnot v.storage.(i)
|
||||
done
|
||||
;;
|
||||
|
||||
let set (v:t) (i:int) (x:bool) : unit =
|
||||
assert (i >= 0);
|
||||
assert (i < v.nbits);
|
||||
let w = i / int_bits in
|
||||
let b = i mod int_bits in
|
||||
let w0 = v.storage.(w) in
|
||||
let flag = 1 lsl b in
|
||||
v.storage.(w) <-
|
||||
if x
|
||||
then w0 lor flag
|
||||
else w0 land (lnot flag)
|
||||
;;
|
||||
|
||||
let to_list (v:t) : int list =
|
||||
if v.nbits = 0
|
||||
then []
|
||||
else
|
||||
let accum = ref [] in
|
||||
let word = ref v.storage.(0) in
|
||||
for i = 0 to (v.nbits-1) do
|
||||
if i mod int_bits = 0
|
||||
then word := v.storage.(i / int_bits);
|
||||
if (1 land (!word)) = 1
|
||||
then accum := i :: (!accum);
|
||||
word := (!word) lsr 1;
|
||||
done;
|
||||
!accum
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
709
src/boot/util/common.ml
Normal file
709
src/boot/util/common.ml
Normal file
@ -0,0 +1,709 @@
|
||||
(*
|
||||
* This module goes near the *bottom* of the dependency DAG, and holds basic
|
||||
* types shared across all phases of the compiler.
|
||||
*)
|
||||
|
||||
type filename = string
|
||||
type pos = (filename * int * int)
|
||||
type span = {lo: pos; hi: pos}
|
||||
|
||||
type node_id = Node of int
|
||||
type temp_id = Temp of int
|
||||
type opaque_id = Opaque of int
|
||||
type constr_id = Constr of int
|
||||
|
||||
let int_of_node (Node i) = i
|
||||
let int_of_temp (Temp i) = i
|
||||
let int_of_opaque (Opaque i) = i
|
||||
let int_of_constr (Constr i) = i
|
||||
|
||||
type 'a identified = { node: 'a; id: node_id }
|
||||
;;
|
||||
|
||||
let bug _ =
|
||||
let k s = failwith s
|
||||
in Printf.ksprintf k
|
||||
;;
|
||||
|
||||
exception Semant_err of ((node_id option) * string)
|
||||
;;
|
||||
|
||||
let err (idopt:node_id option) =
|
||||
let k s =
|
||||
raise (Semant_err (idopt, s))
|
||||
in
|
||||
Printf.ksprintf k
|
||||
;;
|
||||
|
||||
(* Some ubiquitous low-level types. *)
|
||||
|
||||
type target =
|
||||
Linux_x86_elf
|
||||
| Win32_x86_pe
|
||||
| MacOS_x86_macho
|
||||
;;
|
||||
|
||||
type ty_mach =
|
||||
TY_u8
|
||||
| TY_u16
|
||||
| TY_u32
|
||||
| TY_u64
|
||||
| TY_i8
|
||||
| TY_i16
|
||||
| TY_i32
|
||||
| TY_i64
|
||||
| TY_f32
|
||||
| TY_f64
|
||||
;;
|
||||
|
||||
let mach_is_integral (mach:ty_mach) : bool =
|
||||
match mach with
|
||||
TY_i8 | TY_i16 | TY_i32 | TY_i64
|
||||
| TY_u8 | TY_u16 | TY_u32 | TY_u64 -> true
|
||||
| TY_f32 | TY_f64 -> false
|
||||
;;
|
||||
|
||||
|
||||
let mach_is_signed (mach:ty_mach) : bool =
|
||||
match mach with
|
||||
TY_i8 | TY_i16 | TY_i32 | TY_i64 -> true
|
||||
| TY_u8 | TY_u16 | TY_u32 | TY_u64
|
||||
| TY_f32 | TY_f64 -> false
|
||||
;;
|
||||
|
||||
let string_of_ty_mach (mach:ty_mach) : string =
|
||||
match mach with
|
||||
TY_u8 -> "u8"
|
||||
| TY_u16 -> "u16"
|
||||
| TY_u32 -> "u32"
|
||||
| TY_u64 -> "u64"
|
||||
| TY_i8 -> "i8"
|
||||
| TY_i16 -> "i16"
|
||||
| TY_i32 -> "i32"
|
||||
| TY_i64 -> "i64"
|
||||
| TY_f32 -> "f32"
|
||||
| TY_f64 -> "f64"
|
||||
;;
|
||||
|
||||
let bytes_of_ty_mach (mach:ty_mach) : int =
|
||||
match mach with
|
||||
TY_u8 -> 1
|
||||
| TY_u16 -> 2
|
||||
| TY_u32 -> 4
|
||||
| TY_u64 -> 8
|
||||
| TY_i8 -> 1
|
||||
| TY_i16 -> 2
|
||||
| TY_i32 -> 4
|
||||
| TY_i64 -> 8
|
||||
| TY_f32 -> 4
|
||||
| TY_f64 -> 8
|
||||
;;
|
||||
|
||||
type ty_param_idx = int
|
||||
;;
|
||||
|
||||
type nabi_conv =
|
||||
CONV_rust
|
||||
| CONV_cdecl
|
||||
;;
|
||||
|
||||
type nabi = { nabi_indirect: bool;
|
||||
nabi_convention: nabi_conv }
|
||||
;;
|
||||
|
||||
let string_to_conv (a:string) : nabi_conv option =
|
||||
match a with
|
||||
"cdecl" -> Some CONV_cdecl
|
||||
| "rust" -> Some CONV_rust
|
||||
| _ -> None
|
||||
|
||||
(* FIXME: remove this when native items go away. *)
|
||||
let string_to_nabi (s:string) (indirect:bool) : nabi option =
|
||||
match string_to_conv s with
|
||||
None -> None
|
||||
| Some c ->
|
||||
Some { nabi_indirect = indirect;
|
||||
nabi_convention = c }
|
||||
;;
|
||||
|
||||
type required_lib_spec =
|
||||
{
|
||||
required_libname: string;
|
||||
required_prefix: int;
|
||||
}
|
||||
;;
|
||||
|
||||
type required_lib =
|
||||
REQUIRED_LIB_rustrt
|
||||
| REQUIRED_LIB_crt
|
||||
| REQUIRED_LIB_rust of required_lib_spec
|
||||
| REQUIRED_LIB_c of required_lib_spec
|
||||
;;
|
||||
|
||||
type segment =
|
||||
SEG_text
|
||||
| SEG_data
|
||||
;;
|
||||
|
||||
type fixup =
|
||||
{ fixup_name: string;
|
||||
mutable fixup_file_pos: int option;
|
||||
mutable fixup_file_sz: int option;
|
||||
mutable fixup_mem_pos: int64 option;
|
||||
mutable fixup_mem_sz: int64 option }
|
||||
;;
|
||||
|
||||
|
||||
let new_fixup (s:string)
|
||||
: fixup =
|
||||
{ fixup_name = s;
|
||||
fixup_file_pos = None;
|
||||
fixup_file_sz = None;
|
||||
fixup_mem_pos = None;
|
||||
fixup_mem_sz = None }
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary hashtable functions.
|
||||
*)
|
||||
|
||||
let htab_keys (htab:('a,'b) Hashtbl.t) : ('a list) =
|
||||
Hashtbl.fold (fun k _ accum -> k :: accum) htab []
|
||||
;;
|
||||
|
||||
let sorted_htab_keys (tab:('a, 'b) Hashtbl.t) : 'a array =
|
||||
let keys = Array.of_list (htab_keys tab) in
|
||||
Array.sort compare keys;
|
||||
keys
|
||||
;;
|
||||
|
||||
let htab_vals (htab:('a,'b) Hashtbl.t) : ('b list) =
|
||||
Hashtbl.fold (fun _ v accum -> v :: accum) htab []
|
||||
;;
|
||||
|
||||
let htab_pairs (htab:('a,'b) Hashtbl.t) : (('a * 'b) list) =
|
||||
Hashtbl.fold (fun k v accum -> (k,v) :: accum) htab []
|
||||
;;
|
||||
|
||||
let htab_search (htab:('a,'b) Hashtbl.t) (k:'a) : ('b option) =
|
||||
if Hashtbl.mem htab k
|
||||
then Some (Hashtbl.find htab k)
|
||||
else None
|
||||
;;
|
||||
|
||||
let htab_search_or_default
|
||||
(htab:('a,'b) Hashtbl.t)
|
||||
(k:'a)
|
||||
(def:unit -> 'b)
|
||||
: 'b =
|
||||
match htab_search htab k with
|
||||
Some v -> v
|
||||
| None -> def()
|
||||
;;
|
||||
|
||||
let htab_search_or_add
|
||||
(htab:('a,'b) Hashtbl.t)
|
||||
(k:'a)
|
||||
(mk:unit -> 'b)
|
||||
: 'b =
|
||||
let def () =
|
||||
let v = mk() in
|
||||
Hashtbl.add htab k v;
|
||||
v
|
||||
in
|
||||
htab_search_or_default htab k def
|
||||
;;
|
||||
|
||||
let htab_put (htab:('a,'b) Hashtbl.t) (a:'a) (b:'b) : unit =
|
||||
assert (not (Hashtbl.mem htab a));
|
||||
Hashtbl.add htab a b
|
||||
;;
|
||||
|
||||
let htab_map
|
||||
(htab:('a,'b) Hashtbl.t)
|
||||
(f:'a -> 'b -> ('c * 'd))
|
||||
: (('c,'d) Hashtbl.t) =
|
||||
let ntab = Hashtbl.create (Hashtbl.length htab) in
|
||||
let g a b =
|
||||
let (c,d) = f a b in
|
||||
htab_put ntab c d
|
||||
in
|
||||
Hashtbl.iter g htab;
|
||||
ntab
|
||||
;;
|
||||
|
||||
|
||||
let htab_fold
|
||||
(fn:'a -> 'b -> 'c -> 'c)
|
||||
(init:'c)
|
||||
(h:('a, 'b) Hashtbl.t) : 'c =
|
||||
let accum = ref init in
|
||||
let f a b = accum := (fn a b (!accum)) in
|
||||
Hashtbl.iter f h;
|
||||
!accum
|
||||
;;
|
||||
|
||||
|
||||
let reduce_hash_to_list
|
||||
(fn:'a -> 'b -> 'c)
|
||||
(h:('a, 'b) Hashtbl.t)
|
||||
: ('c list) =
|
||||
htab_fold (fun a b ls -> (fn a b) :: ls) [] h
|
||||
;;
|
||||
|
||||
(*
|
||||
* Auxiliary association-array and association-list operations.
|
||||
*)
|
||||
let atab_search (atab:('a * 'b) array) (a:'a) : ('b option) =
|
||||
let lim = Array.length atab in
|
||||
let rec step i =
|
||||
if i = lim
|
||||
then None
|
||||
else
|
||||
let (k,v) = atab.(i) in
|
||||
if k = a
|
||||
then Some v
|
||||
else step (i+1)
|
||||
in
|
||||
step 0
|
||||
|
||||
let atab_find (atab:('a * 'b) array) (a:'a) : 'b =
|
||||
match atab_search atab a with
|
||||
None -> bug () "atab_find: element not found"
|
||||
| Some b -> b
|
||||
|
||||
let atab_mem (atab:('a * 'b) array) (a:'a) : bool =
|
||||
match atab_search atab a with
|
||||
None -> false
|
||||
| Some _ -> true
|
||||
|
||||
let rec ltab_search (ltab:('a * 'b) list) (a:'a) : ('b option) =
|
||||
match ltab with
|
||||
[] -> None
|
||||
| (k,v)::_ when k = a -> Some v
|
||||
| _::lz -> ltab_search lz a
|
||||
|
||||
let ltab_put (ltab:('a * 'b) list) (a:'a) (b:'b) : (('a * 'b) list) =
|
||||
assert ((ltab_search ltab a) = None);
|
||||
(a,b)::ltab
|
||||
|
||||
(*
|
||||
* Auxiliary list functions.
|
||||
*)
|
||||
|
||||
let rec list_search (list:'a list) (f:'a -> 'b option) : ('b option) =
|
||||
match list with
|
||||
[] -> None
|
||||
| a::az ->
|
||||
match f a with
|
||||
Some b -> Some b
|
||||
| None -> list_search az f
|
||||
|
||||
let rec list_search_ctxt
|
||||
(list:'a list)
|
||||
(f:'a -> 'b option)
|
||||
: ((('a list) * 'b) option) =
|
||||
match list with
|
||||
[] -> None
|
||||
| a::az ->
|
||||
match f a with
|
||||
Some b -> Some (list, b)
|
||||
| None -> list_search_ctxt az f
|
||||
|
||||
let rec list_drop n ls =
|
||||
if n = 0
|
||||
then ls
|
||||
else list_drop (n-1) (List.tl ls)
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary option functions.
|
||||
*)
|
||||
|
||||
let bool_of_option x =
|
||||
match x with
|
||||
Some _ -> true
|
||||
| None -> false
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary stack functions.
|
||||
*)
|
||||
|
||||
let stk_fold (s:'a Stack.t) (f:'a -> 'b -> 'b) (x:'b) : 'b =
|
||||
let r = ref x in
|
||||
Stack.iter (fun e -> r := f e (!r)) s;
|
||||
!r
|
||||
|
||||
let stk_elts_from_bot (s:'a Stack.t) : ('a list) =
|
||||
stk_fold s (fun x y -> x::y) []
|
||||
|
||||
let stk_elts_from_top (s:'a Stack.t) : ('a list) =
|
||||
List.rev (stk_elts_from_bot s)
|
||||
|
||||
let stk_search (s:'a Stack.t) (f:'a -> 'b option) : 'b option =
|
||||
stk_fold s (fun e accum -> match accum with None -> (f e) | x -> x) None
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary array functions.
|
||||
*)
|
||||
|
||||
let arr_search (a:'a array) (f:int -> 'a -> 'b option) : 'b option =
|
||||
let max = Array.length a in
|
||||
let rec iter i =
|
||||
if i < max
|
||||
then
|
||||
let v = a.(i) in
|
||||
let r = f i v in
|
||||
match r with
|
||||
Some _ -> r
|
||||
| None -> iter (i+1)
|
||||
else
|
||||
None
|
||||
in
|
||||
iter 0
|
||||
;;
|
||||
|
||||
let arr_idx (arr:'a array) (a:'a) : int =
|
||||
let find i v = if v = a then Some i else None in
|
||||
match arr_search arr find with
|
||||
None -> bug () "arr_idx: element not found"
|
||||
| Some i -> i
|
||||
;;
|
||||
|
||||
let arr_map_partial (a:'a array) (f:'a -> 'b option) : 'b array =
|
||||
let accum a ls =
|
||||
match f a with
|
||||
None -> ls
|
||||
| Some b -> b :: ls
|
||||
in
|
||||
Array.of_list (Array.fold_right accum a [])
|
||||
;;
|
||||
|
||||
let arr_filter_some (a:'a option array) : 'a array =
|
||||
arr_map_partial a (fun x -> x)
|
||||
;;
|
||||
|
||||
let arr_find_dups (a:'a array) : ('a * 'a) option =
|
||||
let copy = Array.copy a in
|
||||
Array.sort compare copy;
|
||||
let lasti = (Array.length copy) - 1 in
|
||||
let rec find_dups i =
|
||||
if i < lasti then
|
||||
let this = copy.(i) in
|
||||
let next = copy.(i+1) in
|
||||
(if (this = next) then
|
||||
Some (this, next)
|
||||
else
|
||||
find_dups (i+1))
|
||||
else
|
||||
None
|
||||
in
|
||||
find_dups 0
|
||||
;;
|
||||
|
||||
let arr_check_dups (a:'a array) (f:'a -> 'a -> unit) : unit =
|
||||
match arr_find_dups a with
|
||||
Some (x, y) -> f x y
|
||||
| None -> ()
|
||||
;;
|
||||
|
||||
let arr_map2 (f:'a -> 'b -> 'c) (a:'a array) (b:'b array) : 'c array =
|
||||
assert ((Array.length a) = (Array.length b));
|
||||
Array.init (Array.length a) (fun i -> f a.(i) b.(i))
|
||||
;;
|
||||
|
||||
let arr_for_all (f:int -> 'a -> bool) (a:'a array) : bool =
|
||||
let len = Array.length a in
|
||||
let rec loop i =
|
||||
(i >= len) || ((f i a.(i)) && (loop (i+1)))
|
||||
in
|
||||
loop 0
|
||||
;;
|
||||
|
||||
let arr_exists (f:int -> 'a -> bool) (a:'a array) : bool =
|
||||
let len = Array.length a in
|
||||
let rec loop i =
|
||||
(i < len) && ((f i a.(i)) || (loop (i+1)))
|
||||
in
|
||||
loop 0
|
||||
;;
|
||||
|
||||
(*
|
||||
* Auxiliary queue functions.
|
||||
*)
|
||||
|
||||
let queue_to_list (q:'a Queue.t) : 'a list =
|
||||
List.rev (Queue.fold (fun ls elt -> elt :: ls) [] q)
|
||||
;;
|
||||
|
||||
let queue_to_arr (q:'a Queue.t) : 'a array =
|
||||
Array.init (Queue.length q) (fun _ -> Queue.take q)
|
||||
;;
|
||||
|
||||
(*
|
||||
* Auxiliary int64 functions
|
||||
*)
|
||||
|
||||
let i64_lt (a:int64) (b:int64) : bool = (Int64.compare a b) < 0
|
||||
let i64_le (a:int64) (b:int64) : bool = (Int64.compare a b) <= 0
|
||||
let i64_ge (a:int64) (b:int64) : bool = (Int64.compare a b) >= 0
|
||||
let i64_gt (a:int64) (b:int64) : bool = (Int64.compare a b) > 0
|
||||
let i64_max (a:int64) (b:int64) : int64 =
|
||||
(if (Int64.compare a b) > 0 then a else b)
|
||||
let i64_min (a:int64) (b:int64) : int64 =
|
||||
(if (Int64.compare a b) < 0 then a else b)
|
||||
let i64_align (align:int64) (v:int64) : int64 =
|
||||
(assert (align <> 0L));
|
||||
let mask = Int64.sub align 1L in
|
||||
Int64.logand (Int64.lognot mask) (Int64.add v mask)
|
||||
;;
|
||||
|
||||
let rec i64_for (lo:int64) (hi:int64) (thunk:int64 -> unit) : unit =
|
||||
if i64_lt lo hi then
|
||||
begin
|
||||
thunk lo;
|
||||
i64_for (Int64.add lo 1L) hi thunk;
|
||||
end
|
||||
;;
|
||||
|
||||
let rec i64_for_rev (hi:int64) (lo:int64) (thunk:int64 -> unit) : unit =
|
||||
if i64_ge hi lo then
|
||||
begin
|
||||
thunk hi;
|
||||
i64_for_rev (Int64.sub hi 1L) lo thunk;
|
||||
end
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary int32 functions
|
||||
*)
|
||||
|
||||
let i32_lt (a:int32) (b:int32) : bool = (Int32.compare a b) < 0
|
||||
let i32_le (a:int32) (b:int32) : bool = (Int32.compare a b) <= 0
|
||||
let i32_ge (a:int32) (b:int32) : bool = (Int32.compare a b) >= 0
|
||||
let i32_gt (a:int32) (b:int32) : bool = (Int32.compare a b) > 0
|
||||
let i32_max (a:int32) (b:int32) : int32 =
|
||||
(if (Int32.compare a b) > 0 then a else b)
|
||||
let i32_min (a:int32) (b:int32) : int32 =
|
||||
(if (Int32.compare a b) < 0 then a else b)
|
||||
let i32_align (align:int32) (v:int32) : int32 =
|
||||
(assert (align <> 0l));
|
||||
let mask = Int32.sub align 1l in
|
||||
Int32.logand (Int32.lognot mask) (Int32.add v mask)
|
||||
;;
|
||||
|
||||
(*
|
||||
* Int-as-unichar functions.
|
||||
*)
|
||||
|
||||
let bounds lo c hi = (lo <= c) && (c <= hi)
|
||||
;;
|
||||
|
||||
let escaped_char i =
|
||||
if bounds 0 i 0x7f
|
||||
then Char.escaped (Char.chr i)
|
||||
else
|
||||
if bounds 0 i 0xffff
|
||||
then Printf.sprintf "\\u%4.4X" i
|
||||
else Printf.sprintf "\\U%8.8X" i
|
||||
;;
|
||||
|
||||
let char_as_utf8 i =
|
||||
let buf = Buffer.create 8 in
|
||||
let addb i =
|
||||
Buffer.add_char buf (Char.chr (i land 0xff))
|
||||
in
|
||||
let fini _ =
|
||||
Buffer.contents buf
|
||||
in
|
||||
let rec add_trailing_bytes n i =
|
||||
if n = 0
|
||||
then fini()
|
||||
else
|
||||
begin
|
||||
addb (0b1000_0000 lor ((i lsr ((n-1) * 6)) land 0b11_1111));
|
||||
add_trailing_bytes (n-1) i
|
||||
end
|
||||
in
|
||||
if bounds 0 i 0x7f
|
||||
then (addb i; fini())
|
||||
else
|
||||
if bounds 0x80 i 0x7ff
|
||||
then (addb ((0b1100_0000) lor (i lsr 6));
|
||||
add_trailing_bytes 1 i)
|
||||
else
|
||||
if bounds 0x800 i 0xffff
|
||||
then (addb ((0b1110_0000) lor (i lsr 12));
|
||||
add_trailing_bytes 2 i)
|
||||
else
|
||||
if bounds 0x1000 i 0x1f_ffff
|
||||
then (addb ((0b1111_0000) lor (i lsr 18));
|
||||
add_trailing_bytes 3 i)
|
||||
else
|
||||
if bounds 0x20_0000 i 0x3ff_ffff
|
||||
then (addb ((0b1111_1000) lor (i lsr 24));
|
||||
add_trailing_bytes 4 i)
|
||||
else
|
||||
if bounds 0x400_0000 i 0x7fff_ffff
|
||||
then (addb ((0b1111_1100) lor (i lsr 30));
|
||||
add_trailing_bytes 5 i)
|
||||
else bug () "bad unicode character 0x%X" i
|
||||
;;
|
||||
|
||||
(*
|
||||
* Size-expressions.
|
||||
*)
|
||||
|
||||
|
||||
type size =
|
||||
SIZE_fixed of int64
|
||||
| SIZE_fixup_mem_sz of fixup
|
||||
| SIZE_fixup_mem_pos of fixup
|
||||
| SIZE_param_size of ty_param_idx
|
||||
| SIZE_param_align of ty_param_idx
|
||||
| SIZE_rt_neg of size
|
||||
| SIZE_rt_add of size * size
|
||||
| SIZE_rt_mul of size * size
|
||||
| SIZE_rt_max of size * size
|
||||
| SIZE_rt_align of size * size
|
||||
;;
|
||||
|
||||
let rec string_of_size (s:size) : string =
|
||||
match s with
|
||||
SIZE_fixed i -> Printf.sprintf "%Ld" i
|
||||
| SIZE_fixup_mem_sz f -> Printf.sprintf "%s.mem_sz" f.fixup_name
|
||||
| SIZE_fixup_mem_pos f -> Printf.sprintf "%s.mem_pos" f.fixup_name
|
||||
| SIZE_param_size i -> Printf.sprintf "ty[%d].size" i
|
||||
| SIZE_param_align i -> Printf.sprintf "ty[%d].align" i
|
||||
| SIZE_rt_neg a ->
|
||||
Printf.sprintf "-(%s)" (string_of_size a)
|
||||
| SIZE_rt_add (a, b) ->
|
||||
Printf.sprintf "(%s + %s)" (string_of_size a) (string_of_size b)
|
||||
| SIZE_rt_mul (a, b) ->
|
||||
Printf.sprintf "(%s * %s)" (string_of_size a) (string_of_size b)
|
||||
| SIZE_rt_max (a, b) ->
|
||||
Printf.sprintf "max(%s,%s)" (string_of_size a) (string_of_size b)
|
||||
| SIZE_rt_align (align, off) ->
|
||||
Printf.sprintf "align(%s,%s)"
|
||||
(string_of_size align) (string_of_size off)
|
||||
;;
|
||||
|
||||
let neg_sz (a:size) : size =
|
||||
match a with
|
||||
SIZE_fixed a -> SIZE_fixed (Int64.neg a)
|
||||
| _ -> SIZE_rt_neg a
|
||||
;;
|
||||
|
||||
let add_sz (a:size) (b:size) : size =
|
||||
match (a, b) with
|
||||
(SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (Int64.add a b)
|
||||
|
||||
| ((SIZE_rt_add ((SIZE_fixed a), c)), SIZE_fixed b)
|
||||
| ((SIZE_rt_add (c, (SIZE_fixed a))), SIZE_fixed b)
|
||||
| (SIZE_fixed a, (SIZE_rt_add ((SIZE_fixed b), c)))
|
||||
| (SIZE_fixed a, (SIZE_rt_add (c, (SIZE_fixed b)))) ->
|
||||
SIZE_rt_add (SIZE_fixed (Int64.add a b), c)
|
||||
|
||||
| (SIZE_fixed 0L, b) -> b
|
||||
| (a, SIZE_fixed 0L) -> a
|
||||
| (a, SIZE_fixed b) -> SIZE_rt_add (SIZE_fixed b, a)
|
||||
| (a, b) -> SIZE_rt_add (a, b)
|
||||
;;
|
||||
|
||||
let mul_sz (a:size) (b:size) : size =
|
||||
match (a, b) with
|
||||
(SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (Int64.mul a b)
|
||||
| (a, SIZE_fixed b) -> SIZE_rt_mul (SIZE_fixed b, a)
|
||||
| (a, b) -> SIZE_rt_mul (a, b)
|
||||
;;
|
||||
|
||||
let rec max_sz (a:size) (b:size) : size =
|
||||
let rec no_negs x =
|
||||
match x with
|
||||
SIZE_fixed _
|
||||
| SIZE_fixup_mem_sz _
|
||||
| SIZE_fixup_mem_pos _
|
||||
| SIZE_param_size _
|
||||
| SIZE_param_align _ -> true
|
||||
| SIZE_rt_neg _ -> false
|
||||
| SIZE_rt_add (a,b) -> (no_negs a) && (no_negs b)
|
||||
| SIZE_rt_mul (a,b) -> (no_negs a) && (no_negs b)
|
||||
| SIZE_rt_max (a,b) -> (no_negs a) && (no_negs b)
|
||||
| SIZE_rt_align (a,b) -> (no_negs a) && (no_negs b)
|
||||
in
|
||||
match (a, b) with
|
||||
(SIZE_rt_align _, SIZE_fixed 1L) -> a
|
||||
| (SIZE_fixed 1L, SIZE_rt_align _) -> b
|
||||
| (SIZE_param_align _, SIZE_fixed 1L) -> a
|
||||
| (SIZE_fixed 1L, SIZE_param_align _) -> b
|
||||
| (a, SIZE_rt_max (b, c)) when a = b -> max_sz a c
|
||||
| (a, SIZE_rt_max (b, c)) when a = c -> max_sz a b
|
||||
| (SIZE_rt_max (b, c), a) when a = b -> max_sz a c
|
||||
| (SIZE_rt_max (b, c), a) when a = c -> max_sz a b
|
||||
| (SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (i64_max a b)
|
||||
| (SIZE_fixed 0L, b) when no_negs b -> b
|
||||
| (a, SIZE_fixed 0L) when no_negs a -> b
|
||||
| (a, SIZE_fixed b) -> max_sz (SIZE_fixed b) a
|
||||
| (a, b) when a = b -> a
|
||||
| (a, b) -> SIZE_rt_max (a, b)
|
||||
;;
|
||||
|
||||
(* FIXME: audit this carefuly; I am not terribly certain of the
|
||||
* algebraic simplification going on here. Sadly, without it
|
||||
* the diagnostic output from translation becomes completely
|
||||
* illegible.
|
||||
*)
|
||||
|
||||
let align_sz (a:size) (b:size) : size =
|
||||
let rec alignment_of s =
|
||||
match s with
|
||||
SIZE_rt_align (SIZE_fixed n, s) ->
|
||||
let inner_alignment = alignment_of s in
|
||||
if (Int64.rem n inner_alignment) = 0L
|
||||
then inner_alignment
|
||||
else n
|
||||
| SIZE_rt_add (SIZE_fixed n, s)
|
||||
| SIZE_rt_add (s, SIZE_fixed n) ->
|
||||
let inner_alignment = alignment_of s in
|
||||
if (Int64.rem n inner_alignment) = 0L
|
||||
then inner_alignment
|
||||
else 1L (* This could be lcd(...) or such. *)
|
||||
| SIZE_rt_max (a, SIZE_fixed 1L) -> alignment_of a
|
||||
| SIZE_rt_max (SIZE_fixed 1L, b) -> alignment_of b
|
||||
| _ -> 1L
|
||||
in
|
||||
match (a, b) with
|
||||
(SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (i64_align a b)
|
||||
| (SIZE_fixed x, _) when i64_lt x 1L -> bug () "alignment less than 1"
|
||||
| (SIZE_fixed 1L, b) -> b (* everything is 1-aligned. *)
|
||||
| (_, SIZE_fixed 0L) -> b (* 0 is everything-aligned. *)
|
||||
| (SIZE_fixed a, b) ->
|
||||
let inner_alignment = alignment_of b in
|
||||
if (Int64.rem a inner_alignment) = 0L
|
||||
then b
|
||||
else SIZE_rt_align (SIZE_fixed a, b)
|
||||
| (SIZE_rt_max (a, SIZE_fixed 1L), b) -> SIZE_rt_align (a, b)
|
||||
| (SIZE_rt_max (SIZE_fixed 1L, a), b) -> SIZE_rt_align (a, b)
|
||||
| (a, b) -> SIZE_rt_align (a, b)
|
||||
;;
|
||||
|
||||
let force_sz (a:size) : int64 =
|
||||
match a with
|
||||
SIZE_fixed i -> i
|
||||
| _ -> bug () "force_sz: forced non-fixed size expression %s"
|
||||
(string_of_size a)
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
12
src/comp/driver/rustc.rs
Normal file
12
src/comp/driver/rustc.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// -*- rust -*-
|
||||
|
||||
fn main(vec[str] args) -> () {
|
||||
let int i = 0;
|
||||
for (str filename in args) {
|
||||
if (i > 0) {
|
||||
auto br = std._io.mk_buf_reader(filename);
|
||||
log "opened file: " + filename;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
0
src/comp/fe/lexer.rs
Normal file
0
src/comp/fe/lexer.rs
Normal file
0
src/comp/fe/parser.rs
Normal file
0
src/comp/fe/parser.rs
Normal file
20
src/comp/rustc.rc
Normal file
20
src/comp/rustc.rc
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
// -*- rust -*-
|
||||
|
||||
use std;
|
||||
|
||||
mod fe {
|
||||
mod lexer;
|
||||
mod parser;
|
||||
}
|
||||
|
||||
mod driver {
|
||||
mod rustc;
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
25
src/etc/tidy.py
Normal file
25
src/etc/tidy.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys, fileinput
|
||||
|
||||
err=0
|
||||
cols=78
|
||||
|
||||
def report_err(s):
|
||||
global err
|
||||
print("%s:%d: %s" % (fileinput.filename(), fileinput.filelineno(), s))
|
||||
err=1
|
||||
|
||||
for line in fileinput.input(openhook=fileinput.hook_encoded("utf-8")):
|
||||
if line.find('\t') != -1 and fileinput.filename().find("Makefile") == -1:
|
||||
report_err("tab character")
|
||||
|
||||
if line.find('\r') != -1:
|
||||
report_err("CR character")
|
||||
|
||||
if len(line)-1 > cols:
|
||||
report_err("line longer than %d chars" % cols)
|
||||
|
||||
|
||||
sys.exit(err)
|
||||
|
14
src/etc/x86.supp
Normal file
14
src/etc/x86.supp
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
our-failure-to-setup-freeres-structure
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:_vgnU_freeres
|
||||
}
|
||||
|
||||
{
|
||||
leaked-TLS-chunk-x86-exit-path-fails-to-clean-up
|
||||
Memcheck:Leak
|
||||
fun:calloc
|
||||
fun:_dl_allocate_tls
|
||||
}
|
20
src/lib/_int.rs
Normal file
20
src/lib/_int.rs
Normal file
@ -0,0 +1,20 @@
|
||||
fn add(int x, int y) -> int { ret x + y; }
|
||||
fn sub(int x, int y) -> int { ret x - y; }
|
||||
fn mul(int x, int y) -> int { ret x * y; }
|
||||
fn div(int x, int y) -> int { ret x / y; }
|
||||
fn rem(int x, int y) -> int { ret x % y; }
|
||||
|
||||
fn lt(int x, int y) -> bool { ret x < y; }
|
||||
fn le(int x, int y) -> bool { ret x <= y; }
|
||||
fn eq(int x, int y) -> bool { ret x == y; }
|
||||
fn ne(int x, int y) -> bool { ret x != y; }
|
||||
fn ge(int x, int y) -> bool { ret x >= y; }
|
||||
fn gt(int x, int y) -> bool { ret x > y; }
|
||||
|
||||
iter range(mutable int lo, int hi) -> int {
|
||||
while (lo < hi) {
|
||||
put lo;
|
||||
lo += 1;
|
||||
}
|
||||
}
|
||||
|
36
src/lib/_io.rs
Normal file
36
src/lib/_io.rs
Normal file
@ -0,0 +1,36 @@
|
||||
type buf_reader = obj {
|
||||
fn read(vec[u8] buf) -> uint;
|
||||
};
|
||||
|
||||
type buf_writer = obj {
|
||||
fn write(vec[u8] buf) -> uint;
|
||||
};
|
||||
|
||||
fn mk_buf_reader(str s) -> buf_reader {
|
||||
|
||||
obj fd_reader(int fd) {
|
||||
fn read(vec[u8] v) -> uint {
|
||||
auto len = _vec.len[u8](v);
|
||||
auto buf = _vec.buf[u8](v);
|
||||
auto count = os.libc.read(fd, buf, len);
|
||||
if (count < 0) {
|
||||
log "error filling buffer";
|
||||
log sys.rustrt.last_os_error();
|
||||
fail;
|
||||
} else {
|
||||
ret uint(count);
|
||||
}
|
||||
}
|
||||
drop {
|
||||
os.libc.close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
auto fd = os.libc.open(_str.buf(s), 0);
|
||||
if (fd < 0) {
|
||||
log "error opening file";
|
||||
log sys.rustrt.last_os_error();
|
||||
fail;
|
||||
}
|
||||
ret fd_reader(fd);
|
||||
}
|
23
src/lib/_str.rs
Normal file
23
src/lib/_str.rs
Normal file
@ -0,0 +1,23 @@
|
||||
import rustrt.sbuf;
|
||||
|
||||
native "rust" mod rustrt {
|
||||
type sbuf;
|
||||
fn str_buf(str s) -> sbuf;
|
||||
fn str_len(str s) -> uint;
|
||||
fn str_alloc(int n_bytes) -> str;
|
||||
}
|
||||
|
||||
fn is_utf8(vec[u8] v) -> bool {
|
||||
}
|
||||
|
||||
fn alloc(int n_bytes) -> str {
|
||||
ret rustrt.str_alloc(n_bytes);
|
||||
}
|
||||
|
||||
fn len(str s) -> uint {
|
||||
ret rustrt.str_len(s);
|
||||
}
|
||||
|
||||
fn buf(str s) -> sbuf {
|
||||
ret rustrt.str_buf(s);
|
||||
}
|
20
src/lib/_u8.rs
Normal file
20
src/lib/_u8.rs
Normal file
@ -0,0 +1,20 @@
|
||||
fn add(u8 x, u8 y) -> u8 { ret x + y; }
|
||||
fn sub(u8 x, u8 y) -> u8 { ret x - y; }
|
||||
fn mul(u8 x, u8 y) -> u8 { ret x * y; }
|
||||
fn div(u8 x, u8 y) -> u8 { ret x / y; }
|
||||
fn rem(u8 x, u8 y) -> u8 { ret x % y; }
|
||||
|
||||
fn lt(u8 x, u8 y) -> bool { ret x < y; }
|
||||
fn le(u8 x, u8 y) -> bool { ret x <= y; }
|
||||
fn eq(u8 x, u8 y) -> bool { ret x == y; }
|
||||
fn ne(u8 x, u8 y) -> bool { ret x != y; }
|
||||
fn ge(u8 x, u8 y) -> bool { ret x >= y; }
|
||||
fn gt(u8 x, u8 y) -> bool { ret x > y; }
|
||||
|
||||
iter range(mutable u8 lo, u8 hi) -> u8 {
|
||||
while (lo < hi) {
|
||||
put lo;
|
||||
lo += u8(1);
|
||||
}
|
||||
}
|
||||
|
30
src/lib/_vec.rs
Normal file
30
src/lib/_vec.rs
Normal file
@ -0,0 +1,30 @@
|
||||
import vbuf = rustrt.vbuf;
|
||||
|
||||
native "rust" mod rustrt {
|
||||
type vbuf;
|
||||
fn vec_buf[T](vec[T] v) -> vbuf;
|
||||
fn vec_len[T](vec[T] v) -> uint;
|
||||
fn vec_alloc[T](int n_elts) -> vec[T];
|
||||
}
|
||||
|
||||
fn alloc[T](int n_elts) -> vec[T] {
|
||||
ret rustrt.vec_alloc[T](n_elts);
|
||||
}
|
||||
|
||||
fn init[T](&T t, int n_elts) -> vec[T] {
|
||||
let vec[T] v = alloc[T](n_elts);
|
||||
let int i = n_elts;
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
v += vec(t);
|
||||
}
|
||||
ret v;
|
||||
}
|
||||
|
||||
fn len[T](vec[T] v) -> uint {
|
||||
ret rustrt.vec_len[T](v);
|
||||
}
|
||||
|
||||
fn buf[T](vec[T] v) -> vbuf {
|
||||
ret rustrt.vec_buf[T](v);
|
||||
}
|
19
src/lib/linux_os.rs
Normal file
19
src/lib/linux_os.rs
Normal file
@ -0,0 +1,19 @@
|
||||
import _str.sbuf;
|
||||
import _vec.vbuf;
|
||||
|
||||
native mod libc = "libc.so.6" {
|
||||
|
||||
fn open(sbuf s, int flags) -> int;
|
||||
fn read(int fd, vbuf buf, uint count) -> int;
|
||||
fn write(int fd, vbuf buf, uint count) -> int;
|
||||
fn close(int fd) -> int;
|
||||
|
||||
type dir;
|
||||
// readdir is a mess; handle via wrapper function in rustrt.
|
||||
fn opendir(sbuf d) -> dir;
|
||||
fn closedir(dir d) -> int;
|
||||
|
||||
fn getenv(sbuf n) -> sbuf;
|
||||
fn setenv(sbuf n, sbuf v, int overwrite) -> int;
|
||||
fn unsetenv(sbuf n) -> int;
|
||||
}
|
19
src/lib/macos_os.rs
Normal file
19
src/lib/macos_os.rs
Normal file
@ -0,0 +1,19 @@
|
||||
import _str.sbuf;
|
||||
import _vec.vbuf;
|
||||
|
||||
native mod libc = "libc.dylib" {
|
||||
|
||||
fn open(sbuf s, int flags) -> int;
|
||||
fn read(int fd, vbuf buf, uint count) -> int;
|
||||
fn write(int fd, vbuf buf, uint count) -> int;
|
||||
fn close(int fd) -> int;
|
||||
|
||||
type dir;
|
||||
// readdir is a mess; handle via wrapper function in rustrt.
|
||||
fn opendir(sbuf d) -> dir;
|
||||
fn closedir(dir d) -> int;
|
||||
|
||||
fn getenv(sbuf n) -> sbuf;
|
||||
fn setenv(sbuf n, sbuf v, int overwrite) -> int;
|
||||
fn unsetenv(sbuf n) -> int;
|
||||
}
|
35
src/lib/std.rc
Normal file
35
src/lib/std.rc
Normal file
@ -0,0 +1,35 @@
|
||||
meta (name = "std",
|
||||
desc = "Rust standard library",
|
||||
uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297",
|
||||
url = "http://rust-lang.org/src/std",
|
||||
ver = "0.0.1");
|
||||
|
||||
// Built-in types support modules.
|
||||
|
||||
mod _int;
|
||||
mod _u8;
|
||||
mod _vec;
|
||||
mod _str;
|
||||
|
||||
// General IO and system-services modules.
|
||||
|
||||
mod _io;
|
||||
mod sys;
|
||||
|
||||
// Authorize various rule-bendings.
|
||||
|
||||
auth _io = unsafe;
|
||||
auth _str = unsafe;
|
||||
auth _vec = unsafe;
|
||||
|
||||
// Target-OS module.
|
||||
|
||||
alt (target_os) {
|
||||
case ("win32") {
|
||||
mod os = "win32_os.rs";
|
||||
} case ("macos") {
|
||||
mod os = "macos_os.rs";
|
||||
} else {
|
||||
mod os = "linux_os.rs";
|
||||
}
|
||||
}
|
7
src/lib/sys.rs
Normal file
7
src/lib/sys.rs
Normal file
@ -0,0 +1,7 @@
|
||||
native "rust" mod rustrt {
|
||||
fn last_os_error() -> str;
|
||||
fn size_of[T]() -> uint;
|
||||
fn align_of[T]() -> uint;
|
||||
fn refcount[T](@T t) -> uint;
|
||||
}
|
||||
|
9
src/lib/win32_os.rs
Normal file
9
src/lib/win32_os.rs
Normal file
@ -0,0 +1,9 @@
|
||||
import _str.sbuf;
|
||||
import _vec.vbuf;
|
||||
|
||||
native mod libc = "msvcrt.dll" {
|
||||
fn open(sbuf s, int flags) -> int = "_open";
|
||||
fn read(int fd, vbuf buf, uint count) -> int = "_read";
|
||||
fn write(int fd, vbuf buf, uint count) -> int = "_write";
|
||||
fn close(int fd) -> int = "_close";
|
||||
}
|
294
src/rt/bigint/bigint.h
Normal file
294
src/rt/bigint/bigint.h
Normal file
@ -0,0 +1,294 @@
|
||||
/* bigint.h - include file for bigint package
|
||||
**
|
||||
** This library lets you do math on arbitrarily large integers. It's
|
||||
** pretty fast - compared with the multi-precision routines in the "bc"
|
||||
** calculator program, these routines are between two and twelve times faster,
|
||||
** except for division which is maybe half as fast.
|
||||
**
|
||||
** The calling convention is a little unusual. There's a basic problem
|
||||
** with writing a math library in a language that doesn't do automatic
|
||||
** garbage collection - what do you do about intermediate results?
|
||||
** You'd like to be able to write code like this:
|
||||
**
|
||||
** d = bi_sqrt( bi_add( bi_multiply( x, x ), bi_multiply( y, y ) ) );
|
||||
**
|
||||
** That works fine when the numbers being passed back and forth are
|
||||
** actual values - ints, floats, or even fixed-size structs. However,
|
||||
** when the numbers can be any size, as in this package, then you have
|
||||
** to pass them around as pointers to dynamically-allocated objects.
|
||||
** Those objects have to get de-allocated after you are done with them.
|
||||
** But how do you de-allocate the intermediate results in a complicated
|
||||
** multiple-call expression like the above?
|
||||
**
|
||||
** There are two common solutions to this problem. One, switch all your
|
||||
** code to a language that provides automatic garbage collection, for
|
||||
** example Java. This is a fine idea and I recommend you do it wherever
|
||||
** it's feasible. Two, change your routines to use a calling convention
|
||||
** that prevents people from writing multiple-call expressions like that.
|
||||
** The resulting code will be somewhat clumsy-looking, but it will work
|
||||
** just fine.
|
||||
**
|
||||
** This package uses a third method, which I haven't seen used anywhere
|
||||
** before. It's simple: each number can be used precisely once, after
|
||||
** which it is automatically de-allocated. This handles the anonymous
|
||||
** intermediate values perfectly. Named values still need to be copied
|
||||
** and freed explicitly. Here's the above example using this convention:
|
||||
**
|
||||
** d = bi_sqrt( bi_add(
|
||||
** bi_multiply( bi_copy( x ), bi_copy( x ) ),
|
||||
** bi_multiply( bi_copy( y ), bi_copy( y ) ) ) );
|
||||
** bi_free( x );
|
||||
** bi_free( y );
|
||||
**
|
||||
** Or, since the package contains a square routine, you could just write:
|
||||
**
|
||||
** d = bi_sqrt( bi_add( bi_square( x ), bi_square( y ) ) );
|
||||
**
|
||||
** This time the named values are only being used once, so you don't
|
||||
** have to copy and free them.
|
||||
**
|
||||
** This really works, however you do have to be very careful when writing
|
||||
** your code. If you leave out a bi_copy() and use a value more than once,
|
||||
** you'll get a runtime error about "zero refs" and a SIGFPE. Run your
|
||||
** code in a debugger, get a backtrace to see where the call was, and then
|
||||
** eyeball the code there to see where you need to add the bi_copy().
|
||||
**
|
||||
**
|
||||
** Copyright © 2000 by Jef Poskanzer <jef@mail.acme.com>.
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
** SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
/* Type definition for bigints - it's an opaque type, the real definition
|
||||
** is in bigint.c.
|
||||
*/
|
||||
typedef void* bigint;
|
||||
|
||||
|
||||
/* Some convenient pre-initialized numbers. These are all permanent,
|
||||
** so you can use them as many times as you want without calling bi_copy().
|
||||
*/
|
||||
extern bigint bi_0, bi_1, bi_2, bi_10, bi_m1, bi_maxint, bi_minint;
|
||||
|
||||
|
||||
/* Initialize the bigint package. You must call this when your program
|
||||
** starts up.
|
||||
*/
|
||||
void bi_initialize( void );
|
||||
|
||||
/* Shut down the bigint package. You should call this when your program
|
||||
** exits. It's not actually required, but it does do some consistency
|
||||
** checks which help keep your program bug-free, so you really ought
|
||||
** to call it.
|
||||
*/
|
||||
void bi_terminate( void );
|
||||
|
||||
/* Run in unsafe mode, skipping most runtime checks. Slightly faster.
|
||||
** Once your code is debugged you can add this call after bi_initialize().
|
||||
*/
|
||||
void bi_no_check( void );
|
||||
|
||||
/* Make a copy of a bigint. You must call this if you want to use a
|
||||
** bigint more than once. (Or you can make the bigint permanent.)
|
||||
** Note that this routine is very cheap - all it actually does is
|
||||
** increment a reference counter.
|
||||
*/
|
||||
bigint bi_copy( bigint bi );
|
||||
|
||||
/* Make a bigint permanent, so it doesn't get automatically freed when
|
||||
** used as an operand.
|
||||
*/
|
||||
void bi_permanent( bigint bi );
|
||||
|
||||
/* Undo bi_permanent(). The next use will free the bigint. */
|
||||
void bi_depermanent( bigint bi );
|
||||
|
||||
/* Explicitly free a bigint. Normally bigints get freed automatically
|
||||
** when they are used as an operand. This routine lets you free one
|
||||
** without using it. If the bigint is permanent, this doesn't do
|
||||
** anything, you have to depermanent it first.
|
||||
*/
|
||||
void bi_free( bigint bi );
|
||||
|
||||
/* Compare two bigints. Returns -1, 0, or 1. */
|
||||
int bi_compare( bigint bia, bigint bib );
|
||||
|
||||
/* Convert an int to a bigint. */
|
||||
bigint int_to_bi( int i );
|
||||
|
||||
/* Convert a string to a bigint. */
|
||||
bigint str_to_bi( char* str );
|
||||
|
||||
/* Convert a bigint to an int. SIGFPE on overflow. */
|
||||
int bi_to_int( bigint bi );
|
||||
|
||||
/* Write a bigint to a file. */
|
||||
void bi_print( FILE* f, bigint bi );
|
||||
|
||||
/* Read a bigint from a file. */
|
||||
bigint bi_scan( FILE* f );
|
||||
|
||||
|
||||
/* Operations on a bigint and a regular int. */
|
||||
|
||||
/* Add an int to a bigint. */
|
||||
bigint bi_int_add( bigint bi, int i );
|
||||
|
||||
/* Subtract an int from a bigint. */
|
||||
bigint bi_int_subtract( bigint bi, int i );
|
||||
|
||||
/* Multiply a bigint by an int. */
|
||||
bigint bi_int_multiply( bigint bi, int i );
|
||||
|
||||
/* Divide a bigint by an int. SIGFPE on divide-by-zero. */
|
||||
bigint bi_int_divide( bigint binumer, int denom );
|
||||
|
||||
/* Take the remainder of a bigint by an int, with an int result.
|
||||
** SIGFPE if m is zero.
|
||||
*/
|
||||
int bi_int_rem( bigint bi, int m );
|
||||
|
||||
/* Take the modulus of a bigint by an int, with an int result.
|
||||
** Note that mod is not rem: mod is always within [0..m), while
|
||||
** rem can be negative. SIGFPE if m is zero or negative.
|
||||
*/
|
||||
int bi_int_mod( bigint bi, int m );
|
||||
|
||||
|
||||
/* Basic operations on two bigints. */
|
||||
|
||||
/* Add two bigints. */
|
||||
bigint bi_add( bigint bia, bigint bib );
|
||||
|
||||
/* Subtract bib from bia. */
|
||||
bigint bi_subtract( bigint bia, bigint bib );
|
||||
|
||||
/* Multiply two bigints. */
|
||||
bigint bi_multiply( bigint bia, bigint bib );
|
||||
|
||||
/* Divide one bigint by another. SIGFPE on divide-by-zero. */
|
||||
bigint bi_divide( bigint binumer, bigint bidenom );
|
||||
|
||||
/* Binary division of one bigint by another. SIGFPE on divide-by-zero.
|
||||
** This is here just for testing. It's about five times slower than
|
||||
** regular division.
|
||||
*/
|
||||
bigint bi_binary_divide( bigint binumer, bigint bidenom );
|
||||
|
||||
/* Take the remainder of one bigint by another. SIGFPE if bim is zero. */
|
||||
bigint bi_rem( bigint bia, bigint bim );
|
||||
|
||||
/* Take the modulus of one bigint by another. Note that mod is not rem:
|
||||
** mod is always within [0..bim), while rem can be negative. SIGFPE if
|
||||
** bim is zero or negative.
|
||||
*/
|
||||
bigint bi_mod( bigint bia, bigint bim );
|
||||
|
||||
|
||||
/* Some less common operations. */
|
||||
|
||||
/* Negate a bigint. */
|
||||
bigint bi_negate( bigint bi );
|
||||
|
||||
/* Absolute value of a bigint. */
|
||||
bigint bi_abs( bigint bi );
|
||||
|
||||
/* Divide a bigint in half. */
|
||||
bigint bi_half( bigint bi );
|
||||
|
||||
/* Multiply a bigint by two. */
|
||||
bigint bi_double( bigint bi );
|
||||
|
||||
/* Square a bigint. */
|
||||
bigint bi_square( bigint bi );
|
||||
|
||||
/* Raise bi to the power of biexp. SIGFPE if biexp is negative. */
|
||||
bigint bi_power( bigint bi, bigint biexp );
|
||||
|
||||
/* Integer square root. */
|
||||
bigint bi_sqrt( bigint bi );
|
||||
|
||||
/* Factorial. */
|
||||
bigint bi_factorial( bigint bi );
|
||||
|
||||
|
||||
/* Some predicates. */
|
||||
|
||||
/* 1 if the bigint is odd, 0 if it's even. */
|
||||
int bi_is_odd( bigint bi );
|
||||
|
||||
/* 1 if the bigint is even, 0 if it's odd. */
|
||||
int bi_is_even( bigint bi );
|
||||
|
||||
/* 1 if the bigint equals zero, 0 if it's nonzero. */
|
||||
int bi_is_zero( bigint bi );
|
||||
|
||||
/* 1 if the bigint equals one, 0 otherwise. */
|
||||
int bi_is_one( bigint bi );
|
||||
|
||||
/* 1 if the bigint is less than zero, 0 if it's zero or greater. */
|
||||
int bi_is_negative( bigint bi );
|
||||
|
||||
|
||||
/* Now we get into the esoteric number-theory stuff used for cryptography. */
|
||||
|
||||
/* Modular exponentiation. Much faster than bi_mod(bi_power(bi,biexp),bim).
|
||||
** Also, biexp can be negative.
|
||||
*/
|
||||
bigint bi_mod_power( bigint bi, bigint biexp, bigint bim );
|
||||
|
||||
/* Modular inverse. mod( bi * modinv(bi), bim ) == 1. SIGFPE if bi is not
|
||||
** relatively prime to bim.
|
||||
*/
|
||||
bigint bi_mod_inverse( bigint bi, bigint bim );
|
||||
|
||||
/* Produce a random number in the half-open interval [0..bi). You need
|
||||
** to have called srandom() before using this.
|
||||
*/
|
||||
bigint bi_random( bigint bi );
|
||||
|
||||
/* Greatest common divisor of two bigints. Euclid's algorithm. */
|
||||
bigint bi_gcd( bigint bim, bigint bin );
|
||||
|
||||
/* Greatest common divisor of two bigints, plus the corresponding multipliers.
|
||||
** Extended Euclid's algorithm.
|
||||
*/
|
||||
bigint bi_egcd( bigint bim, bigint bin, bigint* bim_mul, bigint* bin_mul );
|
||||
|
||||
/* Least common multiple of two bigints. */
|
||||
bigint bi_lcm( bigint bia, bigint bib );
|
||||
|
||||
/* The Jacobi symbol. SIGFPE if bib is even. */
|
||||
bigint bi_jacobi( bigint bia, bigint bib );
|
||||
|
||||
/* Probabalistic prime checking. A non-zero return means the probability
|
||||
** that bi is prime is at least 1 - 1/2 ^ certainty.
|
||||
*/
|
||||
int bi_is_probable_prime( bigint bi, int certainty );
|
||||
|
||||
/* Random probabilistic prime with the specified number of bits. */
|
||||
bigint bi_generate_prime( int bits, int certainty );
|
||||
|
||||
/* Number of bits in the number. The log base 2, approximately. */
|
||||
int bi_bits( bigint bi );
|
553
src/rt/bigint/bigint_ext.cpp
Normal file
553
src/rt/bigint/bigint_ext.cpp
Normal file
@ -0,0 +1,553 @@
|
||||
/* bigint_ext - external portion of large integer package
|
||||
**
|
||||
** Copyright © 2000 by Jef Poskanzer <jef@mail.acme.com>.
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
** SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "bigint.h"
|
||||
#include "low_primes.h"
|
||||
|
||||
|
||||
bigint bi_0, bi_1, bi_2, bi_10, bi_m1, bi_maxint, bi_minint;
|
||||
|
||||
|
||||
/* Forwards. */
|
||||
static void print_pos( FILE* f, bigint bi );
|
||||
|
||||
|
||||
bigint
|
||||
str_to_bi( char* str )
|
||||
{
|
||||
int sign;
|
||||
bigint biR;
|
||||
|
||||
sign = 1;
|
||||
if ( *str == '-' )
|
||||
{
|
||||
sign = -1;
|
||||
++str;
|
||||
}
|
||||
for ( biR = bi_0; *str >= '0' && *str <= '9'; ++str )
|
||||
biR = bi_int_add( bi_int_multiply( biR, 10 ), *str - '0' );
|
||||
if ( sign == -1 )
|
||||
biR = bi_negate( biR );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bi_print( FILE* f, bigint bi )
|
||||
{
|
||||
if ( bi_is_negative( bi_copy( bi ) ) )
|
||||
{
|
||||
putc( '-', f );
|
||||
bi = bi_negate( bi );
|
||||
}
|
||||
print_pos( f, bi );
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_scan( FILE* f )
|
||||
{
|
||||
int sign;
|
||||
int c;
|
||||
bigint biR;
|
||||
|
||||
sign = 1;
|
||||
c = getc( f );
|
||||
if ( c == '-' )
|
||||
sign = -1;
|
||||
else
|
||||
ungetc( c, f );
|
||||
|
||||
biR = bi_0;
|
||||
for (;;)
|
||||
{
|
||||
c = getc( f );
|
||||
if ( c < '0' || c > '9' )
|
||||
break;
|
||||
biR = bi_int_add( bi_int_multiply( biR, 10 ), c - '0' );
|
||||
}
|
||||
|
||||
if ( sign == -1 )
|
||||
biR = bi_negate( biR );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_pos( FILE* f, bigint bi )
|
||||
{
|
||||
if ( bi_compare( bi_copy( bi ), bi_10 ) >= 0 )
|
||||
print_pos( f, bi_int_divide( bi_copy( bi ), 10 ) );
|
||||
putc( bi_int_mod( bi, 10 ) + '0', f );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bi_int_mod( bigint bi, int m )
|
||||
{
|
||||
int r;
|
||||
|
||||
if ( m <= 0 )
|
||||
{
|
||||
(void) fprintf( stderr, "bi_int_mod: zero or negative modulus\n" );
|
||||
(void) kill( getpid(), SIGFPE );
|
||||
}
|
||||
r = bi_int_rem( bi, m );
|
||||
if ( r < 0 )
|
||||
r += m;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_rem( bigint bia, bigint bim )
|
||||
{
|
||||
return bi_subtract(
|
||||
bia, bi_multiply( bi_divide( bi_copy( bia ), bi_copy( bim ) ), bim ) );
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_mod( bigint bia, bigint bim )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
if ( bi_compare( bi_copy( bim ), bi_0 ) <= 0 )
|
||||
{
|
||||
(void) fprintf( stderr, "bi_mod: zero or negative modulus\n" );
|
||||
(void) kill( getpid(), SIGFPE );
|
||||
}
|
||||
biR = bi_rem( bia, bi_copy( bim ) );
|
||||
if ( bi_is_negative( bi_copy( biR ) ) )
|
||||
biR = bi_add( biR, bim );
|
||||
else
|
||||
bi_free( bim );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_square( bigint bi )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
biR = bi_multiply( bi_copy( bi ), bi_copy( bi ) );
|
||||
bi_free( bi );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_power( bigint bi, bigint biexp )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
if ( bi_is_negative( bi_copy( biexp ) ) )
|
||||
{
|
||||
(void) fprintf( stderr, "bi_power: negative exponent\n" );
|
||||
(void) kill( getpid(), SIGFPE );
|
||||
}
|
||||
biR = bi_1;
|
||||
for (;;)
|
||||
{
|
||||
if ( bi_is_odd( bi_copy( biexp ) ) )
|
||||
biR = bi_multiply( biR, bi_copy( bi ) );
|
||||
biexp = bi_half( biexp );
|
||||
if ( bi_compare( bi_copy( biexp ), bi_0 ) <= 0 )
|
||||
break;
|
||||
bi = bi_multiply( bi_copy( bi ), bi );
|
||||
}
|
||||
bi_free( bi );
|
||||
bi_free( biexp );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_factorial( bigint bi )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
biR = bi_1;
|
||||
while ( bi_compare( bi_copy( bi ), bi_1 ) > 0 )
|
||||
{
|
||||
biR = bi_multiply( biR, bi_copy( bi ) );
|
||||
bi = bi_int_subtract( bi, 1 );
|
||||
}
|
||||
bi_free( bi );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bi_is_even( bigint bi )
|
||||
{
|
||||
return ! bi_is_odd( bi );
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_mod_power( bigint bi, bigint biexp, bigint bim )
|
||||
{
|
||||
int invert;
|
||||
bigint biR;
|
||||
|
||||
invert = 0;
|
||||
if ( bi_is_negative( bi_copy( biexp ) ) )
|
||||
{
|
||||
biexp = bi_negate( biexp );
|
||||
invert = 1;
|
||||
}
|
||||
|
||||
biR = bi_1;
|
||||
for (;;)
|
||||
{
|
||||
if ( bi_is_odd( bi_copy( biexp ) ) )
|
||||
biR = bi_mod( bi_multiply( biR, bi_copy( bi ) ), bi_copy( bim ) );
|
||||
biexp = bi_half( biexp );
|
||||
if ( bi_compare( bi_copy( biexp ), bi_0 ) <= 0 )
|
||||
break;
|
||||
bi = bi_mod( bi_multiply( bi_copy( bi ), bi ), bi_copy( bim ) );
|
||||
}
|
||||
bi_free( bi );
|
||||
bi_free( biexp );
|
||||
|
||||
if ( invert )
|
||||
biR = bi_mod_inverse( biR, bim );
|
||||
else
|
||||
bi_free( bim );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_mod_inverse( bigint bi, bigint bim )
|
||||
{
|
||||
bigint gcd, mul0, mul1;
|
||||
|
||||
gcd = bi_egcd( bi_copy( bim ), bi, &mul0, &mul1 );
|
||||
|
||||
/* Did we get gcd == 1? */
|
||||
if ( ! bi_is_one( gcd ) )
|
||||
{
|
||||
(void) fprintf( stderr, "bi_mod_inverse: not relatively prime\n" );
|
||||
(void) kill( getpid(), SIGFPE );
|
||||
}
|
||||
|
||||
bi_free( mul0 );
|
||||
return bi_mod( mul1, bim );
|
||||
}
|
||||
|
||||
|
||||
/* Euclid's algorithm. */
|
||||
bigint
|
||||
bi_gcd( bigint bim, bigint bin )
|
||||
{
|
||||
bigint bit;
|
||||
|
||||
bim = bi_abs( bim );
|
||||
bin = bi_abs( bin );
|
||||
while ( ! bi_is_zero( bi_copy( bin ) ) )
|
||||
{
|
||||
bit = bi_mod( bim, bi_copy( bin ) );
|
||||
bim = bin;
|
||||
bin = bit;
|
||||
}
|
||||
bi_free( bin );
|
||||
return bim;
|
||||
}
|
||||
|
||||
|
||||
/* Extended Euclidean algorithm. */
|
||||
bigint
|
||||
bi_egcd( bigint bim, bigint bin, bigint* bim_mul, bigint* bin_mul )
|
||||
{
|
||||
bigint a0, b0, c0, a1, b1, c1, q, t;
|
||||
|
||||
if ( bi_is_negative( bi_copy( bim ) ) )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
biR = bi_egcd( bi_negate( bim ), bin, &t, bin_mul );
|
||||
*bim_mul = bi_negate( t );
|
||||
return biR;
|
||||
}
|
||||
if ( bi_is_negative( bi_copy( bin ) ) )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
biR = bi_egcd( bim, bi_negate( bin ), bim_mul, &t );
|
||||
*bin_mul = bi_negate( t );
|
||||
return biR;
|
||||
}
|
||||
|
||||
a0 = bi_1; b0 = bi_0; c0 = bim;
|
||||
a1 = bi_0; b1 = bi_1; c1 = bin;
|
||||
|
||||
while ( ! bi_is_zero( bi_copy( c1 ) ) )
|
||||
{
|
||||
q = bi_divide( bi_copy( c0 ), bi_copy( c1 ) );
|
||||
t = a0;
|
||||
a0 = bi_copy( a1 );
|
||||
a1 = bi_subtract( t, bi_multiply( bi_copy( q ), a1 ) );
|
||||
t = b0;
|
||||
b0 = bi_copy( b1 );
|
||||
b1 = bi_subtract( t, bi_multiply( bi_copy( q ), b1 ) );
|
||||
t = c0;
|
||||
c0 = bi_copy( c1 );
|
||||
c1 = bi_subtract( t, bi_multiply( bi_copy( q ), c1 ) );
|
||||
bi_free( q );
|
||||
}
|
||||
|
||||
bi_free( a1 );
|
||||
bi_free( b1 );
|
||||
bi_free( c1 );
|
||||
*bim_mul = a0;
|
||||
*bin_mul = b0;
|
||||
return c0;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_lcm( bigint bia, bigint bib )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
biR = bi_divide(
|
||||
bi_multiply( bi_copy( bia ), bi_copy( bib ) ),
|
||||
bi_gcd( bi_copy( bia ), bi_copy( bib ) ) );
|
||||
bi_free( bia );
|
||||
bi_free( bib );
|
||||
return biR;
|
||||
}
|
||||
|
||||
|
||||
/* The Jacobi symbol. */
|
||||
bigint
|
||||
bi_jacobi( bigint bia, bigint bib )
|
||||
{
|
||||
bigint biR;
|
||||
|
||||
if ( bi_is_even( bi_copy( bib ) ) )
|
||||
{
|
||||
(void) fprintf( stderr, "bi_jacobi: don't know how to compute Jacobi(n, even)\n" );
|
||||
(void) kill( getpid(), SIGFPE );
|
||||
}
|
||||
|
||||
if ( bi_compare( bi_copy( bia ), bi_copy( bib ) ) >= 0 )
|
||||
return bi_jacobi( bi_mod( bia, bi_copy( bib ) ), bib );
|
||||
|
||||
if ( bi_is_zero( bi_copy( bia ) ) || bi_is_one( bi_copy( bia ) ) )
|
||||
{
|
||||
bi_free( bib );
|
||||
return bia;
|
||||
}
|
||||
|
||||
if ( bi_compare( bi_copy( bia ), bi_2 ) == 0 )
|
||||
{
|
||||
bi_free( bia );
|
||||
switch ( bi_int_mod( bib, 8 ) )
|
||||
{
|
||||
case 1: case 7:
|
||||
return bi_1;
|
||||
case 3: case 5:
|
||||
return bi_m1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bi_is_even( bi_copy( bia ) ) )
|
||||
{
|
||||
biR = bi_multiply(
|
||||
bi_jacobi( bi_2, bi_copy( bib ) ),
|
||||
bi_jacobi( bi_half( bia ), bi_copy( bib ) ) );
|
||||
bi_free( bib );
|
||||
return biR;
|
||||
}
|
||||
|
||||
if ( bi_int_mod( bi_copy( bia ), 4 ) == 3 &&
|
||||
bi_int_mod( bi_copy( bib ), 4 ) == 3 )
|
||||
return bi_negate( bi_jacobi( bib, bia ) );
|
||||
else
|
||||
return bi_jacobi( bib, bia );
|
||||
}
|
||||
|
||||
|
||||
/* Probabalistic prime checking. */
|
||||
int
|
||||
bi_is_probable_prime( bigint bi, int certainty )
|
||||
{
|
||||
int i, p;
|
||||
bigint bim1;
|
||||
|
||||
/* First do trial division by a list of small primes. This eliminates
|
||||
** many candidates.
|
||||
*/
|
||||
for ( i = 0; i < sizeof(low_primes)/sizeof(*low_primes); ++i )
|
||||
{
|
||||
p = low_primes[i];
|
||||
switch ( bi_compare( int_to_bi( p ), bi_copy( bi ) ) )
|
||||
{
|
||||
case 0:
|
||||
bi_free( bi );
|
||||
return 1;
|
||||
case 1:
|
||||
bi_free( bi );
|
||||
return 0;
|
||||
}
|
||||
if ( bi_int_mod( bi_copy( bi ), p ) == 0 )
|
||||
{
|
||||
bi_free( bi );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do the probabilistic tests. */
|
||||
bim1 = bi_int_subtract( bi_copy( bi ), 1 );
|
||||
for ( i = 0; i < certainty; ++i )
|
||||
{
|
||||
bigint a, j, jac;
|
||||
|
||||
/* Pick random test number. */
|
||||
a = bi_random( bi_copy( bi ) );
|
||||
|
||||
/* Decide whether to run the Fermat test or the Solovay-Strassen
|
||||
** test. The Fermat test is fast but lets some composite numbers
|
||||
** through. Solovay-Strassen runs slower but is more certain.
|
||||
** So the compromise here is we run the Fermat test a couple of
|
||||
** times to quickly reject most composite numbers, and then do
|
||||
** the rest of the iterations with Solovay-Strassen so nothing
|
||||
** slips through.
|
||||
*/
|
||||
if ( i < 2 && certainty >= 5 )
|
||||
{
|
||||
/* Fermat test. Note that this is not state of the art. There's a
|
||||
** class of numbers called Carmichael numbers which are composite
|
||||
** but look prime to this test - it lets them slip through no
|
||||
** matter how many reps you run. However, it's nice and fast so
|
||||
** we run it anyway to help quickly reject most of the composites.
|
||||
*/
|
||||
if ( ! bi_is_one( bi_mod_power( bi_copy( a ), bi_copy( bim1 ), bi_copy( bi ) ) ) )
|
||||
{
|
||||
bi_free( bi );
|
||||
bi_free( bim1 );
|
||||
bi_free( a );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* GCD test. This rarely hits, but we need it for Solovay-Strassen. */
|
||||
if ( ! bi_is_one( bi_gcd( bi_copy( bi ), bi_copy( a ) ) ) )
|
||||
{
|
||||
bi_free( bi );
|
||||
bi_free( bim1 );
|
||||
bi_free( a );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Solovay-Strassen test. First compute pseudo Jacobi. */
|
||||
j = bi_mod_power(
|
||||
bi_copy( a ), bi_half( bi_copy( bim1 ) ), bi_copy( bi ) );
|
||||
if ( bi_compare( bi_copy( j ), bi_copy( bim1 ) ) == 0 )
|
||||
{
|
||||
bi_free( j );
|
||||
j = bi_m1;
|
||||
}
|
||||
|
||||
/* Now compute real Jacobi. */
|
||||
jac = bi_jacobi( bi_copy( a ), bi_copy( bi ) );
|
||||
|
||||
/* If they're not equal, the number is definitely composite. */
|
||||
if ( bi_compare( j, jac ) != 0 )
|
||||
{
|
||||
bi_free( bi );
|
||||
bi_free( bim1 );
|
||||
bi_free( a );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bi_free( a );
|
||||
}
|
||||
|
||||
bi_free( bim1 );
|
||||
|
||||
bi_free( bi );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bigint
|
||||
bi_generate_prime( int bits, int certainty )
|
||||
{
|
||||
bigint bimo2, bip;
|
||||
int i, inc = 0;
|
||||
|
||||
bimo2 = bi_power( bi_2, int_to_bi( bits - 1 ) );
|
||||
for (;;)
|
||||
{
|
||||
bip = bi_add( bi_random( bi_copy( bimo2 ) ), bi_copy( bimo2 ) );
|
||||
/* By shoving the candidate numbers up to the next highest multiple
|
||||
** of six plus or minus one, we pre-eliminate all multiples of
|
||||
** two and/or three.
|
||||
*/
|
||||
switch ( bi_int_mod( bi_copy( bip ), 6 ) )
|
||||
{
|
||||
case 0: inc = 4; bip = bi_int_add( bip, 1 ); break;
|
||||
case 1: inc = 4; break;
|
||||
case 2: inc = 2; bip = bi_int_add( bip, 3 ); break;
|
||||
case 3: inc = 2; bip = bi_int_add( bip, 2 ); break;
|
||||
case 4: inc = 2; bip = bi_int_add( bip, 1 ); break;
|
||||
case 5: inc = 2; break;
|
||||
}
|
||||
/* Starting from the generated random number, check a bunch of
|
||||
** numbers in sequence. This is just to avoid calls to bi_random(),
|
||||
** which is more expensive than a simple add.
|
||||
*/
|
||||
for ( i = 0; i < 1000; ++i ) /* arbitrary */
|
||||
{
|
||||
if ( bi_is_probable_prime( bi_copy( bip ), certainty ) )
|
||||
{
|
||||
bi_free( bimo2 );
|
||||
return bip;
|
||||
}
|
||||
bip = bi_int_add( bip, inc );
|
||||
inc = 6 - inc;
|
||||
}
|
||||
/* We ran through the whole sequence and didn't find a prime.
|
||||
** Shrug, just try a different random starting point.
|
||||
*/
|
||||
bi_free( bip );
|
||||
}
|
||||
}
|
1428
src/rt/bigint/bigint_int.cpp
Normal file
1428
src/rt/bigint/bigint_int.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1069
src/rt/bigint/low_primes.h
Normal file
1069
src/rt/bigint/low_primes.h
Normal file
File diff suppressed because it is too large
Load Diff
56
src/rt/isaac/rand.h
Normal file
56
src/rt/isaac/rand.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
rand.h: definitions for a random number generator
|
||||
By Bob Jenkins, 1996, Public Domain
|
||||
MODIFIED:
|
||||
960327: Creation (addition of randinit, really)
|
||||
970719: use context, not global variables, for internal state
|
||||
980324: renamed seed to flag
|
||||
980605: recommend RANDSIZL=4 for noncryptography.
|
||||
010626: note this is public domain
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef STANDARD
|
||||
#include "standard.h"
|
||||
#endif
|
||||
|
||||
#ifndef RAND
|
||||
#define RAND
|
||||
#define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */
|
||||
#define RANDSIZ (1<<RANDSIZL)
|
||||
|
||||
/* context of random number generator */
|
||||
struct randctx
|
||||
{
|
||||
ub4 randcnt;
|
||||
ub4 randrsl[RANDSIZ];
|
||||
ub4 randmem[RANDSIZ];
|
||||
ub4 randa;
|
||||
ub4 randb;
|
||||
ub4 randc;
|
||||
};
|
||||
typedef struct randctx randctx;
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
void randinit(randctx *r, word flag);
|
||||
|
||||
void isaac(randctx *r);
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#define rand(r) \
|
||||
(!(r)->randcnt-- ? \
|
||||
(isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
|
||||
(r)->randrsl[(r)->randcnt])
|
||||
|
||||
#endif /* RAND */
|
||||
|
||||
|
134
src/rt/isaac/randport.cpp
Normal file
134
src/rt/isaac/randport.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain
|
||||
MODIFIED:
|
||||
960327: Creation (addition of randinit, really)
|
||||
970719: use context, not global variables, for internal state
|
||||
980324: make a portable version
|
||||
010626: Note this is public domain
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef STANDARD
|
||||
#include "standard.h"
|
||||
#endif
|
||||
#ifndef RAND
|
||||
#include "rand.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define ind(mm,x) ((mm)[(x>>2)&(RANDSIZ-1)])
|
||||
#define rngstep(mix,a,b,mm,m,m2,r,x) \
|
||||
{ \
|
||||
x = *m; \
|
||||
a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
|
||||
*(m++) = y = (ind(mm,x) + a + b) & 0xffffffff; \
|
||||
*(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \
|
||||
}
|
||||
|
||||
void isaac(randctx *ctx)
|
||||
{
|
||||
register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
|
||||
mm=ctx->randmem; r=ctx->randrsl;
|
||||
a = ctx->randa; b = (ctx->randb + (++ctx->randc)) & 0xffffffff;
|
||||
for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
|
||||
{
|
||||
rngstep( a<<13, a, b, mm, m, m2, r, x);
|
||||
rngstep( a>>6 , a, b, mm, m, m2, r, x);
|
||||
rngstep( a<<2 , a, b, mm, m, m2, r, x);
|
||||
rngstep( a>>16, a, b, mm, m, m2, r, x);
|
||||
}
|
||||
for (m2 = mm; m2<mend; )
|
||||
{
|
||||
rngstep( a<<13, a, b, mm, m, m2, r, x);
|
||||
rngstep( a>>6 , a, b, mm, m, m2, r, x);
|
||||
rngstep( a<<2 , a, b, mm, m, m2, r, x);
|
||||
rngstep( a>>16, a, b, mm, m, m2, r, x);
|
||||
}
|
||||
ctx->randb = b; ctx->randa = a;
|
||||
}
|
||||
|
||||
|
||||
#define mix(a,b,c,d,e,f,g,h) \
|
||||
{ \
|
||||
a^=b<<11; d+=a; b+=c; \
|
||||
b^=c>>2; e+=b; c+=d; \
|
||||
c^=d<<8; f+=c; d+=e; \
|
||||
d^=e>>16; g+=d; e+=f; \
|
||||
e^=f<<10; h+=e; f+=g; \
|
||||
f^=g>>4; a+=f; g+=h; \
|
||||
g^=h<<8; b+=g; h+=a; \
|
||||
h^=a>>9; c+=h; a+=b; \
|
||||
}
|
||||
|
||||
/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
|
||||
void randinit(randctx *ctx, word flag)
|
||||
{
|
||||
word i;
|
||||
ub4 a,b,c,d,e,f,g,h;
|
||||
ub4 *m,*r;
|
||||
ctx->randa = ctx->randb = ctx->randc = 0;
|
||||
m=ctx->randmem;
|
||||
r=ctx->randrsl;
|
||||
a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
|
||||
|
||||
for (i=0; i<4; ++i) /* scramble it */
|
||||
{
|
||||
mix(a,b,c,d,e,f,g,h);
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
/* initialize using the contents of r[] as the seed */
|
||||
for (i=0; i<RANDSIZ; i+=8)
|
||||
{
|
||||
a+=r[i ]; b+=r[i+1]; c+=r[i+2]; d+=r[i+3];
|
||||
e+=r[i+4]; f+=r[i+5]; g+=r[i+6]; h+=r[i+7];
|
||||
mix(a,b,c,d,e,f,g,h);
|
||||
m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
|
||||
m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
|
||||
}
|
||||
/* do a second pass to make all of the seed affect all of m */
|
||||
for (i=0; i<RANDSIZ; i+=8)
|
||||
{
|
||||
a+=m[i ]; b+=m[i+1]; c+=m[i+2]; d+=m[i+3];
|
||||
e+=m[i+4]; f+=m[i+5]; g+=m[i+6]; h+=m[i+7];
|
||||
mix(a,b,c,d,e,f,g,h);
|
||||
m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
|
||||
m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<RANDSIZ; i+=8)
|
||||
{
|
||||
/* fill in mm[] with messy stuff */
|
||||
mix(a,b,c,d,e,f,g,h);
|
||||
m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
|
||||
m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
|
||||
}
|
||||
}
|
||||
|
||||
isaac(ctx); /* fill in the first set of results */
|
||||
ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
|
||||
}
|
||||
|
||||
|
||||
#ifdef NEVER
|
||||
int main()
|
||||
{
|
||||
ub4 i,j;
|
||||
randctx ctx;
|
||||
ctx.randa=ctx.randb=ctx.randc=(ub4)0;
|
||||
for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0;
|
||||
randinit(&ctx, TRUE);
|
||||
for (i=0; i<2; ++i)
|
||||
{
|
||||
isaac(&ctx);
|
||||
for (j=0; j<256; ++j)
|
||||
{
|
||||
printf("%.8lx",ctx.randrsl[j]);
|
||||
if ((j&7)==7) printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
57
src/rt/isaac/standard.h
Normal file
57
src/rt/isaac/standard.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
Standard definitions and types, Bob Jenkins
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef STANDARD
|
||||
# define STANDARD
|
||||
# ifndef STDIO
|
||||
# include <stdio.h>
|
||||
# define STDIO
|
||||
# endif
|
||||
# ifndef STDDEF
|
||||
# include <stddef.h>
|
||||
# define STDDEF
|
||||
# endif
|
||||
typedef unsigned long long ub8;
|
||||
#define UB8MAXVAL 0xffffffffffffffffLL
|
||||
#define UB8BITS 64
|
||||
typedef signed long long sb8;
|
||||
#define SB8MAXVAL 0x7fffffffffffffffLL
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
#define UB4MAXVAL 0xffffffff
|
||||
typedef signed long int sb4;
|
||||
#define UB4BITS 32
|
||||
#define SB4MAXVAL 0x7fffffff
|
||||
typedef unsigned short int ub2;
|
||||
#define UB2MAXVAL 0xffff
|
||||
#define UB2BITS 16
|
||||
typedef signed short int sb2;
|
||||
#define SB2MAXVAL 0x7fff
|
||||
typedef unsigned char ub1;
|
||||
#define UB1MAXVAL 0xff
|
||||
#define UB1BITS 8
|
||||
typedef signed char sb1; /* signed 1-byte quantities */
|
||||
#define SB1MAXVAL 0x7f
|
||||
typedef int word; /* fastest type available */
|
||||
|
||||
#define bis(target,mask) ((target) |= (mask))
|
||||
#define bic(target,mask) ((target) &= ~(mask))
|
||||
#define bit(target,mask) ((target) & (mask))
|
||||
#ifndef min
|
||||
# define min(a,b) (((a)<(b)) ? (a) : (b))
|
||||
#endif /* min */
|
||||
#ifndef max
|
||||
# define max(a,b) (((a)<(b)) ? (b) : (a))
|
||||
#endif /* max */
|
||||
#ifndef align
|
||||
# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
|
||||
#endif /* align */
|
||||
#ifndef abs
|
||||
# define abs(a) (((a)>0) ? (a) : -(a))
|
||||
#endif
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define SUCCESS 0 /* 1 on VAX */
|
||||
|
||||
#endif /* STANDARD */
|
309
src/rt/memcheck.h
Normal file
309
src/rt/memcheck.h
Normal file
@ -0,0 +1,309 @@
|
||||
|
||||
/*
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the following BSD-style license applies to this one
|
||||
file (memcheck.h) only. The rest of Valgrind is licensed under the
|
||||
terms of the GNU General Public License, version 2, unless
|
||||
otherwise indicated. See the COPYING file in the source
|
||||
distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
This file is part of MemCheck, a heavyweight Valgrind tool for
|
||||
detecting memory errors.
|
||||
|
||||
Copyright (C) 2000-2009 Julian Seward. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
3. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
4. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
Notice that the above BSD-style license applies to this one file
|
||||
(memcheck.h) only. The entire rest of Valgrind is licensed under
|
||||
the terms of the GNU General Public License, version 2. See the
|
||||
COPYING file in the source distribution for details.
|
||||
|
||||
----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __MEMCHECK_H
|
||||
#define __MEMCHECK_H
|
||||
|
||||
|
||||
/* This file is for inclusion into client (your!) code.
|
||||
|
||||
You can use these macros to manipulate and query memory permissions
|
||||
inside your own programs.
|
||||
|
||||
See comment near the top of valgrind.h on how to use them.
|
||||
*/
|
||||
|
||||
#include "valgrind.h"
|
||||
|
||||
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
|
||||
This enum comprises an ABI exported by Valgrind to programs
|
||||
which use client requests. DO NOT CHANGE THE ORDER OF THESE
|
||||
ENTRIES, NOR DELETE ANY -- add new ones at the end. */
|
||||
typedef
|
||||
enum {
|
||||
VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
|
||||
VG_USERREQ__MAKE_MEM_UNDEFINED,
|
||||
VG_USERREQ__MAKE_MEM_DEFINED,
|
||||
VG_USERREQ__DISCARD,
|
||||
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
|
||||
VG_USERREQ__CHECK_MEM_IS_DEFINED,
|
||||
VG_USERREQ__DO_LEAK_CHECK,
|
||||
VG_USERREQ__COUNT_LEAKS,
|
||||
|
||||
VG_USERREQ__GET_VBITS,
|
||||
VG_USERREQ__SET_VBITS,
|
||||
|
||||
VG_USERREQ__CREATE_BLOCK,
|
||||
|
||||
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
|
||||
|
||||
/* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */
|
||||
VG_USERREQ__COUNT_LEAK_BLOCKS,
|
||||
|
||||
/* This is just for memcheck's internal use - don't use it */
|
||||
_VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
|
||||
= VG_USERREQ_TOOL_BASE('M','C') + 256
|
||||
} Vg_MemCheckClientRequest;
|
||||
|
||||
|
||||
|
||||
/* Client-code macros to manipulate the state of memory. */
|
||||
|
||||
/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
|
||||
#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__MAKE_MEM_NOACCESS, \
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Similarly, mark memory at _qzz_addr as addressable but undefined
|
||||
for _qzz_len bytes. */
|
||||
#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__MAKE_MEM_UNDEFINED, \
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Similarly, mark memory at _qzz_addr as addressable and defined
|
||||
for _qzz_len bytes. */
|
||||
#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__MAKE_MEM_DEFINED, \
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
|
||||
not altered: bytes which are addressable are marked as defined,
|
||||
but those which are not addressable are left unchanged. */
|
||||
#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Create a block-description handle. The description is an ascii
|
||||
string which is included in any messages pertaining to addresses
|
||||
within the specified memory range. Has no other effect on the
|
||||
properties of the memory range. */
|
||||
#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__CREATE_BLOCK, \
|
||||
_qzz_addr, _qzz_len, _qzz_desc, \
|
||||
0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Discard a block-description-handle. Returns 1 for an
|
||||
invalid handle, 0 for a valid handle. */
|
||||
#define VALGRIND_DISCARD(_qzz_blkindex) \
|
||||
(__extension__ ({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
|
||||
VG_USERREQ__DISCARD, \
|
||||
0, _qzz_blkindex, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
|
||||
/* Client-code macros to check the state of memory. */
|
||||
|
||||
/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
|
||||
If suitable addressibility is not established, Valgrind prints an
|
||||
error message and returns the address of the first offending byte.
|
||||
Otherwise it returns zero. */
|
||||
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,\
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Check that memory at _qzz_addr is addressable and defined for
|
||||
_qzz_len bytes. If suitable addressibility and definedness are not
|
||||
established, Valgrind prints an error message and returns the
|
||||
address of the first offending byte. Otherwise it returns zero. */
|
||||
#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__CHECK_MEM_IS_DEFINED, \
|
||||
_qzz_addr, _qzz_len, 0, 0, 0); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Use this macro to force the definedness and addressibility of an
|
||||
lvalue to be checked. If suitable addressibility and definedness
|
||||
are not established, Valgrind prints an error message and returns
|
||||
the address of the first offending byte. Otherwise it returns
|
||||
zero. */
|
||||
#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
|
||||
VALGRIND_CHECK_MEM_IS_DEFINED( \
|
||||
(volatile unsigned char *)&(__lvalue), \
|
||||
(unsigned long)(sizeof (__lvalue)))
|
||||
|
||||
|
||||
/* Do a full memory leak check (like --leak-check=full) mid-execution. */
|
||||
#define VALGRIND_DO_LEAK_CHECK \
|
||||
{unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__DO_LEAK_CHECK, \
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
|
||||
#define VALGRIND_DO_QUICK_LEAK_CHECK \
|
||||
{unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__DO_LEAK_CHECK, \
|
||||
1, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Return number of leaked, dubious, reachable and suppressed bytes found by
|
||||
all previous leak checks. They must be lvalues. */
|
||||
#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
|
||||
/* For safety on 64-bit platforms we assign the results to private
|
||||
unsigned long variables, then assign these to the lvalues the user
|
||||
specified, which works no matter what type 'leaked', 'dubious', etc
|
||||
are. We also initialise '_qzz_leaked', etc because
|
||||
VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
|
||||
defined. */ \
|
||||
{unsigned long _qzz_res; \
|
||||
unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
|
||||
unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__COUNT_LEAKS, \
|
||||
&_qzz_leaked, &_qzz_dubious, \
|
||||
&_qzz_reachable, &_qzz_suppressed, 0); \
|
||||
leaked = _qzz_leaked; \
|
||||
dubious = _qzz_dubious; \
|
||||
reachable = _qzz_reachable; \
|
||||
suppressed = _qzz_suppressed; \
|
||||
}
|
||||
|
||||
/* Return number of leaked, dubious, reachable and suppressed bytes found by
|
||||
all previous leak checks. They must be lvalues. */
|
||||
#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \
|
||||
/* For safety on 64-bit platforms we assign the results to private
|
||||
unsigned long variables, then assign these to the lvalues the user
|
||||
specified, which works no matter what type 'leaked', 'dubious', etc
|
||||
are. We also initialise '_qzz_leaked', etc because
|
||||
VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
|
||||
defined. */ \
|
||||
{unsigned long _qzz_res; \
|
||||
unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
|
||||
unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__COUNT_LEAK_BLOCKS, \
|
||||
&_qzz_leaked, &_qzz_dubious, \
|
||||
&_qzz_reachable, &_qzz_suppressed, 0); \
|
||||
leaked = _qzz_leaked; \
|
||||
dubious = _qzz_dubious; \
|
||||
reachable = _qzz_reachable; \
|
||||
suppressed = _qzz_suppressed; \
|
||||
}
|
||||
|
||||
|
||||
/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
|
||||
into the provided zzvbits array. Return values:
|
||||
0 if not running on valgrind
|
||||
1 success
|
||||
2 [previously indicated unaligned arrays; these are now allowed]
|
||||
3 if any parts of zzsrc/zzvbits are not addressable.
|
||||
The metadata is not copied in cases 0, 2 or 3 so it should be
|
||||
impossible to segfault your system by using this call.
|
||||
*/
|
||||
#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
|
||||
(__extension__({unsigned long _qzz_res; \
|
||||
char* czza = (char*)zza; \
|
||||
char* czzvbits = (char*)zzvbits; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__GET_VBITS, \
|
||||
czza, czzvbits, zznbytes, 0, 0 ); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
|
||||
from the provided zzvbits array. Return values:
|
||||
0 if not running on valgrind
|
||||
1 success
|
||||
2 [previously indicated unaligned arrays; these are now allowed]
|
||||
3 if any parts of zza/zzvbits are not addressable.
|
||||
The metadata is not copied in cases 0, 2 or 3 so it should be
|
||||
impossible to segfault your system by using this call.
|
||||
*/
|
||||
#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
|
||||
(__extension__({unsigned int _qzz_res; \
|
||||
char* czza = (char*)zza; \
|
||||
char* czzvbits = (char*)zzvbits; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
VG_USERREQ__SET_VBITS, \
|
||||
czza, czzvbits, zznbytes, 0, 0 ); \
|
||||
_qzz_res; \
|
||||
}))
|
||||
|
||||
#endif
|
||||
|
267
src/rt/rust.cpp
Normal file
267
src/rt/rust.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
#include "rust_internal.h"
|
||||
#include "util/array_list.h"
|
||||
|
||||
|
||||
// #define TRACK_ALLOCATIONS
|
||||
// For debugging, keeps track of live allocations, so you can find out
|
||||
// exactly what leaked.
|
||||
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
array_list<void *> allocation_list;
|
||||
#endif
|
||||
|
||||
rust_srv::rust_srv() :
|
||||
live_allocs(0)
|
||||
{
|
||||
}
|
||||
|
||||
rust_srv::~rust_srv()
|
||||
{
|
||||
if (live_allocs != 0) {
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"leaked memory in rust main loop (%" PRIuPTR " objects)",
|
||||
live_allocs);
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
for (size_t i = 0; i < allocation_list.size(); i++) {
|
||||
if (allocation_list[i] != NULL) {
|
||||
printf("allocation 0x%" PRIxPTR " was not freed\n",
|
||||
(uintptr_t) allocation_list[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fatal(msg, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rust_srv::log(char const *str)
|
||||
{
|
||||
printf("rt: %s\n", str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *
|
||||
rust_srv::malloc(size_t bytes)
|
||||
{
|
||||
++live_allocs;
|
||||
void * val = ::malloc(bytes);
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
allocation_list.append(val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
void *
|
||||
rust_srv::realloc(void *p, size_t bytes)
|
||||
{
|
||||
if (!p) {
|
||||
live_allocs++;
|
||||
}
|
||||
void * val = ::realloc(p, bytes);
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
if (allocation_list.replace(p, val) == NULL) {
|
||||
fatal("not in allocation_list", __FILE__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
rust_srv::free(void *p)
|
||||
{
|
||||
if (live_allocs < 1) {
|
||||
fatal("live_allocs < 1", __FILE__, __LINE__);
|
||||
}
|
||||
live_allocs--;
|
||||
::free(p);
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
if (allocation_list.replace(p, NULL) == NULL) {
|
||||
fatal("not in allocation_list", __FILE__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
rust_srv::fatal(char const *expr, char const *file, size_t line)
|
||||
{
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"fatal, '%s' failed, %s:%d",
|
||||
expr, file, (int)line);
|
||||
log(buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rust_srv *
|
||||
rust_srv::clone()
|
||||
{
|
||||
return new rust_srv();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
rust_main_loop(rust_dom *dom)
|
||||
{
|
||||
// Make sure someone is watching, to pull us out of infinite loops.
|
||||
rust_timer timer(*dom);
|
||||
|
||||
int rval;
|
||||
rust_task *task;
|
||||
|
||||
dom->log(rust_log::DOM,
|
||||
"running main-loop on domain 0x%" PRIxPTR, dom);
|
||||
dom->logptr("exit-task glue",
|
||||
dom->root_crate->get_exit_task_glue());
|
||||
|
||||
while ((task = dom->sched()) != NULL) {
|
||||
I(dom, task->running());
|
||||
|
||||
dom->log(rust_log::TASK,
|
||||
"activating task 0x%" PRIxPTR ", sp=0x%" PRIxPTR,
|
||||
(uintptr_t)task, task->rust_sp);
|
||||
|
||||
dom->interrupt_flag = 0;
|
||||
|
||||
dom->activate(task);
|
||||
|
||||
dom->log(rust_log::TASK,
|
||||
"returned from task 0x%" PRIxPTR
|
||||
" in state '%s', sp=0x%" PRIxPTR,
|
||||
(uintptr_t)task,
|
||||
dom->state_vec_name(task->state),
|
||||
task->rust_sp);
|
||||
|
||||
I(dom, task->rust_sp >= (uintptr_t) &task->stk->data[0]);
|
||||
I(dom, task->rust_sp < task->stk->limit);
|
||||
|
||||
dom->reap_dead_tasks();
|
||||
}
|
||||
|
||||
dom->log(rust_log::DOM, "finished main-loop (dom.rval = %d)", dom->rval);
|
||||
rval = dom->rval;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
struct
|
||||
command_line_args
|
||||
{
|
||||
rust_dom &dom;
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
// vec[str] passed to rust_task::start.
|
||||
rust_vec *args;
|
||||
|
||||
command_line_args(rust_dom &dom,
|
||||
int sys_argc,
|
||||
char **sys_argv)
|
||||
: dom(dom),
|
||||
argc(sys_argc),
|
||||
argv(sys_argv),
|
||||
args(NULL)
|
||||
{
|
||||
#if defined(__WIN32__)
|
||||
LPCWSTR cmdline = GetCommandLineW();
|
||||
LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
|
||||
dom.win32_require("CommandLineToArgvW", argv != NULL);
|
||||
argv = (char **) dom.malloc(sizeof(char*) * argc);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
int n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
|
||||
NULL, 0, NULL, NULL);
|
||||
dom.win32_require("WideCharToMultiByte(0)", n_chars != 0);
|
||||
argv[i] = (char *) dom.malloc(n_chars);
|
||||
n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
|
||||
argv[i], n_chars, NULL, NULL);
|
||||
dom.win32_require("WideCharToMultiByte(1)", n_chars != 0);
|
||||
}
|
||||
LocalFree(wargv);
|
||||
#endif
|
||||
size_t vec_fill = sizeof(rust_str *) * argc;
|
||||
size_t vec_alloc = next_power_of_two(sizeof(rust_vec) + vec_fill);
|
||||
void *mem = dom.malloc(vec_alloc);
|
||||
args = new (mem) rust_vec(&dom, vec_alloc, 0, NULL);
|
||||
rust_str **strs = (rust_str**) &args->data[0];
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
size_t str_fill = strlen(argv[i]) + 1;
|
||||
size_t str_alloc = next_power_of_two(sizeof(rust_str) + str_fill);
|
||||
mem = dom.malloc(str_alloc);
|
||||
strs[i] = new (mem) rust_str(&dom, str_alloc, str_fill,
|
||||
(uint8_t const *)argv[i]);
|
||||
}
|
||||
args->fill = vec_fill;
|
||||
// If the caller has a declared args array, they may drop; but
|
||||
// we don't know if they have such an array. So we pin the args
|
||||
// array here to ensure it survives to program-shutdown.
|
||||
args->ref();
|
||||
}
|
||||
|
||||
~command_line_args() {
|
||||
if (args) {
|
||||
// Drop the args we've had pinned here.
|
||||
rust_str **strs = (rust_str**) &args->data[0];
|
||||
for (int i = 0; i < argc; ++i)
|
||||
dom.free(strs[i]);
|
||||
dom.free(args);
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
dom.free(argv[i]);
|
||||
}
|
||||
dom.free(argv);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
extern "C" CDECL int
|
||||
rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
{
|
||||
rust_srv srv;
|
||||
rust_dom dom(&srv, crate);
|
||||
command_line_args args(dom, argc, argv);
|
||||
|
||||
dom.log(rust_log::DOM, "startup: %d args", args.argc);
|
||||
for (int i = 0; i < args.argc; ++i)
|
||||
dom.log(rust_log::DOM,
|
||||
"startup: arg[%d] = '%s'", i, args.argv[i]);
|
||||
|
||||
if (dom._log.is_tracing(rust_log::DWARF)) {
|
||||
rust_crate_reader rdr(&dom, crate);
|
||||
}
|
||||
|
||||
uintptr_t main_args[3] = { 0, 0, (uintptr_t)args.args };
|
||||
|
||||
dom.root_task->start(crate->get_exit_task_glue(),
|
||||
main_fn,
|
||||
(uintptr_t)&main_args,
|
||||
sizeof(main_args));
|
||||
|
||||
ret = rust_main_loop(&dom);
|
||||
}
|
||||
|
||||
#if !defined(__WIN32__)
|
||||
// Don't take down the process if the main thread exits without an
|
||||
// error.
|
||||
if (!ret)
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
49
src/rt/rust.h
Normal file
49
src/rt/rust.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef RUST_H
|
||||
#define RUST_H
|
||||
|
||||
/*
|
||||
* Include this file after you've defined the ISO C9x stdint
|
||||
* types (size_t, uint8_t, uintptr_t, etc.)
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
// 'cdecl' ABI only means anything on i386
|
||||
#ifdef __WIN32__
|
||||
#define CDECL __cdecl
|
||||
#else
|
||||
#define CDECL __attribute__((cdecl))
|
||||
#endif
|
||||
#else
|
||||
#define CDECL
|
||||
#endif
|
||||
|
||||
struct rust_srv {
|
||||
size_t live_allocs;
|
||||
|
||||
virtual void log(char const *);
|
||||
virtual void fatal(char const *, char const *, size_t);
|
||||
virtual void *malloc(size_t);
|
||||
virtual void *realloc(void *, size_t);
|
||||
virtual void free(void *);
|
||||
virtual rust_srv *clone();
|
||||
|
||||
rust_srv();
|
||||
virtual ~rust_srv();
|
||||
};
|
||||
|
||||
inline void *operator new(size_t size, rust_srv *srv)
|
||||
{
|
||||
return srv->malloc(size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* c-basic-offset: 4
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*/
|
||||
|
||||
#endif /* RUST_H */
|
129
src/rt/rust_builtin.cpp
Normal file
129
src/rt/rust_builtin.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
/* Native builtins. */
|
||||
extern "C" CDECL rust_str*
|
||||
str_alloc(rust_task *task, size_t n_bytes)
|
||||
{
|
||||
rust_dom *dom = task->dom;
|
||||
size_t alloc = next_power_of_two(sizeof(rust_str) + n_bytes);
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(2);
|
||||
return NULL;
|
||||
}
|
||||
rust_str *st = new (mem) rust_str(dom, alloc, 1, (uint8_t const *)"");
|
||||
return st;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_str*
|
||||
last_os_error(rust_task *task) {
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::TASK, "last_os_error()");
|
||||
|
||||
#if defined(__WIN32__)
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
DWORD res = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &buf, 0, NULL);
|
||||
if (!res) {
|
||||
task->fail(1);
|
||||
return NULL;
|
||||
}
|
||||
#elif defined(_GNU_SOURCE)
|
||||
char cbuf[1024];
|
||||
char *buf = strerror_r(errno, cbuf, sizeof(cbuf));
|
||||
if (!buf) {
|
||||
task->fail(1);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
char buf[1024];
|
||||
int err = strerror_r(errno, buf, sizeof(buf));
|
||||
if (err) {
|
||||
task->fail(1);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
size_t fill = strlen(buf) + 1;
|
||||
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(1);
|
||||
return NULL;
|
||||
}
|
||||
rust_str *st = new (mem) rust_str(dom, alloc, fill, (const uint8_t *)buf);
|
||||
|
||||
#ifdef __WIN32__
|
||||
LocalFree((HLOCAL)buf);
|
||||
#endif
|
||||
return st;
|
||||
}
|
||||
|
||||
extern "C" CDECL size_t
|
||||
size_of(rust_task *task, type_desc *t) {
|
||||
return t->size;
|
||||
}
|
||||
|
||||
extern "C" CDECL size_t
|
||||
align_of(rust_task *task, type_desc *t) {
|
||||
return t->align;
|
||||
}
|
||||
|
||||
extern "C" CDECL size_t
|
||||
refcount(rust_task *task, type_desc *t, size_t *v) {
|
||||
// Passed-in value has refcount 1 too high
|
||||
// because it was ref'ed while making the call.
|
||||
return (*v) - 1;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_vec*
|
||||
vec_alloc(rust_task *task, type_desc *t, size_t n_elts)
|
||||
{
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::MEM,
|
||||
"vec_alloc %" PRIdPTR " elements of size %" PRIdPTR,
|
||||
n_elts, t->size);
|
||||
size_t fill = n_elts * t->size;
|
||||
size_t alloc = next_power_of_two(sizeof(rust_vec) + fill);
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(3);
|
||||
return NULL;
|
||||
}
|
||||
rust_vec *vec = new (mem) rust_vec(dom, alloc, 0, NULL);
|
||||
return vec;
|
||||
}
|
||||
|
||||
extern "C" CDECL char const *
|
||||
str_buf(rust_task *task, rust_str *s)
|
||||
{
|
||||
return (char const *)&s->data[0];
|
||||
}
|
||||
|
||||
extern "C" CDECL void *
|
||||
vec_buf(rust_task *task, type_desc *ty, rust_vec *v)
|
||||
{
|
||||
return (void *)&v->data[0];
|
||||
}
|
||||
|
||||
extern "C" CDECL size_t
|
||||
vec_len(rust_task *task, type_desc *ty, rust_vec *v)
|
||||
{
|
||||
return v->fill;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
34
src/rt/rust_chan.cpp
Normal file
34
src/rt/rust_chan.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
#include "rust_chan.h"
|
||||
|
||||
rust_chan::rust_chan(rust_task *task, rust_port *port) :
|
||||
task(task),
|
||||
port(port),
|
||||
buffer(task->dom, port->unit_sz),
|
||||
token(this)
|
||||
{
|
||||
if (port)
|
||||
port->chans.push(this);
|
||||
}
|
||||
|
||||
rust_chan::~rust_chan()
|
||||
{
|
||||
if (port) {
|
||||
if (token.pending())
|
||||
token.withdraw();
|
||||
port->chans.swapdel(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rust_chan::disassociate()
|
||||
{
|
||||
I(task->dom, port);
|
||||
|
||||
if (token.pending())
|
||||
token.withdraw();
|
||||
|
||||
// Delete reference to the port/
|
||||
port = NULL;
|
||||
}
|
22
src/rt/rust_chan.h
Normal file
22
src/rt/rust_chan.h
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
#ifndef RUST_CHAN_H
|
||||
#define RUST_CHAN_H
|
||||
|
||||
class rust_chan : public rc_base<rust_chan>, public task_owned<rust_chan> {
|
||||
public:
|
||||
rust_chan(rust_task *task, rust_port *port);
|
||||
~rust_chan();
|
||||
|
||||
rust_task *task;
|
||||
rust_port *port;
|
||||
circ_buf buffer;
|
||||
size_t idx; // Index into port->chans.
|
||||
|
||||
// Token belonging to this chan, it will be placed into a port's
|
||||
// writers vector if we have something to send to the port.
|
||||
rust_token token;
|
||||
|
||||
void disassociate();
|
||||
};
|
||||
|
||||
#endif /* RUST_CHAN_H */
|
199
src/rt/rust_comm.cpp
Normal file
199
src/rt/rust_comm.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
template class ptr_vec<rust_token>;
|
||||
template class ptr_vec<rust_alarm>;
|
||||
template class ptr_vec<rust_chan>;
|
||||
|
||||
rust_alarm::rust_alarm(rust_task *receiver) :
|
||||
receiver(receiver)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Circular buffers.
|
||||
|
||||
circ_buf::circ_buf(rust_dom *dom, size_t unit_sz) :
|
||||
dom(dom),
|
||||
alloc(INIT_CIRC_BUF_UNITS * unit_sz),
|
||||
unit_sz(unit_sz),
|
||||
next(0),
|
||||
unread(0),
|
||||
data((uint8_t *)dom->calloc(alloc))
|
||||
{
|
||||
I(dom, unit_sz);
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"new circ_buf(alloc=%d, unread=%d) -> circ_buf=0x%" PRIxPTR,
|
||||
alloc, unread, this);
|
||||
I(dom, data);
|
||||
}
|
||||
|
||||
circ_buf::~circ_buf()
|
||||
{
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"~circ_buf 0x%" PRIxPTR,
|
||||
this);
|
||||
I(dom, data);
|
||||
// I(dom, unread == 0);
|
||||
dom->free(data);
|
||||
}
|
||||
|
||||
void
|
||||
circ_buf::transfer(void *dst)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *d = (uint8_t *)dst;
|
||||
I(dom, dst);
|
||||
for (i = 0; i < unread; i += unit_sz)
|
||||
memcpy(&d[i], &data[next + i % alloc], unit_sz);
|
||||
}
|
||||
|
||||
void
|
||||
circ_buf::push(void *src)
|
||||
{
|
||||
size_t i;
|
||||
void *tmp;
|
||||
|
||||
I(dom, src);
|
||||
I(dom, unread <= alloc);
|
||||
|
||||
/* Grow if necessary. */
|
||||
if (unread == alloc) {
|
||||
I(dom, alloc <= MAX_CIRC_BUF_SIZE);
|
||||
tmp = dom->malloc(alloc << 1);
|
||||
transfer(tmp);
|
||||
alloc <<= 1;
|
||||
dom->free(data);
|
||||
data = (uint8_t *)tmp;
|
||||
}
|
||||
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"circ buf push, unread=%d, alloc=%d, unit_sz=%d",
|
||||
unread, alloc, unit_sz);
|
||||
|
||||
I(dom, unread < alloc);
|
||||
I(dom, unread + unit_sz <= alloc);
|
||||
|
||||
i = (next + unread) % alloc;
|
||||
memcpy(&data[i], src, unit_sz);
|
||||
|
||||
dom->log(rust_log::MEM|rust_log::COMM, "pushed data at index %d", i);
|
||||
unread += unit_sz;
|
||||
}
|
||||
|
||||
void
|
||||
circ_buf::shift(void *dst)
|
||||
{
|
||||
size_t i;
|
||||
void *tmp;
|
||||
|
||||
I(dom, dst);
|
||||
I(dom, unit_sz > 0);
|
||||
I(dom, unread >= unit_sz);
|
||||
I(dom, unread <= alloc);
|
||||
I(dom, data);
|
||||
i = next;
|
||||
memcpy(dst, &data[i], unit_sz);
|
||||
dom->log(rust_log::MEM|rust_log::COMM, "shifted data from index %d", i);
|
||||
unread -= unit_sz;
|
||||
next += unit_sz;
|
||||
I(dom, next <= alloc);
|
||||
if (next == alloc)
|
||||
next = 0;
|
||||
|
||||
/* Shrink if necessary. */
|
||||
if (alloc >= INIT_CIRC_BUF_UNITS * unit_sz &&
|
||||
unread <= alloc / 4) {
|
||||
tmp = dom->malloc(alloc / 2);
|
||||
transfer(tmp);
|
||||
alloc >>= 1;
|
||||
dom->free(data);
|
||||
data = (uint8_t *)tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ports.
|
||||
|
||||
rust_port::rust_port(rust_task *task, size_t unit_sz) :
|
||||
task(task),
|
||||
unit_sz(unit_sz),
|
||||
writers(task->dom),
|
||||
chans(task->dom)
|
||||
{
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"new rust_port(task=0x%" PRIxPTR ", unit_sz=%d) -> port=0x%"
|
||||
PRIxPTR, (uintptr_t)task, unit_sz, (uintptr_t)this);
|
||||
}
|
||||
|
||||
rust_port::~rust_port()
|
||||
{
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::COMM|rust_log::MEM,
|
||||
"~rust_port 0x%" PRIxPTR,
|
||||
(uintptr_t)this);
|
||||
while (chans.length() > 0)
|
||||
chans.pop()->disassociate();
|
||||
}
|
||||
|
||||
|
||||
// Tokens.
|
||||
|
||||
rust_token::rust_token(rust_chan *chan) :
|
||||
chan(chan),
|
||||
idx(0),
|
||||
submitted(false)
|
||||
{
|
||||
}
|
||||
|
||||
rust_token::~rust_token()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
rust_token::pending() const
|
||||
{
|
||||
return submitted;
|
||||
}
|
||||
|
||||
void
|
||||
rust_token::submit()
|
||||
{
|
||||
rust_port *port = chan->port;
|
||||
rust_dom *dom = chan->task->dom;
|
||||
|
||||
I(dom, port);
|
||||
I(dom, !submitted);
|
||||
|
||||
port->writers.push(this);
|
||||
submitted = true;
|
||||
}
|
||||
|
||||
void
|
||||
rust_token::withdraw()
|
||||
{
|
||||
rust_task *task = chan->task;
|
||||
rust_port *port = chan->port;
|
||||
rust_dom *dom = task->dom;
|
||||
|
||||
I(dom, port);
|
||||
I(dom, submitted);
|
||||
|
||||
if (task->blocked())
|
||||
task->wakeup(this); // must be blocked on us (or dead)
|
||||
port->writers.swapdel(this);
|
||||
submitted = false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
63
src/rt/rust_crate.cpp
Normal file
63
src/rt/rust_crate.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
uintptr_t
|
||||
rust_crate::get_image_base() const {
|
||||
return ((uintptr_t)this + image_base_off);
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
rust_crate::get_relocation_diff() const {
|
||||
return ((uintptr_t)this - self_addr);
|
||||
}
|
||||
|
||||
activate_glue_ty
|
||||
rust_crate::get_activate_glue() const {
|
||||
return (activate_glue_ty) ((uintptr_t)this + activate_glue_off);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate::get_exit_task_glue() const {
|
||||
return ((uintptr_t)this + exit_task_glue_off);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate::get_unwind_glue() const {
|
||||
return ((uintptr_t)this + unwind_glue_off);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate::get_yield_glue() const {
|
||||
return ((uintptr_t)this + yield_glue_off);
|
||||
}
|
||||
|
||||
rust_crate::mem_area::mem_area(rust_dom *dom, uintptr_t pos, size_t sz)
|
||||
: dom(dom),
|
||||
base(pos),
|
||||
lim(pos + sz)
|
||||
{
|
||||
dom->log(rust_log::MEM, "new mem_area [0x%" PRIxPTR ",0x%" PRIxPTR "]",
|
||||
base, lim);
|
||||
}
|
||||
|
||||
rust_crate::mem_area
|
||||
rust_crate::get_debug_info(rust_dom *dom) const {
|
||||
return mem_area(dom, ((uintptr_t)this + debug_info_off),
|
||||
debug_info_sz);
|
||||
}
|
||||
|
||||
rust_crate::mem_area
|
||||
rust_crate::get_debug_abbrev(rust_dom *dom) const {
|
||||
return mem_area(dom, ((uintptr_t)this + debug_abbrev_off),
|
||||
debug_abbrev_sz);
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
306
src/rt/rust_crate_cache.cpp
Normal file
306
src/rt/rust_crate_cache.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
rust_crate_cache::lib::lib(rust_dom *dom, char const *name)
|
||||
: handle(0),
|
||||
dom(dom)
|
||||
{
|
||||
#if defined(__WIN32__)
|
||||
handle = (uintptr_t)LoadLibrary(_T(name));
|
||||
#else
|
||||
handle = (uintptr_t)dlopen(name, RTLD_LOCAL|RTLD_LAZY);
|
||||
#endif
|
||||
dom->log(rust_log::CACHE, "loaded library '%s' as 0x%" PRIxPTR,
|
||||
name, handle);
|
||||
}
|
||||
|
||||
rust_crate_cache::lib::~lib() {
|
||||
dom->log(rust_log::CACHE, "~rust_crate_cache::lib(0x%" PRIxPTR ")",
|
||||
handle);
|
||||
if (handle) {
|
||||
#if defined(__WIN32__)
|
||||
FreeLibrary((HMODULE)handle);
|
||||
#else
|
||||
dlclose((void*)handle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate_cache::lib::get_handle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
rust_crate_cache::c_sym::c_sym(rust_dom *dom, lib *library, char const *name)
|
||||
: val(0),
|
||||
library(library),
|
||||
dom(dom)
|
||||
{
|
||||
library->ref();
|
||||
uintptr_t handle = library->get_handle();
|
||||
if (handle) {
|
||||
#if defined(__WIN32__)
|
||||
val = (uintptr_t)GetProcAddress((HMODULE)handle, _T(name));
|
||||
#else
|
||||
val = (uintptr_t)dlsym((void*)handle, name);
|
||||
#endif
|
||||
dom->log(rust_log::CACHE, "resolved symbol '%s' to 0x%" PRIxPTR,
|
||||
name, val);
|
||||
} else {
|
||||
dom->log(rust_log::CACHE, "unresolved symbol '%s', null lib handle",
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
rust_crate_cache::c_sym::~c_sym() {
|
||||
dom->log(rust_log::CACHE,
|
||||
"~rust_crate_cache::c_sym(0x%" PRIxPTR ")", val);
|
||||
library->deref();
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate_cache::c_sym::get_val() {
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
rust_crate_cache::rust_sym::rust_sym(rust_dom *dom,
|
||||
rust_crate const *curr_crate,
|
||||
c_sym *crate_sym,
|
||||
char const **path)
|
||||
: val(0),
|
||||
crate_sym(crate_sym),
|
||||
dom(dom)
|
||||
{
|
||||
crate_sym->ref();
|
||||
typedef rust_crate_reader::die die;
|
||||
rust_crate const *crate = (rust_crate*)crate_sym->get_val();
|
||||
if (!crate) {
|
||||
dom->log(rust_log::CACHE,
|
||||
"failed to resolve symbol, null crate symbol");
|
||||
return;
|
||||
}
|
||||
rust_crate_reader rdr(dom, crate);
|
||||
bool found_root = false;
|
||||
bool found_leaf = false;
|
||||
for (die d = rdr.dies.first_die();
|
||||
!(found_root || d.is_null());
|
||||
d = d.next_sibling()) {
|
||||
|
||||
die t1 = d;
|
||||
die t2 = d;
|
||||
for (char const **c = crate_rel(curr_crate, path);
|
||||
(*c
|
||||
&& !t1.is_null()
|
||||
&& t1.find_child_by_name(crate_rel(curr_crate, *c), t2));
|
||||
++c, t1=t2) {
|
||||
dom->log(rust_log::DWARF|rust_log::CACHE,
|
||||
"matched die <0x%" PRIxPTR
|
||||
">, child '%s' = die<0x%" PRIxPTR ">",
|
||||
t1.off, crate_rel(curr_crate, *c), t2.off);
|
||||
found_root = found_root || true;
|
||||
if (!*(c+1) && t2.find_num_attr(DW_AT_low_pc, val)) {
|
||||
dom->log(rust_log::DWARF|rust_log::CACHE,
|
||||
"found relative address: 0x%" PRIxPTR, val);
|
||||
dom->log(rust_log::DWARF|rust_log::CACHE,
|
||||
"plus image-base 0x%" PRIxPTR,
|
||||
crate->get_image_base());
|
||||
val += crate->get_image_base();
|
||||
found_leaf = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_root || found_leaf)
|
||||
break;
|
||||
}
|
||||
if (found_leaf) {
|
||||
dom->log(rust_log::CACHE, "resolved symbol to 0x%" PRIxPTR, val);
|
||||
} else {
|
||||
dom->log(rust_log::CACHE, "failed to resolve symbol");
|
||||
}
|
||||
}
|
||||
|
||||
rust_crate_cache::rust_sym::~rust_sym() {
|
||||
dom->log(rust_log::CACHE,
|
||||
"~rust_crate_cache::rust_sym(0x%" PRIxPTR ")", val);
|
||||
crate_sym->deref();
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate_cache::rust_sym::get_val() {
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
rust_crate_cache::lib *
|
||||
rust_crate_cache::get_lib(size_t n, char const *name)
|
||||
{
|
||||
I(dom, n < crate->n_libs);
|
||||
lib *library = libs[n];
|
||||
if (!library) {
|
||||
library = new (dom) lib(dom, name);
|
||||
libs[n] = library;
|
||||
}
|
||||
return library;
|
||||
}
|
||||
|
||||
rust_crate_cache::c_sym *
|
||||
rust_crate_cache::get_c_sym(size_t n, lib *library, char const *name)
|
||||
{
|
||||
I(dom, n < crate->n_c_syms);
|
||||
c_sym *sym = c_syms[n];
|
||||
dom->log(rust_log::CACHE, "cached C symbol %s = 0x%" PRIxPTR, name, sym);
|
||||
if (!sym) {
|
||||
sym = new (dom) c_sym(dom, library, name);
|
||||
c_syms[n] = sym;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
rust_crate_cache::rust_sym *
|
||||
rust_crate_cache::get_rust_sym(size_t n,
|
||||
rust_dom *dom,
|
||||
rust_crate const *curr_crate,
|
||||
c_sym *crate_sym,
|
||||
char const **path)
|
||||
{
|
||||
I(dom, n < crate->n_rust_syms);
|
||||
rust_sym *sym = rust_syms[n];
|
||||
if (!sym) {
|
||||
sym = new (dom) rust_sym(dom, curr_crate, crate_sym, path);
|
||||
rust_syms[n] = sym;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
static inline void
|
||||
adjust_disp(uintptr_t &disp, const void *oldp, const void *newp)
|
||||
{
|
||||
if (disp) {
|
||||
disp += (uintptr_t)oldp;
|
||||
disp -= (uintptr_t)newp;
|
||||
}
|
||||
}
|
||||
|
||||
type_desc *
|
||||
rust_crate_cache::get_type_desc(size_t size,
|
||||
size_t align,
|
||||
size_t n_descs,
|
||||
type_desc const **descs)
|
||||
{
|
||||
I(dom, n_descs > 1);
|
||||
type_desc *td = NULL;
|
||||
size_t keysz = n_descs * sizeof(type_desc*);
|
||||
HASH_FIND(hh, this->type_descs, descs, keysz, td);
|
||||
if (td) {
|
||||
dom->log(rust_log::CACHE, "rust_crate_cache::get_type_desc hit");
|
||||
return td;
|
||||
}
|
||||
dom->log(rust_log::CACHE, "rust_crate_cache::get_type_desc miss");
|
||||
td = (type_desc*) dom->malloc(sizeof(type_desc) + keysz);
|
||||
if (!td)
|
||||
return NULL;
|
||||
// By convention, desc 0 is the root descriptor.
|
||||
// but we ignore the size and alignment of it and use the
|
||||
// passed-in, computed values.
|
||||
memcpy(td, descs[0], sizeof(type_desc));
|
||||
td->first_param = &td->descs[1];
|
||||
td->size = size;
|
||||
td->align = align;
|
||||
for (size_t i = 0; i < n_descs; ++i) {
|
||||
dom->log(rust_log::CACHE,
|
||||
"rust_crate_cache::descs[%" PRIdPTR "] = 0x%" PRIxPTR,
|
||||
i, descs[i]);
|
||||
td->descs[i] = descs[i];
|
||||
}
|
||||
adjust_disp(td->copy_glue_off, descs[0], td);
|
||||
adjust_disp(td->drop_glue_off, descs[0], td);
|
||||
adjust_disp(td->free_glue_off, descs[0], td);
|
||||
adjust_disp(td->mark_glue_off, descs[0], td);
|
||||
adjust_disp(td->obj_drop_glue_off, descs[0], td);
|
||||
HASH_ADD(hh, this->type_descs, descs, keysz, td);
|
||||
return td;
|
||||
}
|
||||
|
||||
rust_crate_cache::rust_crate_cache(rust_dom *dom,
|
||||
rust_crate const *crate)
|
||||
: rust_syms((rust_sym**)
|
||||
dom->calloc(sizeof(rust_sym*) * crate->n_rust_syms)),
|
||||
c_syms((c_sym**) dom->calloc(sizeof(c_sym*) * crate->n_c_syms)),
|
||||
libs((lib**) dom->calloc(sizeof(lib*) * crate->n_libs)),
|
||||
type_descs(NULL),
|
||||
crate(crate),
|
||||
dom(dom),
|
||||
idx(0)
|
||||
{
|
||||
I(dom, rust_syms);
|
||||
I(dom, c_syms);
|
||||
I(dom, libs);
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_cache::flush() {
|
||||
dom->log(rust_log::CACHE, "rust_crate_cache::flush()");
|
||||
for (size_t i = 0; i < crate->n_rust_syms; ++i) {
|
||||
rust_sym *s = rust_syms[i];
|
||||
if (s) {
|
||||
dom->log(rust_log::CACHE,
|
||||
"rust_crate_cache::flush() deref rust_sym %"
|
||||
PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
|
||||
s->deref();
|
||||
}
|
||||
rust_syms[i] = NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < crate->n_c_syms; ++i) {
|
||||
c_sym *s = c_syms[i];
|
||||
if (s) {
|
||||
dom->log(rust_log::CACHE,
|
||||
"rust_crate_cache::flush() deref c_sym %"
|
||||
PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
|
||||
s->deref();
|
||||
}
|
||||
c_syms[i] = NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < crate->n_libs; ++i) {
|
||||
lib *l = libs[i];
|
||||
if (l) {
|
||||
dom->log(rust_log::CACHE, "rust_crate_cache::flush() deref lib %"
|
||||
PRIdPTR " (rc=%" PRIdPTR ")", i, l->refcnt);
|
||||
l->deref();
|
||||
}
|
||||
libs[i] = NULL;
|
||||
}
|
||||
|
||||
while (type_descs) {
|
||||
type_desc *d = type_descs;
|
||||
HASH_DEL(type_descs, d);
|
||||
dom->log(rust_log::MEM,
|
||||
"rust_crate_cache::flush() tydesc %" PRIxPTR, d);
|
||||
dom->free(d);
|
||||
}
|
||||
}
|
||||
|
||||
rust_crate_cache::~rust_crate_cache()
|
||||
{
|
||||
flush();
|
||||
dom->free(rust_syms);
|
||||
dom->free(c_syms);
|
||||
dom->free(libs);
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
578
src/rt/rust_crate_reader.cpp
Normal file
578
src/rt/rust_crate_reader.cpp
Normal file
@ -0,0 +1,578 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
bool
|
||||
rust_crate_reader::mem_reader::is_ok()
|
||||
{
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::mem_reader::at_end()
|
||||
{
|
||||
return pos == mem.lim;
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::mem_reader::fail()
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::mem_reader::reset()
|
||||
{
|
||||
pos = mem.base;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
rust_crate_reader::mem_reader::mem_reader(rust_crate::mem_area &m)
|
||||
: mem(m),
|
||||
ok(true),
|
||||
pos(m.base)
|
||||
{}
|
||||
|
||||
size_t
|
||||
rust_crate_reader::mem_reader::tell_abs()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t
|
||||
rust_crate_reader::mem_reader::tell_off()
|
||||
{
|
||||
return pos - mem.base;
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::mem_reader::seek_abs(uintptr_t p)
|
||||
{
|
||||
if (!ok || p < mem.base || p >= mem.lim)
|
||||
ok = false;
|
||||
else
|
||||
pos = p;
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::mem_reader::seek_off(uintptr_t p)
|
||||
{
|
||||
seek_abs(p + mem.base);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
rust_crate_reader::mem_reader::adv_zstr(size_t sz)
|
||||
{
|
||||
sz = 0;
|
||||
while (ok) {
|
||||
char c;
|
||||
get(c);
|
||||
++sz;
|
||||
if (c == '\0')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::mem_reader::get_zstr(char const *&c, size_t &sz)
|
||||
{
|
||||
if (!ok)
|
||||
return false;
|
||||
c = (char const *)(pos);
|
||||
return adv_zstr(sz);
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::mem_reader::adv(size_t amt)
|
||||
{
|
||||
if (pos < mem.base
|
||||
|| pos >= mem.lim
|
||||
|| pos + amt > mem.lim)
|
||||
ok = false;
|
||||
if (!ok)
|
||||
return;
|
||||
// mem.dom->log(rust_log::MEM, "adv %d bytes", amt);
|
||||
pos += amt;
|
||||
ok &= !at_end();
|
||||
I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
|
||||
}
|
||||
|
||||
|
||||
rust_crate_reader::abbrev::abbrev(rust_dom *dom,
|
||||
uintptr_t body_off,
|
||||
size_t body_sz,
|
||||
uintptr_t tag,
|
||||
uint8_t has_children) :
|
||||
dom(dom),
|
||||
body_off(body_off),
|
||||
tag(tag),
|
||||
has_children(has_children),
|
||||
idx(0)
|
||||
{}
|
||||
|
||||
|
||||
rust_crate_reader::abbrev_reader::abbrev_reader
|
||||
(rust_crate::mem_area &abbrev_mem)
|
||||
: mem_reader(abbrev_mem),
|
||||
abbrevs(abbrev_mem.dom)
|
||||
{
|
||||
rust_dom *dom = mem.dom;
|
||||
while (is_ok()) {
|
||||
|
||||
// dom->log(rust_log::DWARF, "reading new abbrev at 0x%" PRIxPTR,
|
||||
// tell_off());
|
||||
|
||||
uintptr_t idx, tag;
|
||||
uint8_t has_children;
|
||||
get_uleb(idx);
|
||||
get_uleb(tag);
|
||||
get(has_children);
|
||||
|
||||
uintptr_t attr, form;
|
||||
size_t body_off = tell_off();
|
||||
while (is_ok() && step_attr_form_pair(attr, form));
|
||||
|
||||
// dom->log(rust_log::DWARF,
|
||||
// "finished scanning attr/form pairs, pos=0x%"
|
||||
// PRIxPTR ", lim=0x%" PRIxPTR ", is_ok=%d, at_end=%d",
|
||||
// pos, mem.lim, is_ok(), at_end());
|
||||
|
||||
if (is_ok() || at_end()) {
|
||||
dom->log(rust_log::DWARF, "read abbrev: %" PRIdPTR, idx);
|
||||
I(dom, idx = abbrevs.length() + 1);
|
||||
abbrevs.push(new (dom) abbrev(dom, body_off,
|
||||
tell_off() - body_off,
|
||||
tag, has_children));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rust_crate_reader::abbrev *
|
||||
rust_crate_reader::abbrev_reader::get_abbrev(size_t i) {
|
||||
i -= 1;
|
||||
if (i < abbrevs.length())
|
||||
return abbrevs[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::abbrev_reader::step_attr_form_pair(uintptr_t &attr,
|
||||
uintptr_t &form)
|
||||
{
|
||||
attr = 0;
|
||||
form = 0;
|
||||
// mem.dom->log(rust_log::DWARF, "reading attr/form pair at 0x%" PRIxPTR,
|
||||
// tell_off());
|
||||
get_uleb(attr);
|
||||
get_uleb(form);
|
||||
// mem.dom->log(rust_log::DWARF, "attr 0x%" PRIxPTR ", form 0x%" PRIxPTR,
|
||||
// attr, form);
|
||||
return ! (attr == 0 && form == 0);
|
||||
}
|
||||
rust_crate_reader::abbrev_reader::~abbrev_reader() {
|
||||
while (abbrevs.length()) {
|
||||
delete abbrevs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
rust_crate_reader::attr::is_numeric() const
|
||||
{
|
||||
switch (form) {
|
||||
case DW_FORM_ref_addr:
|
||||
case DW_FORM_addr:
|
||||
case DW_FORM_data4:
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_flag:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::attr::is_string() const
|
||||
{
|
||||
return form == DW_FORM_string;
|
||||
}
|
||||
|
||||
size_t
|
||||
rust_crate_reader::attr::get_ssz(rust_dom *dom) const
|
||||
{
|
||||
I(dom, is_string());
|
||||
return val.str.sz;
|
||||
}
|
||||
|
||||
char const *
|
||||
rust_crate_reader::attr::get_str(rust_dom *dom) const
|
||||
{
|
||||
I(dom, is_string());
|
||||
return val.str.s;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_crate_reader::attr::get_num(rust_dom *dom) const
|
||||
{
|
||||
I(dom, is_numeric());
|
||||
return val.num;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::attr::is_unknown() const {
|
||||
return !(is_numeric() || is_string());
|
||||
}
|
||||
|
||||
rust_crate_reader::rdr_sess::rdr_sess(die_reader *rdr) : rdr(rdr)
|
||||
{
|
||||
I(rdr->mem.dom, !rdr->in_use);
|
||||
rdr->in_use = true;
|
||||
}
|
||||
|
||||
rust_crate_reader::rdr_sess::~rdr_sess()
|
||||
{
|
||||
rdr->in_use = false;
|
||||
}
|
||||
|
||||
rust_crate_reader::die::die(die_reader *rdr, uintptr_t off)
|
||||
: rdr(rdr),
|
||||
off(off),
|
||||
using_rdr(false)
|
||||
{
|
||||
rust_dom *dom = rdr->mem.dom;
|
||||
rdr_sess use(rdr);
|
||||
|
||||
rdr->reset();
|
||||
rdr->seek_off(off);
|
||||
if (!rdr->is_ok()) {
|
||||
ab = NULL;
|
||||
return;
|
||||
}
|
||||
size_t ab_idx;
|
||||
rdr->get_uleb(ab_idx);
|
||||
if (!ab_idx) {
|
||||
ab = NULL;
|
||||
dom->log(rust_log::DWARF, "DIE <0x%" PRIxPTR "> (null)", off);
|
||||
} else {
|
||||
ab = rdr->abbrevs.get_abbrev(ab_idx);
|
||||
dom->log(rust_log::DWARF, "DIE <0x%" PRIxPTR "> abbrev 0x%"
|
||||
PRIxPTR, off, ab_idx);
|
||||
dom->log(rust_log::DWARF, " tag 0x%x, has children: %d",
|
||||
ab->tag, ab->has_children);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::is_null() const
|
||||
{
|
||||
return ab == NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::has_children() const
|
||||
{
|
||||
return (!is_null()) && ab->has_children;
|
||||
}
|
||||
|
||||
dw_tag
|
||||
rust_crate_reader::die::tag() const
|
||||
{
|
||||
if (is_null())
|
||||
return (dw_tag) (-1);
|
||||
return (dw_tag) ab->tag;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::start_attrs() const
|
||||
{
|
||||
if (is_null())
|
||||
return false;
|
||||
rdr->reset();
|
||||
rdr->seek_off(off + 1);
|
||||
rdr->abbrevs.reset();
|
||||
rdr->abbrevs.seek_off(ab->body_off);
|
||||
return rdr->is_ok();
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::step_attr(attr &a) const
|
||||
{
|
||||
uintptr_t ai, fi;
|
||||
if (rdr->abbrevs.step_attr_form_pair(ai, fi) && rdr->is_ok()) {
|
||||
a.at = (dw_at)ai;
|
||||
a.form = (dw_form)fi;
|
||||
|
||||
uint32_t u32;
|
||||
uint8_t u8;
|
||||
|
||||
switch (a.form) {
|
||||
case DW_FORM_string:
|
||||
return rdr->get_zstr(a.val.str.s, a.val.str.sz);
|
||||
break;
|
||||
|
||||
case DW_FORM_ref_addr:
|
||||
I(rdr->mem.dom, sizeof(uintptr_t) == 4);
|
||||
case DW_FORM_addr:
|
||||
case DW_FORM_data4:
|
||||
rdr->get(u32);
|
||||
a.val.num = (uintptr_t)u32;
|
||||
return rdr->is_ok() || rdr->at_end();
|
||||
break;
|
||||
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_flag:
|
||||
rdr->get(u8);
|
||||
a.val.num = u8;
|
||||
return rdr->is_ok() || rdr->at_end();
|
||||
break;
|
||||
|
||||
case DW_FORM_block1:
|
||||
rdr->get(u8);
|
||||
rdr->adv(u8);
|
||||
return rdr->is_ok() || rdr->at_end();
|
||||
break;
|
||||
|
||||
default:
|
||||
rdr->mem.dom->log(rust_log::DWARF, " unknown dwarf form: 0x%"
|
||||
PRIxPTR, a.form);
|
||||
rdr->fail();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::find_str_attr(dw_at at, char const *&c)
|
||||
{
|
||||
rdr_sess use(rdr);
|
||||
if (is_null())
|
||||
return false;
|
||||
if (start_attrs()) {
|
||||
attr a;
|
||||
while (step_attr(a)) {
|
||||
if (a.at == at && a.is_string()) {
|
||||
c = a.get_str(rdr->mem.dom);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::find_num_attr(dw_at at, uintptr_t &n)
|
||||
{
|
||||
rdr_sess use(rdr);
|
||||
if (is_null())
|
||||
return false;
|
||||
if (start_attrs()) {
|
||||
attr a;
|
||||
while (step_attr(a)) {
|
||||
if (a.at == at && a.is_numeric()) {
|
||||
n = a.get_num(rdr->mem.dom);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::is_transparent()
|
||||
{
|
||||
// "semantically transparent" DIEs are those with
|
||||
// children that serve to structure the tree but have
|
||||
// tags that don't reflect anything in the rust-module
|
||||
// name hierarchy.
|
||||
switch (tag()) {
|
||||
case DW_TAG_compile_unit:
|
||||
case DW_TAG_lexical_block:
|
||||
return (has_children());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::find_child_by_name(char const *c,
|
||||
die &child,
|
||||
bool exact)
|
||||
{
|
||||
rust_dom *dom = rdr->mem.dom;
|
||||
I(dom, has_children());
|
||||
I(dom, !is_null());
|
||||
|
||||
for (die ch = next(); !ch.is_null(); ch = ch.next_sibling()) {
|
||||
char const *ac;
|
||||
if (!exact && ch.is_transparent()) {
|
||||
if (ch.find_child_by_name(c, child, exact)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ch.find_str_attr(DW_AT_name, ac)) {
|
||||
if (strcmp(ac, c) == 0) {
|
||||
child = ch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_crate_reader::die::find_child_by_tag(dw_tag tag, die &child)
|
||||
{
|
||||
rust_dom *dom = rdr->mem.dom;
|
||||
I(dom, has_children());
|
||||
I(dom, !is_null());
|
||||
|
||||
for (child = next(); !child.is_null();
|
||||
child = child.next_sibling()) {
|
||||
if (child.tag() == tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
rust_crate_reader::die
|
||||
rust_crate_reader::die::next() const
|
||||
{
|
||||
rust_dom *dom = rdr->mem.dom;
|
||||
|
||||
if (is_null()) {
|
||||
rdr->seek_off(off + 1);
|
||||
return die(rdr, rdr->tell_off());
|
||||
}
|
||||
|
||||
{
|
||||
rdr_sess use(rdr);
|
||||
if (start_attrs()) {
|
||||
attr a;
|
||||
while (step_attr(a)) {
|
||||
I(dom, !(a.is_numeric() && a.is_string()));
|
||||
if (a.is_numeric())
|
||||
dom->log(rust_log::DWARF, " attr num: 0x%"
|
||||
PRIxPTR, a.get_num(dom));
|
||||
else if (a.is_string())
|
||||
dom->log(rust_log::DWARF, " attr str: %s",
|
||||
a.get_str(dom));
|
||||
else
|
||||
dom->log(rust_log::DWARF, " attr ??:");
|
||||
}
|
||||
}
|
||||
}
|
||||
return die(rdr, rdr->tell_off());
|
||||
}
|
||||
|
||||
rust_crate_reader::die
|
||||
rust_crate_reader::die::next_sibling() const
|
||||
{
|
||||
// FIXME: use DW_AT_sibling, when present.
|
||||
if (has_children()) {
|
||||
// rdr->mem.dom->log(rust_log::DWARF, "+++ children of die 0x%"
|
||||
// PRIxPTR, off);
|
||||
die child = next();
|
||||
while (!child.is_null())
|
||||
child = child.next_sibling();
|
||||
// rdr->mem.dom->log(rust_log::DWARF, "--- children of die 0x%"
|
||||
// PRIxPTR, off);
|
||||
return child.next();
|
||||
} else {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rust_crate_reader::die
|
||||
rust_crate_reader::die_reader::first_die()
|
||||
{
|
||||
reset();
|
||||
seek_off(cu_base
|
||||
+ sizeof(dwarf_vers)
|
||||
+ sizeof(cu_abbrev_off)
|
||||
+ sizeof(sizeof_addr));
|
||||
return die(this, tell_off());
|
||||
}
|
||||
|
||||
void
|
||||
rust_crate_reader::die_reader::dump()
|
||||
{
|
||||
rust_dom *dom = mem.dom;
|
||||
die d = first_die();
|
||||
while (!d.is_null())
|
||||
d = d.next_sibling();
|
||||
I(dom, d.is_null());
|
||||
I(dom, d.off == mem.lim - mem.base);
|
||||
}
|
||||
|
||||
|
||||
rust_crate_reader::die_reader::die_reader(rust_crate::mem_area &die_mem,
|
||||
abbrev_reader &abbrevs)
|
||||
: mem_reader(die_mem),
|
||||
abbrevs(abbrevs),
|
||||
cu_unit_length(0),
|
||||
cu_base(0),
|
||||
dwarf_vers(0),
|
||||
cu_abbrev_off(0),
|
||||
sizeof_addr(0),
|
||||
in_use(false)
|
||||
{
|
||||
rust_dom *dom = mem.dom;
|
||||
|
||||
rdr_sess use(this);
|
||||
|
||||
get(cu_unit_length);
|
||||
cu_base = tell_off();
|
||||
|
||||
get(dwarf_vers);
|
||||
get(cu_abbrev_off);
|
||||
get(sizeof_addr);
|
||||
|
||||
if (is_ok()) {
|
||||
dom->log(rust_log::DWARF, "new root CU at 0x%" PRIxPTR, die_mem.base);
|
||||
dom->log(rust_log::DWARF, "CU unit length: %" PRId32, cu_unit_length);
|
||||
dom->log(rust_log::DWARF, "dwarf version: %" PRId16, dwarf_vers);
|
||||
dom->log(rust_log::DWARF, "CU abbrev off: %" PRId32, cu_abbrev_off);
|
||||
dom->log(rust_log::DWARF, "size of address: %" PRId8, sizeof_addr);
|
||||
I(dom, sizeof_addr == sizeof(uintptr_t));
|
||||
I(dom, dwarf_vers >= 2);
|
||||
I(dom, cu_base + cu_unit_length == die_mem.lim - die_mem.base);
|
||||
} else {
|
||||
dom->log(rust_log::DWARF, "failed to read root CU header");
|
||||
}
|
||||
}
|
||||
|
||||
rust_crate_reader::die_reader::~die_reader() {
|
||||
}
|
||||
|
||||
|
||||
rust_crate_reader::rust_crate_reader(rust_dom *dom,
|
||||
rust_crate const *crate)
|
||||
: dom(dom),
|
||||
crate(crate),
|
||||
abbrev_mem(crate->get_debug_abbrev(dom)),
|
||||
abbrevs(abbrev_mem),
|
||||
die_mem(crate->get_debug_info(dom)),
|
||||
dies(die_mem, abbrevs)
|
||||
{
|
||||
dom->log(rust_log::MEM, "crate_reader on crate: 0x%" PRIxPTR, this);
|
||||
dom->log(rust_log::MEM, "debug_abbrev: 0x%" PRIxPTR, abbrev_mem.base);
|
||||
dom->log(rust_log::MEM, "debug_info: 0x%" PRIxPTR, die_mem.base);
|
||||
// For now, perform diagnostics only.
|
||||
dies.dump();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
271
src/rt/rust_dom.cpp
Normal file
271
src/rt/rust_dom.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "rust_internal.h"
|
||||
|
||||
template class ptr_vec<rust_task>;
|
||||
|
||||
rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) :
|
||||
interrupt_flag(0),
|
||||
root_crate(root_crate),
|
||||
_log(srv, this),
|
||||
srv(srv),
|
||||
running_tasks(this),
|
||||
blocked_tasks(this),
|
||||
dead_tasks(this),
|
||||
caches(this),
|
||||
root_task(NULL),
|
||||
curr_task(NULL),
|
||||
rval(0)
|
||||
{
|
||||
logptr("new dom", (uintptr_t)this);
|
||||
memset(&rctx, 0, sizeof(rctx));
|
||||
|
||||
#ifdef __WIN32__
|
||||
{
|
||||
HCRYPTPROV hProv;
|
||||
win32_require
|
||||
(_T("CryptAcquireContext"),
|
||||
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
|
||||
win32_require
|
||||
(_T("CryptGenRandom"),
|
||||
CryptGenRandom(hProv, sizeof(rctx.randrsl),
|
||||
(BYTE*)(&rctx.randrsl)));
|
||||
win32_require
|
||||
(_T("CryptReleaseContext"),
|
||||
CryptReleaseContext(hProv, 0));
|
||||
}
|
||||
#else
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
I(this, fd > 0);
|
||||
I(this, read(fd, (void*) &rctx.randrsl, sizeof(rctx.randrsl))
|
||||
== sizeof(rctx.randrsl));
|
||||
I(this, close(fd) == 0);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, 1024 * 1024);
|
||||
pthread_attr_setdetachstate(&attr, true);
|
||||
#endif
|
||||
randinit(&rctx, 1);
|
||||
|
||||
root_task = new (this) rust_task(this, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
del_all_tasks(rust_dom *dom, ptr_vec<rust_task> *v) {
|
||||
I(dom, v);
|
||||
while (v->length()) {
|
||||
dom->log(rust_log::TASK, "deleting task %" PRIdPTR, v->length() - 1);
|
||||
delete v->pop();
|
||||
}
|
||||
}
|
||||
|
||||
rust_dom::~rust_dom() {
|
||||
log(rust_log::TASK, "deleting all running tasks");
|
||||
del_all_tasks(this, &running_tasks);
|
||||
log(rust_log::TASK, "deleting all blocked tasks");
|
||||
del_all_tasks(this, &blocked_tasks);
|
||||
log(rust_log::TASK, "deleting all dead tasks");
|
||||
del_all_tasks(this, &dead_tasks);
|
||||
#ifndef __WIN32__
|
||||
pthread_attr_destroy(&attr);
|
||||
#endif
|
||||
while (caches.length())
|
||||
delete caches.pop();
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::activate(rust_task *task) {
|
||||
curr_task = task;
|
||||
root_crate->get_activate_glue()(task);
|
||||
curr_task = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::log(uint32_t type_bits, char const *fmt, ...) {
|
||||
char buf[256];
|
||||
if (_log.is_tracing(type_bits)) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
_log.trace_ln(type_bits, buf);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
rust_log &
|
||||
rust_dom::get_log() {
|
||||
return _log;
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::logptr(char const *msg, uintptr_t ptrval) {
|
||||
log(rust_log::MEM, "%s 0x%" PRIxPTR, msg, ptrval);
|
||||
}
|
||||
|
||||
template<typename T> void
|
||||
rust_dom::logptr(char const *msg, T* ptrval) {
|
||||
log(rust_log::MEM, "%s 0x%" PRIxPTR, msg, (uintptr_t)ptrval);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rust_dom::fail() {
|
||||
log(rust_log::DOM, "domain 0x%" PRIxPTR " root task failed", this);
|
||||
I(this, rval == 0);
|
||||
rval = 1;
|
||||
}
|
||||
|
||||
void *
|
||||
rust_dom::malloc(size_t sz) {
|
||||
void *p = srv->malloc(sz);
|
||||
I(this, p);
|
||||
log(rust_log::MEM, "rust_dom::malloc(%d) -> 0x%" PRIxPTR,
|
||||
sz, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
rust_dom::calloc(size_t sz) {
|
||||
void *p = this->malloc(sz);
|
||||
memset(p, 0, sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
rust_dom::realloc(void *p, size_t sz) {
|
||||
void *p1 = srv->realloc(p, sz);
|
||||
I(this, p1);
|
||||
log(rust_log::MEM, "rust_dom::realloc(0x%" PRIxPTR ", %d) -> 0x%" PRIxPTR,
|
||||
p, sz, p1);
|
||||
return p1;
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::free(void *p) {
|
||||
log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", p);
|
||||
I(this, p);
|
||||
srv->free(p);
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
void
|
||||
rust_dom::win32_require(LPCTSTR fn, BOOL ok) {
|
||||
if (!ok) {
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &buf, 0, NULL );
|
||||
log(rust_log::ERR, "%s failed with error %ld: %s", fn, err, buf);
|
||||
LocalFree((HLOCAL)buf);
|
||||
I(this, ok);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t
|
||||
rust_dom::n_live_tasks()
|
||||
{
|
||||
return running_tasks.length() + blocked_tasks.length();
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task)
|
||||
{
|
||||
log(rust_log::MEM|rust_log::TASK,
|
||||
"adding task 0x%" PRIxPTR " in state '%s' to vec 0x%" PRIxPTR,
|
||||
(uintptr_t)task, state_vec_name(v), (uintptr_t)v);
|
||||
v->push(task);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rust_dom::remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task)
|
||||
{
|
||||
log(rust_log::MEM|rust_log::TASK,
|
||||
"removing task 0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR,
|
||||
(uintptr_t)task, state_vec_name(v), (uintptr_t)v);
|
||||
I(this, (*v)[task->idx] == task);
|
||||
v->swapdel(task);
|
||||
}
|
||||
|
||||
const char *
|
||||
rust_dom::state_vec_name(ptr_vec<rust_task> *v)
|
||||
{
|
||||
if (v == &running_tasks)
|
||||
return "running";
|
||||
if (v == &blocked_tasks)
|
||||
return "blocked";
|
||||
I(this, v == &dead_tasks);
|
||||
return "dead";
|
||||
}
|
||||
|
||||
void
|
||||
rust_dom::reap_dead_tasks()
|
||||
{
|
||||
for (size_t i = 0; i < dead_tasks.length(); ) {
|
||||
rust_task *t = dead_tasks[i];
|
||||
if (t == root_task || t->refcnt == 0) {
|
||||
I(this, !t->waiting_tasks.length());
|
||||
dead_tasks.swapdel(t);
|
||||
log(rust_log::TASK,
|
||||
"deleting unreferenced dead task 0x%" PRIxPTR, t);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
rust_task *
|
||||
rust_dom::sched()
|
||||
{
|
||||
I(this, this);
|
||||
// FIXME: in the face of failing tasks, this is not always right.
|
||||
// I(this, n_live_tasks() > 0);
|
||||
if (running_tasks.length() > 0) {
|
||||
size_t i = rand(&rctx);
|
||||
i %= running_tasks.length();
|
||||
return (rust_task *)running_tasks[i];
|
||||
}
|
||||
log(rust_log::DOM|rust_log::TASK,
|
||||
"no schedulable tasks");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rust_crate_cache *
|
||||
rust_dom::get_cache(rust_crate const *crate) {
|
||||
log(rust_log::CACHE,
|
||||
"looking for crate-cache for crate 0x%" PRIxPTR, crate);
|
||||
rust_crate_cache *cache = NULL;
|
||||
for (size_t i = 0; i < caches.length(); ++i) {
|
||||
rust_crate_cache *c = caches[i];
|
||||
if (c->crate == crate) {
|
||||
cache = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cache) {
|
||||
log(rust_log::CACHE,
|
||||
"making new crate-cache for crate 0x%" PRIxPTR, crate);
|
||||
cache = new (this) rust_crate_cache(this, crate);
|
||||
caches.push(cache);
|
||||
}
|
||||
cache->ref();
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 70;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
198
src/rt/rust_dwarf.h
Normal file
198
src/rt/rust_dwarf.h
Normal file
@ -0,0 +1,198 @@
|
||||
#ifndef RUST_DWARF_H
|
||||
#define RUST_DWARF_H
|
||||
|
||||
enum
|
||||
dw_form
|
||||
{
|
||||
DW_FORM_addr = 0x01,
|
||||
DW_FORM_block2 = 0x03,
|
||||
DW_FORM_block4 = 0x04,
|
||||
DW_FORM_data2 = 0x05,
|
||||
DW_FORM_data4 = 0x06,
|
||||
DW_FORM_data8 = 0x07,
|
||||
DW_FORM_string = 0x08,
|
||||
DW_FORM_block = 0x09,
|
||||
DW_FORM_block1 = 0x0a,
|
||||
DW_FORM_data1 = 0x0b,
|
||||
DW_FORM_flag = 0x0c,
|
||||
DW_FORM_sdata = 0x0d,
|
||||
DW_FORM_strp = 0x0e,
|
||||
DW_FORM_udata = 0x0f,
|
||||
DW_FORM_ref_addr = 0x10,
|
||||
DW_FORM_ref1 = 0x11,
|
||||
DW_FORM_ref2 = 0x12,
|
||||
DW_FORM_ref4 = 0x13,
|
||||
DW_FORM_ref8 = 0x14,
|
||||
DW_FORM_ref_udata = 0x15,
|
||||
DW_FORM_indirect = 0x16
|
||||
};
|
||||
|
||||
enum
|
||||
dw_at
|
||||
{
|
||||
DW_AT_sibling = 0x01,
|
||||
DW_AT_location = 0x02,
|
||||
DW_AT_name = 0x03,
|
||||
DW_AT_ordering = 0x09,
|
||||
DW_AT_byte_size = 0x0b,
|
||||
DW_AT_bit_offset = 0x0c,
|
||||
DW_AT_bit_size = 0x0d,
|
||||
DW_AT_stmt_list = 0x10,
|
||||
DW_AT_low_pc = 0x11,
|
||||
DW_AT_high_pc = 0x12,
|
||||
DW_AT_language = 0x13,
|
||||
DW_AT_discr = 0x15,
|
||||
DW_AT_discr_value = 0x16,
|
||||
DW_AT_visibility = 0x17,
|
||||
DW_AT_import = 0x18,
|
||||
DW_AT_string_length = 0x19,
|
||||
DW_AT_common_reference = 0x1a,
|
||||
DW_AT_comp_dir = 0x1b,
|
||||
DW_AT_const_value = 0x1c,
|
||||
DW_AT_containing_type = 0x1d,
|
||||
DW_AT_default_value = 0x1e,
|
||||
DW_AT_inline = 0x20,
|
||||
DW_AT_is_optional = 0x21,
|
||||
DW_AT_lower_bound = 0x22,
|
||||
DW_AT_producer = 0x25,
|
||||
DW_AT_prototyped = 0x27,
|
||||
DW_AT_return_addr = 0x2a,
|
||||
DW_AT_start_scope = 0x2c,
|
||||
DW_AT_bit_stride = 0x2e,
|
||||
DW_AT_upper_bound = 0x2f,
|
||||
DW_AT_abstract_origin = 0x31,
|
||||
DW_AT_accessibility = 0x32,
|
||||
DW_AT_address_class = 0x33,
|
||||
DW_AT_artificial = 0x34,
|
||||
DW_AT_base_types = 0x35,
|
||||
DW_AT_calling_convention = 0x36,
|
||||
DW_AT_count = 0x37,
|
||||
DW_AT_data_member_location = 0x38,
|
||||
DW_AT_decl_column = 0x39,
|
||||
DW_AT_decl_file = 0x3a,
|
||||
DW_AT_decl_line = 0x3b,
|
||||
DW_AT_declaration = 0x3c,
|
||||
DW_AT_discr_list = 0x3d,
|
||||
DW_AT_encoding = 0x3e,
|
||||
DW_AT_external = 0x3f,
|
||||
DW_AT_frame_base = 0x40,
|
||||
DW_AT_friend = 0x41,
|
||||
DW_AT_identifier_case = 0x42,
|
||||
DW_AT_macro_info = 0x43,
|
||||
DW_AT_namelist_item = 0x44,
|
||||
DW_AT_priority = 0x45,
|
||||
DW_AT_segment = 0x46,
|
||||
DW_AT_specification = 0x47,
|
||||
DW_AT_static_link = 0x48,
|
||||
DW_AT_type = 0x49,
|
||||
DW_AT_use_location = 0x4a,
|
||||
DW_AT_variable_parameter = 0x4b,
|
||||
DW_AT_virtuality = 0x4c,
|
||||
DW_AT_vtable_elem_location = 0x4d,
|
||||
DW_AT_allocated = 0x4e,
|
||||
DW_AT_associated = 0x4f,
|
||||
DW_AT_data_location = 0x50,
|
||||
DW_AT_byte_stride = 0x51,
|
||||
DW_AT_entry_pc = 0x52,
|
||||
DW_AT_use_UTF8 = 0x53,
|
||||
DW_AT_extension = 0x54,
|
||||
DW_AT_ranges = 0x55,
|
||||
DW_AT_trampoline = 0x56,
|
||||
DW_AT_call_column = 0x57,
|
||||
DW_AT_call_file = 0x58,
|
||||
DW_AT_call_line = 0x59,
|
||||
DW_AT_description = 0x5a,
|
||||
DW_AT_binary_scale = 0x5b,
|
||||
DW_AT_decimal_scale = 0x5c,
|
||||
DW_AT_small = 0x5d,
|
||||
DW_AT_decimal_sign = 0x5e,
|
||||
DW_AT_digit_count = 0x5f,
|
||||
DW_AT_picture_string = 0x60,
|
||||
DW_AT_mutable = 0x61,
|
||||
DW_AT_threads_scaled = 0x62,
|
||||
DW_AT_explicit = 0x63,
|
||||
DW_AT_object_pointer = 0x64,
|
||||
DW_AT_endianity = 0x65,
|
||||
DW_AT_elemental = 0x66,
|
||||
DW_AT_pure = 0x67,
|
||||
DW_AT_recursive = 0x68,
|
||||
DW_AT_lo_user = 0x2000,
|
||||
DW_AT_hi_user = 0x3fff
|
||||
};
|
||||
|
||||
enum
|
||||
dw_tag
|
||||
{
|
||||
DW_TAG_array_type = 0x01,
|
||||
DW_TAG_class_type = 0x02,
|
||||
DW_TAG_entry_point = 0x03,
|
||||
DW_TAG_enumeration_type = 0x04,
|
||||
DW_TAG_formal_parameter = 0x05,
|
||||
DW_TAG_imported_declaration = 0x08,
|
||||
DW_TAG_label = 0x0a,
|
||||
DW_TAG_lexical_block = 0x0b,
|
||||
DW_TAG_member = 0x0d,
|
||||
DW_TAG_pointer_type = 0x0f,
|
||||
DW_TAG_reference_type = 0x10,
|
||||
DW_TAG_compile_unit = 0x11,
|
||||
DW_TAG_string_type = 0x12,
|
||||
DW_TAG_structure_type = 0x13,
|
||||
DW_TAG_subroutine_type = 0x15,
|
||||
DW_TAG_typedef = 0x16,
|
||||
DW_TAG_union_type = 0x17,
|
||||
DW_TAG_unspecified_parameters = 0x18,
|
||||
DW_TAG_variant = 0x19,
|
||||
DW_TAG_common_block = 0x1a,
|
||||
DW_TAG_common_inclusion = 0x1b,
|
||||
DW_TAG_inheritance = 0x1c,
|
||||
DW_TAG_inlined_subroutine = 0x1d,
|
||||
DW_TAG_module = 0x1e,
|
||||
DW_TAG_ptr_to_member_type = 0x1f,
|
||||
DW_TAG_set_type = 0x20,
|
||||
DW_TAG_subrange_type = 0x21,
|
||||
DW_TAG_with_stmt = 0x22,
|
||||
DW_TAG_access_declaration = 0x23,
|
||||
DW_TAG_base_type = 0x24,
|
||||
DW_TAG_catch_block = 0x25,
|
||||
DW_TAG_const_type = 0x26,
|
||||
DW_TAG_constant = 0x27,
|
||||
DW_TAG_enumerator = 0x28,
|
||||
DW_TAG_file_type = 0x29,
|
||||
DW_TAG_friend = 0x2a,
|
||||
DW_TAG_namelist = 0x2b,
|
||||
DW_TAG_namelist_item = 0x2c,
|
||||
DW_TAG_packed_type = 0x2d,
|
||||
DW_TAG_subprogram = 0x2e,
|
||||
DW_TAG_template_type_parameter = 0x2f,
|
||||
DW_TAG_template_value_parameter = 0x30,
|
||||
DW_TAG_thrown_type = 0x31,
|
||||
DW_TAG_try_block = 0x32,
|
||||
DW_TAG_variant_part = 0x33,
|
||||
DW_TAG_variable = 0x34,
|
||||
DW_TAG_volatile_type = 0x35,
|
||||
DW_TAG_dwarf_procedure = 0x36,
|
||||
DW_TAG_restrict_type = 0x37,
|
||||
DW_TAG_interface_type = 0x38,
|
||||
DW_TAG_namespace = 0x39,
|
||||
DW_TAG_imported_module = 0x3a,
|
||||
DW_TAG_unspecified_type = 0x3b,
|
||||
DW_TAG_partial_unit = 0x3c,
|
||||
DW_TAG_imported_unit = 0x3d,
|
||||
DW_TAG_condition = 0x3f,
|
||||
DW_TAG_shared_type = 0x40,
|
||||
DW_TAG_lo_user = 0x4080,
|
||||
DW_TAG_hi_user = 0xffff,
|
||||
};
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
||||
|
||||
#endif
|
730
src/rt/rust_internal.h
Normal file
730
src/rt/rust_internal.h
Normal file
@ -0,0 +1,730 @@
|
||||
#ifndef RUST_INTERNAL_H
|
||||
#define RUST_INTERNAL_H
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#define __STDC_CONSTANT_MACROS 1
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rust.h"
|
||||
|
||||
#include "rand.h"
|
||||
#include "rust_log.h"
|
||||
#include "uthash.h"
|
||||
|
||||
#if defined(__WIN32__)
|
||||
extern "C" {
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <wincrypt.h>
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#else
|
||||
#error "Platform not supported."
|
||||
#endif
|
||||
|
||||
#ifndef __i386__
|
||||
#error "Target CPU not supported."
|
||||
#endif
|
||||
|
||||
#define I(dom, e) ((e) ? (void)0 : \
|
||||
(dom)->srv->fatal(#e, __FILE__, __LINE__))
|
||||
|
||||
struct rust_task;
|
||||
struct rust_port;
|
||||
class rust_chan;
|
||||
struct rust_token;
|
||||
struct rust_dom;
|
||||
class rust_crate;
|
||||
class rust_crate_cache;
|
||||
class lockfree_queue;
|
||||
|
||||
struct stk_seg;
|
||||
struct type_desc;
|
||||
struct frame_glue_fns;
|
||||
|
||||
// This drives our preemption scheme.
|
||||
|
||||
static size_t const TIME_SLICE_IN_MS = 10;
|
||||
|
||||
// Every reference counted object should derive from this base class.
|
||||
|
||||
template <typename T>
|
||||
struct
|
||||
rc_base
|
||||
{
|
||||
size_t refcnt;
|
||||
|
||||
void ref() {
|
||||
++refcnt;
|
||||
}
|
||||
|
||||
void deref() {
|
||||
if (--refcnt == 0) {
|
||||
delete (T*)this;
|
||||
}
|
||||
}
|
||||
|
||||
rc_base();
|
||||
~rc_base();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct
|
||||
dom_owned
|
||||
{
|
||||
void operator delete(void *ptr) {
|
||||
((T *)ptr)->dom->free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct
|
||||
task_owned
|
||||
{
|
||||
void operator delete(void *ptr) {
|
||||
((T *)ptr)->task->dom->free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Helper class used regularly elsewhere.
|
||||
|
||||
template <typename T>
|
||||
class
|
||||
ptr_vec : public dom_owned<ptr_vec<T> >
|
||||
{
|
||||
static const size_t INIT_SIZE = 8;
|
||||
|
||||
rust_dom *dom;
|
||||
size_t alloc;
|
||||
size_t fill;
|
||||
T **data;
|
||||
|
||||
public:
|
||||
ptr_vec(rust_dom *dom);
|
||||
~ptr_vec();
|
||||
|
||||
size_t length() {
|
||||
return fill;
|
||||
}
|
||||
|
||||
T *& operator[](size_t offset);
|
||||
void push(T *p);
|
||||
T *pop();
|
||||
void trim(size_t fill);
|
||||
void swapdel(T* p);
|
||||
};
|
||||
|
||||
struct
|
||||
rust_dom
|
||||
{
|
||||
// Fields known to the compiler:
|
||||
uintptr_t interrupt_flag;
|
||||
|
||||
// Fields known only by the runtime:
|
||||
|
||||
// NB: the root crate must remain in memory until the root of the
|
||||
// tree of domains exits. All domains within this tree have a
|
||||
// copy of this root_crate value and use it for finding utility
|
||||
// glue.
|
||||
rust_crate const *root_crate;
|
||||
rust_log _log;
|
||||
rust_srv *srv;
|
||||
// uint32_t logbits;
|
||||
ptr_vec<rust_task> running_tasks;
|
||||
ptr_vec<rust_task> blocked_tasks;
|
||||
ptr_vec<rust_task> dead_tasks;
|
||||
ptr_vec<rust_crate_cache> caches;
|
||||
randctx rctx;
|
||||
rust_task *root_task;
|
||||
rust_task *curr_task;
|
||||
int rval;
|
||||
lockfree_queue *incoming; // incoming messages from other threads
|
||||
|
||||
#ifndef __WIN32__
|
||||
pthread_attr_t attr;
|
||||
#endif
|
||||
|
||||
rust_dom(rust_srv *srv, rust_crate const *root_crate);
|
||||
~rust_dom();
|
||||
|
||||
void activate(rust_task *task);
|
||||
void log(uint32_t logbit, char const *fmt, ...);
|
||||
rust_log & get_log();
|
||||
void logptr(char const *msg, uintptr_t ptrval);
|
||||
template<typename T>
|
||||
void logptr(char const *msg, T* ptrval);
|
||||
void fail();
|
||||
void *malloc(size_t sz);
|
||||
void *calloc(size_t sz);
|
||||
void *realloc(void *data, size_t sz);
|
||||
void free(void *p);
|
||||
|
||||
#ifdef __WIN32__
|
||||
void win32_require(LPCTSTR fn, BOOL ok);
|
||||
#endif
|
||||
|
||||
rust_crate_cache *get_cache(rust_crate const *crate);
|
||||
size_t n_live_tasks();
|
||||
void add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
||||
void remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
||||
const char *state_vec_name(ptr_vec<rust_task> *v);
|
||||
|
||||
void reap_dead_tasks();
|
||||
rust_task *sched();
|
||||
};
|
||||
|
||||
inline void *operator new(size_t sz, void *mem) {
|
||||
return mem;
|
||||
}
|
||||
|
||||
inline void *operator new(size_t sz, rust_dom *dom) {
|
||||
return dom->malloc(sz);
|
||||
}
|
||||
|
||||
inline void *operator new[](size_t sz, rust_dom *dom) {
|
||||
return dom->malloc(sz);
|
||||
}
|
||||
|
||||
inline void *operator new(size_t sz, rust_dom &dom) {
|
||||
return dom.malloc(sz);
|
||||
}
|
||||
|
||||
inline void *operator new[](size_t sz, rust_dom &dom) {
|
||||
return dom.malloc(sz);
|
||||
}
|
||||
|
||||
struct
|
||||
rust_timer
|
||||
{
|
||||
// FIXME: This will probably eventually need replacement
|
||||
// with something more sophisticated and integrated with
|
||||
// an IO event-handling library, when we have such a thing.
|
||||
// For now it's just the most basic "thread that can interrupt
|
||||
// its associated domain-thread" device, so that we have
|
||||
// *some* form of task-preemption.
|
||||
rust_dom &dom;
|
||||
uintptr_t exit_flag;
|
||||
|
||||
#if defined(__WIN32__)
|
||||
HANDLE thread;
|
||||
#else
|
||||
pthread_attr_t attr;
|
||||
pthread_t thread;
|
||||
#endif
|
||||
|
||||
rust_timer(rust_dom &dom);
|
||||
~rust_timer();
|
||||
};
|
||||
|
||||
#include "rust_util.h"
|
||||
|
||||
// Crates.
|
||||
|
||||
template<typename T> T*
|
||||
crate_rel(rust_crate const *crate, T *t) {
|
||||
return (T*)(((uintptr_t)crate) + ((ptrdiff_t)t));
|
||||
}
|
||||
|
||||
template<typename T> T const*
|
||||
crate_rel(rust_crate const *crate, T const *t) {
|
||||
return (T const*)(((uintptr_t)crate) + ((ptrdiff_t)t));
|
||||
}
|
||||
|
||||
typedef void CDECL (*activate_glue_ty)(rust_task *);
|
||||
|
||||
class
|
||||
rust_crate
|
||||
{
|
||||
// The following fields are emitted by the compiler for the static
|
||||
// rust_crate object inside each compiled crate.
|
||||
|
||||
ptrdiff_t image_base_off; // (Loaded image base) - this.
|
||||
uintptr_t self_addr; // Un-relocated addres of 'this'.
|
||||
|
||||
ptrdiff_t debug_abbrev_off; // Offset from this to .debug_abbrev.
|
||||
size_t debug_abbrev_sz; // Size of .debug_abbrev.
|
||||
|
||||
ptrdiff_t debug_info_off; // Offset from this to .debug_info.
|
||||
size_t debug_info_sz; // Size of .debug_info.
|
||||
|
||||
ptrdiff_t activate_glue_off;
|
||||
ptrdiff_t exit_task_glue_off;
|
||||
ptrdiff_t unwind_glue_off;
|
||||
ptrdiff_t yield_glue_off;
|
||||
|
||||
public:
|
||||
|
||||
size_t n_rust_syms;
|
||||
size_t n_c_syms;
|
||||
size_t n_libs;
|
||||
|
||||
// Crates are immutable, constructed by the compiler.
|
||||
|
||||
uintptr_t get_image_base() const;
|
||||
ptrdiff_t get_relocation_diff() const;
|
||||
activate_glue_ty get_activate_glue() const;
|
||||
uintptr_t get_exit_task_glue() const;
|
||||
uintptr_t get_unwind_glue() const;
|
||||
uintptr_t get_yield_glue() const;
|
||||
struct mem_area
|
||||
{
|
||||
rust_dom *dom;
|
||||
uintptr_t base;
|
||||
uintptr_t lim;
|
||||
mem_area(rust_dom *dom, uintptr_t pos, size_t sz);
|
||||
};
|
||||
|
||||
mem_area get_debug_info(rust_dom *dom) const;
|
||||
mem_area get_debug_abbrev(rust_dom *dom) const;
|
||||
};
|
||||
|
||||
|
||||
struct type_desc {
|
||||
// First part of type_desc is known to compiler.
|
||||
// first_param = &descs[1] if dynamic, null if static.
|
||||
const type_desc **first_param;
|
||||
size_t size;
|
||||
size_t align;
|
||||
uintptr_t copy_glue_off;
|
||||
uintptr_t drop_glue_off;
|
||||
uintptr_t free_glue_off;
|
||||
uintptr_t mark_glue_off; // For GC.
|
||||
uintptr_t obj_drop_glue_off; // For custom destructors.
|
||||
|
||||
// Residual fields past here are known only to runtime.
|
||||
UT_hash_handle hh;
|
||||
size_t n_descs;
|
||||
const type_desc *descs[];
|
||||
};
|
||||
|
||||
class
|
||||
rust_crate_cache : public dom_owned<rust_crate_cache>,
|
||||
public rc_base<rust_crate_cache>
|
||||
{
|
||||
public:
|
||||
class lib :
|
||||
public rc_base<lib>, public dom_owned<lib>
|
||||
{
|
||||
uintptr_t handle;
|
||||
public:
|
||||
rust_dom *dom;
|
||||
lib(rust_dom *dom, char const *name);
|
||||
uintptr_t get_handle();
|
||||
~lib();
|
||||
};
|
||||
|
||||
class c_sym :
|
||||
public rc_base<c_sym>, public dom_owned<c_sym>
|
||||
{
|
||||
uintptr_t val;
|
||||
lib *library;
|
||||
public:
|
||||
rust_dom *dom;
|
||||
c_sym(rust_dom *dom, lib *library, char const *name);
|
||||
uintptr_t get_val();
|
||||
~c_sym();
|
||||
};
|
||||
|
||||
class rust_sym :
|
||||
public rc_base<rust_sym>, public dom_owned<rust_sym>
|
||||
{
|
||||
uintptr_t val;
|
||||
c_sym *crate_sym;
|
||||
public:
|
||||
rust_dom *dom;
|
||||
rust_sym(rust_dom *dom, rust_crate const *curr_crate,
|
||||
c_sym *crate_sym, char const **path);
|
||||
uintptr_t get_val();
|
||||
~rust_sym();
|
||||
};
|
||||
|
||||
lib *get_lib(size_t n, char const *name);
|
||||
c_sym *get_c_sym(size_t n, lib *library, char const *name);
|
||||
rust_sym *get_rust_sym(size_t n,
|
||||
rust_dom *dom,
|
||||
rust_crate const *curr_crate,
|
||||
c_sym *crate_sym,
|
||||
char const **path);
|
||||
type_desc *get_type_desc(size_t size,
|
||||
size_t align,
|
||||
size_t n_descs,
|
||||
type_desc const **descs);
|
||||
|
||||
private:
|
||||
|
||||
rust_sym **rust_syms;
|
||||
c_sym **c_syms;
|
||||
lib **libs;
|
||||
type_desc *type_descs;
|
||||
|
||||
public:
|
||||
|
||||
rust_crate const *crate;
|
||||
rust_dom *dom;
|
||||
size_t idx;
|
||||
|
||||
rust_crate_cache(rust_dom *dom,
|
||||
rust_crate const *crate);
|
||||
~rust_crate_cache();
|
||||
void flush();
|
||||
};
|
||||
|
||||
#include "rust_dwarf.h"
|
||||
|
||||
class
|
||||
rust_crate_reader
|
||||
{
|
||||
struct mem_reader
|
||||
{
|
||||
rust_crate::mem_area &mem;
|
||||
bool ok;
|
||||
uintptr_t pos;
|
||||
|
||||
bool is_ok();
|
||||
bool at_end();
|
||||
void fail();
|
||||
void reset();
|
||||
mem_reader(rust_crate::mem_area &m);
|
||||
size_t tell_abs();
|
||||
size_t tell_off();
|
||||
void seek_abs(uintptr_t p);
|
||||
void seek_off(uintptr_t p);
|
||||
|
||||
template<typename T>
|
||||
void get(T &out) {
|
||||
if (pos < mem.base
|
||||
|| pos >= mem.lim
|
||||
|| pos + sizeof(T) > mem.lim)
|
||||
ok = false;
|
||||
if (!ok)
|
||||
return;
|
||||
out = *((T*)(pos));
|
||||
pos += sizeof(T);
|
||||
ok &= !at_end();
|
||||
I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void get_uleb(T &out) {
|
||||
out = T(0);
|
||||
for (size_t i = 0; i < sizeof(T) && ok; ++i) {
|
||||
uint8_t byte;
|
||||
get(byte);
|
||||
out <<= 7;
|
||||
out |= byte & 0x7f;
|
||||
if (!(byte & 0x80))
|
||||
break;
|
||||
}
|
||||
I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void adv_sizeof(T &) {
|
||||
adv(sizeof(T));
|
||||
}
|
||||
|
||||
bool adv_zstr(size_t sz);
|
||||
bool get_zstr(char const *&c, size_t &sz);
|
||||
void adv(size_t amt);
|
||||
};
|
||||
|
||||
struct
|
||||
abbrev : dom_owned<abbrev>
|
||||
{
|
||||
rust_dom *dom;
|
||||
uintptr_t body_off;
|
||||
size_t body_sz;
|
||||
uintptr_t tag;
|
||||
uint8_t has_children;
|
||||
size_t idx;
|
||||
abbrev(rust_dom *dom, uintptr_t body_off, size_t body_sz,
|
||||
uintptr_t tag, uint8_t has_children);
|
||||
};
|
||||
|
||||
class
|
||||
abbrev_reader : public mem_reader
|
||||
{
|
||||
ptr_vec<abbrev> abbrevs;
|
||||
public:
|
||||
abbrev_reader(rust_crate::mem_area &abbrev_mem);
|
||||
abbrev *get_abbrev(size_t i);
|
||||
bool step_attr_form_pair(uintptr_t &attr, uintptr_t &form);
|
||||
~abbrev_reader();
|
||||
};
|
||||
|
||||
rust_dom *dom;
|
||||
size_t idx;
|
||||
rust_crate const *crate;
|
||||
|
||||
rust_crate::mem_area abbrev_mem;
|
||||
abbrev_reader abbrevs;
|
||||
|
||||
rust_crate::mem_area die_mem;
|
||||
|
||||
public:
|
||||
|
||||
struct
|
||||
attr
|
||||
{
|
||||
dw_form form;
|
||||
dw_at at;
|
||||
union {
|
||||
struct {
|
||||
char const *s;
|
||||
size_t sz;
|
||||
} str;
|
||||
uintptr_t num;
|
||||
} val;
|
||||
|
||||
bool is_numeric() const;
|
||||
bool is_string() const;
|
||||
size_t get_ssz(rust_dom *dom) const;
|
||||
char const *get_str(rust_dom *dom) const;
|
||||
uintptr_t get_num(rust_dom *dom) const;
|
||||
bool is_unknown() const;
|
||||
};
|
||||
|
||||
struct die_reader;
|
||||
|
||||
struct
|
||||
die
|
||||
{
|
||||
die_reader *rdr;
|
||||
uintptr_t off;
|
||||
abbrev *ab;
|
||||
bool using_rdr;
|
||||
|
||||
die(die_reader *rdr, uintptr_t off);
|
||||
bool is_null() const;
|
||||
bool has_children() const;
|
||||
dw_tag tag() const;
|
||||
bool start_attrs() const;
|
||||
bool step_attr(attr &a) const;
|
||||
bool find_str_attr(dw_at at, char const *&c);
|
||||
bool find_num_attr(dw_at at, uintptr_t &n);
|
||||
bool is_transparent();
|
||||
bool find_child_by_name(char const *c, die &child,
|
||||
bool exact=false);
|
||||
bool find_child_by_tag(dw_tag tag, die &child);
|
||||
die next() const;
|
||||
die next_sibling() const;
|
||||
};
|
||||
|
||||
struct
|
||||
rdr_sess
|
||||
{
|
||||
die_reader *rdr;
|
||||
rdr_sess(die_reader *rdr);
|
||||
~rdr_sess();
|
||||
};
|
||||
|
||||
struct
|
||||
die_reader : public mem_reader
|
||||
{
|
||||
abbrev_reader &abbrevs;
|
||||
uint32_t cu_unit_length;
|
||||
uintptr_t cu_base;
|
||||
uint16_t dwarf_vers;
|
||||
uint32_t cu_abbrev_off;
|
||||
uint8_t sizeof_addr;
|
||||
bool in_use;
|
||||
|
||||
die first_die();
|
||||
void dump();
|
||||
die_reader(rust_crate::mem_area &die_mem,
|
||||
abbrev_reader &abbrevs);
|
||||
~die_reader();
|
||||
};
|
||||
die_reader dies;
|
||||
rust_crate_reader(rust_dom *dom, rust_crate const *crate);
|
||||
};
|
||||
|
||||
|
||||
// A cond(ition) is something we can block on. This can be a channel
|
||||
// (writing), a port (reading) or a task (waiting).
|
||||
|
||||
struct
|
||||
rust_cond
|
||||
{
|
||||
};
|
||||
|
||||
// An alarm can be put into a wait queue and the task will be notified
|
||||
// when the wait queue is flushed.
|
||||
|
||||
struct
|
||||
rust_alarm
|
||||
{
|
||||
rust_task *receiver;
|
||||
size_t idx;
|
||||
|
||||
rust_alarm(rust_task *receiver);
|
||||
};
|
||||
|
||||
|
||||
typedef ptr_vec<rust_alarm> rust_wait_queue;
|
||||
|
||||
|
||||
struct stk_seg {
|
||||
unsigned int valgrind_id;
|
||||
uintptr_t limit;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
struct frame_glue_fns {
|
||||
uintptr_t mark_glue_off;
|
||||
uintptr_t drop_glue_off;
|
||||
uintptr_t reloc_glue_off;
|
||||
};
|
||||
|
||||
struct
|
||||
rust_task : public rc_base<rust_task>,
|
||||
public dom_owned<rust_task>,
|
||||
public rust_cond
|
||||
{
|
||||
// Fields known to the compiler.
|
||||
stk_seg *stk;
|
||||
uintptr_t runtime_sp; // Runtime sp while task running.
|
||||
uintptr_t rust_sp; // Saved sp when not running.
|
||||
uintptr_t gc_alloc_chain; // Linked list of GC allocations.
|
||||
rust_dom *dom;
|
||||
rust_crate_cache *cache;
|
||||
|
||||
// Fields known only to the runtime.
|
||||
ptr_vec<rust_task> *state;
|
||||
rust_cond *cond;
|
||||
uintptr_t* dptr; // Rendezvous pointer for send/recv.
|
||||
rust_task *spawner; // Parent-link.
|
||||
size_t idx;
|
||||
|
||||
// Wait queue for tasks waiting for this task.
|
||||
rust_wait_queue waiting_tasks;
|
||||
rust_alarm alarm;
|
||||
|
||||
rust_task(rust_dom *dom,
|
||||
rust_task *spawner);
|
||||
~rust_task();
|
||||
|
||||
void start(uintptr_t exit_task_glue,
|
||||
uintptr_t spawnee_fn,
|
||||
uintptr_t args,
|
||||
size_t callsz);
|
||||
void grow(size_t n_frame_bytes);
|
||||
bool running();
|
||||
bool blocked();
|
||||
bool blocked_on(rust_cond *cond);
|
||||
bool dead();
|
||||
|
||||
const char *state_str();
|
||||
void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec);
|
||||
|
||||
void block(rust_cond *on);
|
||||
void wakeup(rust_cond *from);
|
||||
void die();
|
||||
void unblock();
|
||||
|
||||
void check_active() { I(dom, dom->curr_task == this); }
|
||||
void check_suspended() { I(dom, dom->curr_task != this); }
|
||||
|
||||
// Swap in some glue code to run when we have returned to the
|
||||
// task's context (assuming we're the active task).
|
||||
void run_after_return(size_t nargs, uintptr_t glue);
|
||||
|
||||
// Swap in some glue code to run when we're next activated
|
||||
// (assuming we're the suspended task).
|
||||
void run_on_resume(uintptr_t glue);
|
||||
|
||||
// Save callee-saved registers and return to the main loop.
|
||||
void yield(size_t nargs);
|
||||
|
||||
// Fail this task (assuming caller-on-stack is different task).
|
||||
void kill();
|
||||
|
||||
// Fail self, assuming caller-on-stack is this task.
|
||||
void fail(size_t nargs);
|
||||
|
||||
// Notify tasks waiting for us that we are about to die.
|
||||
void notify_waiting_tasks();
|
||||
|
||||
uintptr_t get_fp();
|
||||
uintptr_t get_previous_fp(uintptr_t fp);
|
||||
frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
|
||||
rust_crate_cache * get_crate_cache(rust_crate const *curr_crate);
|
||||
};
|
||||
|
||||
struct rust_port : public rc_base<rust_port>,
|
||||
public task_owned<rust_port>,
|
||||
public rust_cond {
|
||||
rust_task *task;
|
||||
size_t unit_sz;
|
||||
ptr_vec<rust_token> writers;
|
||||
ptr_vec<rust_chan> chans;
|
||||
|
||||
rust_port(rust_task *task, size_t unit_sz);
|
||||
~rust_port();
|
||||
};
|
||||
|
||||
struct rust_token : public rust_cond {
|
||||
rust_chan *chan; // Link back to the channel this token belongs to
|
||||
size_t idx; // Index into port->writers.
|
||||
bool submitted; // Whether token is in a port->writers.
|
||||
|
||||
rust_token(rust_chan *chan);
|
||||
~rust_token();
|
||||
|
||||
bool pending() const;
|
||||
void submit();
|
||||
void withdraw();
|
||||
};
|
||||
|
||||
|
||||
struct circ_buf : public dom_owned<circ_buf> {
|
||||
static const size_t INIT_CIRC_BUF_UNITS = 8;
|
||||
static const size_t MAX_CIRC_BUF_SIZE = 1 << 24;
|
||||
|
||||
rust_dom *dom;
|
||||
size_t alloc;
|
||||
size_t unit_sz;
|
||||
size_t next;
|
||||
size_t unread;
|
||||
uint8_t *data;
|
||||
|
||||
circ_buf(rust_dom *dom, size_t unit_sz);
|
||||
~circ_buf();
|
||||
|
||||
void transfer(void *dst);
|
||||
void push(void *src);
|
||||
void shift(void *dst);
|
||||
};
|
||||
|
||||
#include "rust_chan.h"
|
||||
|
||||
int
|
||||
rust_main_loop(rust_dom *dom);
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
||||
|
||||
#endif
|
117
src/rt/rust_log.cpp
Normal file
117
src/rt/rust_log.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Logging infrastructure that aims to support multi-threading, indentation
|
||||
* and ansi colors.
|
||||
*/
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
static uint32_t read_type_bit_mask() {
|
||||
uint32_t bits = rust_log::ULOG | rust_log::ERR;
|
||||
char *env_str = getenv("RUST_LOG");
|
||||
if (env_str) {
|
||||
bits = 0;
|
||||
bits |= strstr(env_str, "err") ? rust_log::ERR : 0;
|
||||
bits |= strstr(env_str, "mem") ? rust_log::MEM : 0;
|
||||
bits |= strstr(env_str, "comm") ? rust_log::COMM : 0;
|
||||
bits |= strstr(env_str, "task") ? rust_log::TASK : 0;
|
||||
bits |= strstr(env_str, "up") ? rust_log::UPCALL : 0;
|
||||
bits |= strstr(env_str, "dom") ? rust_log::DOM : 0;
|
||||
bits |= strstr(env_str, "ulog") ? rust_log::ULOG : 0;
|
||||
bits |= strstr(env_str, "trace") ? rust_log::TRACE : 0;
|
||||
bits |= strstr(env_str, "dwarf") ? rust_log::DWARF : 0;
|
||||
bits |= strstr(env_str, "cache") ? rust_log::CACHE : 0;
|
||||
bits |= strstr(env_str, "timer") ? rust_log::TIMER : 0;
|
||||
bits |= strstr(env_str, "all") ? rust_log::ALL : 0;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
rust_log::ansi_color rust_log::get_type_color(log_type type) {
|
||||
switch (type) {
|
||||
case ERR:
|
||||
return rust_log::RED;
|
||||
case UPCALL:
|
||||
return rust_log::GREEN;
|
||||
case COMM:
|
||||
return rust_log::MAGENTA;
|
||||
case DOM:
|
||||
case TASK:
|
||||
return rust_log::LIGHTTEAL;
|
||||
case MEM:
|
||||
return rust_log::YELLOW;
|
||||
default:
|
||||
return rust_log::WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * _foreground_colors[] = { "[30m", "[1;30m", "[37m",
|
||||
"[31m", "[1;31m", "[32m",
|
||||
"[1;32m", "[33m", "[33m",
|
||||
"[34m", "[1;34m", "[35m",
|
||||
"[1;35m", "[36m", "[1;36m" };
|
||||
rust_log::rust_log(rust_srv *srv, rust_dom *dom) :
|
||||
_srv(srv), _dom(dom), _type_bit_mask(read_type_bit_mask()),
|
||||
_use_colors(getenv("RUST_COLOR_LOG")), _indent(0) {
|
||||
}
|
||||
|
||||
rust_log::~rust_log() {
|
||||
|
||||
}
|
||||
|
||||
void rust_log::trace_ln(char *message) {
|
||||
char buffer[512];
|
||||
if (_use_colors) {
|
||||
snprintf(buffer, sizeof(buffer), "\x1b%s0x%08" PRIxPTR "\x1b[0m: ",
|
||||
_foreground_colors[1 + ((uintptr_t) _dom % 2687 % (LIGHTTEAL
|
||||
- 1))], (uintptr_t) _dom);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "0x%08" PRIxPTR ": ",
|
||||
(uintptr_t) _dom);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < _indent; i++) {
|
||||
strncat(buffer, "\t", sizeof(buffer) - strlen(buffer) - 1);
|
||||
}
|
||||
strncat(buffer, message, sizeof(buffer) - strlen(buffer) - 1);
|
||||
_srv->log(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traces a log message if the specified logging type is not filtered.
|
||||
*/
|
||||
void rust_log::trace_ln(uint32_t type_bits, char *message) {
|
||||
trace_ln(get_type_color((rust_log::log_type) type_bits), type_bits,
|
||||
message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traces a log message using the specified ANSI color code.
|
||||
*/
|
||||
void rust_log::trace_ln(ansi_color color, uint32_t type_bits, char *message) {
|
||||
if (is_tracing(type_bits)) {
|
||||
if (_use_colors) {
|
||||
char buffer[512];
|
||||
snprintf(buffer, sizeof(buffer), "\x1b%s%s\x1b[0m",
|
||||
_foreground_colors[color], message);
|
||||
trace_ln(buffer);
|
||||
} else {
|
||||
trace_ln(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rust_log::is_tracing(uint32_t type_bits) {
|
||||
return type_bits & _type_bit_mask;
|
||||
}
|
||||
|
||||
void rust_log::indent() {
|
||||
_indent++;
|
||||
}
|
||||
|
||||
void rust_log::outdent() {
|
||||
_indent--;
|
||||
}
|
||||
|
||||
void rust_log::reset_indent(uint32_t indent) {
|
||||
_indent = indent;
|
||||
}
|
59
src/rt/rust_log.h
Normal file
59
src/rt/rust_log.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef RUST_LOG_H_
|
||||
#define RUST_LOG_H_
|
||||
|
||||
class rust_dom;
|
||||
|
||||
class rust_log {
|
||||
rust_srv *_srv;
|
||||
rust_dom *_dom;
|
||||
uint32_t _type_bit_mask;
|
||||
bool _use_colors;
|
||||
uint32_t _indent;
|
||||
void trace_ln(char *message);
|
||||
public:
|
||||
rust_log(rust_srv *srv, rust_dom *dom);
|
||||
virtual ~rust_log();
|
||||
|
||||
enum ansi_color {
|
||||
BLACK,
|
||||
GRAY,
|
||||
WHITE,
|
||||
RED,
|
||||
LIGHTRED,
|
||||
GREEN,
|
||||
LIGHTGREEN,
|
||||
YELLOW,
|
||||
LIGHTYELLOW,
|
||||
BLUE,
|
||||
LIGHTBLUE,
|
||||
MAGENTA,
|
||||
LIGHTMAGENTA,
|
||||
TEAL,
|
||||
LIGHTTEAL
|
||||
};
|
||||
|
||||
enum log_type {
|
||||
ERR = 0x1,
|
||||
MEM = 0x2,
|
||||
COMM = 0x4,
|
||||
TASK = 0x8,
|
||||
DOM = 0x10,
|
||||
ULOG = 0x20,
|
||||
TRACE = 0x40,
|
||||
DWARF = 0x80,
|
||||
CACHE = 0x100,
|
||||
UPCALL = 0x200,
|
||||
TIMER = 0x400,
|
||||
ALL = 0xffffffff
|
||||
};
|
||||
|
||||
void indent();
|
||||
void outdent();
|
||||
void reset_indent(uint32_t indent);
|
||||
void trace_ln(uint32_t type_bits, char *message);
|
||||
void trace_ln(ansi_color color, uint32_t type_bits, char *message);
|
||||
bool is_tracing(uint32_t type_bits);
|
||||
static ansi_color get_type_color(log_type type);
|
||||
};
|
||||
|
||||
#endif /* RUST_LOG_H_ */
|
474
src/rt/rust_task.cpp
Normal file
474
src/rt/rust_task.cpp
Normal file
@ -0,0 +1,474 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
#include "valgrind.h"
|
||||
#include "memcheck.h"
|
||||
|
||||
// Stacks
|
||||
|
||||
static size_t const min_stk_bytes = 0x300;
|
||||
|
||||
// Task stack segments. Heap allocated and chained together.
|
||||
|
||||
static stk_seg*
|
||||
new_stk(rust_dom *dom, size_t minsz)
|
||||
{
|
||||
if (minsz < min_stk_bytes)
|
||||
minsz = min_stk_bytes;
|
||||
size_t sz = sizeof(stk_seg) + minsz;
|
||||
stk_seg *stk = (stk_seg *)dom->malloc(sz);
|
||||
dom->logptr("new stk", (uintptr_t)stk);
|
||||
memset(stk, 0, sizeof(stk_seg));
|
||||
stk->limit = (uintptr_t) &stk->data[minsz];
|
||||
dom->logptr("stk limit", stk->limit);
|
||||
stk->valgrind_id =
|
||||
VALGRIND_STACK_REGISTER(&stk->data[0],
|
||||
&stk->data[minsz]);
|
||||
return stk;
|
||||
}
|
||||
|
||||
static void
|
||||
del_stk(rust_dom *dom, stk_seg *stk)
|
||||
{
|
||||
VALGRIND_STACK_DEREGISTER(stk->valgrind_id);
|
||||
dom->logptr("freeing stk segment", (uintptr_t)stk);
|
||||
dom->free(stk);
|
||||
}
|
||||
|
||||
// Tasks
|
||||
|
||||
// FIXME (issue #31): ifdef by platform. This is getting absurdly
|
||||
// x86-specific.
|
||||
|
||||
size_t const n_callee_saves = 4;
|
||||
size_t const callee_save_fp = 0;
|
||||
|
||||
static uintptr_t
|
||||
align_down(uintptr_t sp)
|
||||
{
|
||||
// There is no platform we care about that needs more than a
|
||||
// 16-byte alignment.
|
||||
return sp & ~(16 - 1);
|
||||
}
|
||||
|
||||
|
||||
rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
|
||||
stk(new_stk(dom, 0)),
|
||||
runtime_sp(0),
|
||||
rust_sp(stk->limit),
|
||||
gc_alloc_chain(0),
|
||||
dom(dom),
|
||||
cache(NULL),
|
||||
state(&dom->running_tasks),
|
||||
cond(NULL),
|
||||
dptr(0),
|
||||
spawner(spawner),
|
||||
idx(0),
|
||||
waiting_tasks(dom),
|
||||
alarm(this)
|
||||
{
|
||||
dom->logptr("new task", (uintptr_t)this);
|
||||
}
|
||||
|
||||
rust_task::~rust_task()
|
||||
{
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"~rust_task 0x%" PRIxPTR ", refcnt=%d",
|
||||
(uintptr_t)this, refcnt);
|
||||
|
||||
/*
|
||||
for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) {
|
||||
frame_glue_fns *glue_fns = get_frame_glue_fns(fp);
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"~rust_task, frame fp=0x%" PRIxPTR ", glue_fns=0x%" PRIxPTR,
|
||||
fp, glue_fns);
|
||||
if (glue_fns) {
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"~rust_task, mark_glue=0x%" PRIxPTR,
|
||||
glue_fns->mark_glue);
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"~rust_task, drop_glue=0x%" PRIxPTR,
|
||||
glue_fns->drop_glue);
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"~rust_task, reloc_glue=0x%" PRIxPTR,
|
||||
glue_fns->reloc_glue);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* FIXME: tighten this up, there are some more
|
||||
assertions that hold at task-lifecycle events. */
|
||||
I(dom, refcnt == 0 ||
|
||||
(refcnt == 1 && this == dom->root_task));
|
||||
|
||||
del_stk(dom, stk);
|
||||
if (cache)
|
||||
cache->deref();
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::start(uintptr_t exit_task_glue,
|
||||
uintptr_t spawnee_fn,
|
||||
uintptr_t args,
|
||||
size_t callsz)
|
||||
{
|
||||
dom->logptr("exit-task glue", exit_task_glue);
|
||||
dom->logptr("from spawnee", spawnee_fn);
|
||||
|
||||
// Set sp to last uintptr_t-sized cell of segment and align down.
|
||||
rust_sp -= sizeof(uintptr_t);
|
||||
rust_sp = align_down(rust_sp);
|
||||
|
||||
// Begin synthesizing frames. There are two: a "fully formed"
|
||||
// exit-task frame at the top of the stack -- that pretends to be
|
||||
// mid-execution -- and a just-starting frame beneath it that
|
||||
// starts executing the first instruction of the spawnee. The
|
||||
// spawnee *thinks* it was called by the exit-task frame above
|
||||
// it. It wasn't; we put that fake frame in place here, but the
|
||||
// illusion is enough for the spawnee to return to the exit-task
|
||||
// frame when it's done, and exit.
|
||||
uintptr_t *spp = (uintptr_t *)rust_sp;
|
||||
|
||||
// The exit_task_glue frame we synthesize above the frame we activate:
|
||||
*spp-- = (uintptr_t) this; // task
|
||||
*spp-- = (uintptr_t) 0; // output
|
||||
*spp-- = (uintptr_t) 0; // retpc
|
||||
for (size_t j = 0; j < n_callee_saves; ++j) {
|
||||
*spp-- = 0;
|
||||
}
|
||||
|
||||
// We want 'frame_base' to point to the last callee-save in this
|
||||
// (exit-task) frame, because we're going to inject this
|
||||
// frame-pointer into the callee-save frame pointer value in the
|
||||
// *next* (spawnee) frame. A cheap trick, but this means the
|
||||
// spawnee frame will restore the proper frame pointer of the glue
|
||||
// frame as it runs its epilogue.
|
||||
uintptr_t frame_base = (uintptr_t) (spp+1);
|
||||
|
||||
*spp-- = (uintptr_t) dom->root_crate; // crate ptr
|
||||
*spp-- = (uintptr_t) 0; // frame_glue_fns
|
||||
|
||||
// Copy args from spawner to spawnee.
|
||||
if (args) {
|
||||
uintptr_t *src = (uintptr_t *)args;
|
||||
src += 1; // spawn-call output slot
|
||||
src += 1; // spawn-call task slot
|
||||
// Memcpy all but the task and output pointers
|
||||
callsz -= (2 * sizeof(uintptr_t));
|
||||
spp = (uintptr_t*) (((uintptr_t)spp) - callsz);
|
||||
memcpy(spp, src, callsz);
|
||||
|
||||
// Move sp down to point to task cell.
|
||||
spp--;
|
||||
} else {
|
||||
// We're at root, starting up.
|
||||
I(dom, callsz==0);
|
||||
}
|
||||
|
||||
// The *implicit* incoming args to the spawnee frame we're
|
||||
// activating:
|
||||
|
||||
*spp-- = (uintptr_t) this; // task
|
||||
*spp-- = (uintptr_t) 0; // output addr
|
||||
*spp-- = (uintptr_t) exit_task_glue; // retpc
|
||||
|
||||
// The context the activate_glue needs to switch stack.
|
||||
*spp-- = (uintptr_t) spawnee_fn; // instruction to start at
|
||||
for (size_t j = 0; j < n_callee_saves; ++j) {
|
||||
// callee-saves to carry in when we activate
|
||||
if (j == callee_save_fp)
|
||||
*spp-- = frame_base;
|
||||
else
|
||||
*spp-- = NULL;
|
||||
}
|
||||
|
||||
// Back up one, we overshot where sp should be.
|
||||
rust_sp = (uintptr_t) (spp+1);
|
||||
|
||||
dom->add_task_to_state_vec(&dom->running_tasks, this);
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::grow(size_t n_frame_bytes)
|
||||
{
|
||||
stk_seg *old_stk = this->stk;
|
||||
uintptr_t old_top = (uintptr_t) old_stk->limit;
|
||||
uintptr_t old_bottom = (uintptr_t) &old_stk->data[0];
|
||||
uintptr_t rust_sp_disp = old_top - this->rust_sp;
|
||||
size_t ssz = old_top - old_bottom;
|
||||
dom->log(rust_log::MEM|rust_log::TASK|rust_log::UPCALL,
|
||||
"upcall_grow_task(%" PRIdPTR
|
||||
"), old size %" PRIdPTR
|
||||
" bytes (old lim: 0x%" PRIxPTR ")",
|
||||
n_frame_bytes, ssz, old_top);
|
||||
ssz *= 2;
|
||||
if (ssz < n_frame_bytes)
|
||||
ssz = n_frame_bytes;
|
||||
ssz = next_power_of_two(ssz);
|
||||
|
||||
dom->log(rust_log::MEM|rust_log::TASK, "upcall_grow_task growing stk 0x%"
|
||||
PRIxPTR " to %d bytes", old_stk, ssz);
|
||||
|
||||
stk_seg *nstk = new_stk(dom, ssz);
|
||||
uintptr_t new_top = (uintptr_t) &nstk->data[ssz];
|
||||
size_t n_copy = old_top - old_bottom;
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"copying %d bytes of stack from [0x%" PRIxPTR ", 0x%" PRIxPTR "]"
|
||||
" to [0x%" PRIxPTR ", 0x%" PRIxPTR "]",
|
||||
n_copy,
|
||||
old_bottom, old_bottom + n_copy,
|
||||
new_top - n_copy, new_top);
|
||||
|
||||
VALGRIND_MAKE_MEM_DEFINED((void*)old_bottom, n_copy);
|
||||
memcpy((void*)(new_top - n_copy), (void*)old_bottom, n_copy);
|
||||
|
||||
nstk->limit = new_top;
|
||||
this->stk = nstk;
|
||||
this->rust_sp = new_top - rust_sp_disp;
|
||||
|
||||
dom->log(rust_log::MEM|rust_log::TASK, "processing relocations");
|
||||
|
||||
// FIXME (issue #32): this is the most ridiculously crude
|
||||
// relocation scheme ever. Try actually, you know, writing out
|
||||
// reloc descriptors?
|
||||
size_t n_relocs = 0;
|
||||
for (uintptr_t* p = (uintptr_t*)(new_top - n_copy);
|
||||
p < (uintptr_t*)new_top; ++p) {
|
||||
if (old_bottom <= *p && *p < old_top) {
|
||||
//dom->log(rust_log::MEM, "relocating pointer 0x%" PRIxPTR
|
||||
// " by %d bytes", *p, (new_top - old_top));
|
||||
n_relocs++;
|
||||
*p += (new_top - old_top);
|
||||
}
|
||||
}
|
||||
dom->log(rust_log::MEM|rust_log::TASK,
|
||||
"processed %d relocations", n_relocs);
|
||||
del_stk(dom, old_stk);
|
||||
dom->logptr("grown stk limit", new_top);
|
||||
}
|
||||
|
||||
void
|
||||
push_onto_thread_stack(uintptr_t &sp, uintptr_t value)
|
||||
{
|
||||
asm("xchgl %0, %%esp\n"
|
||||
"push %2\n"
|
||||
"xchgl %0, %%esp\n"
|
||||
: "=r" (sp)
|
||||
: "0" (sp), "r" (value)
|
||||
: "eax");
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::run_after_return(size_t nargs, uintptr_t glue)
|
||||
{
|
||||
// This is only safe to call if we're the currently-running task.
|
||||
check_active();
|
||||
|
||||
uintptr_t sp = runtime_sp;
|
||||
|
||||
// The compiler reserves nargs + 1 word for oldsp on the stack and
|
||||
// then aligns it.
|
||||
sp = align_down(sp - nargs * sizeof(uintptr_t));
|
||||
|
||||
uintptr_t *retpc = ((uintptr_t *) sp) - 1;
|
||||
dom->log(rust_log::TASK|rust_log::MEM,
|
||||
"run_after_return: overwriting retpc=0x%" PRIxPTR
|
||||
" @ runtime_sp=0x%" PRIxPTR
|
||||
" with glue=0x%" PRIxPTR,
|
||||
*retpc, sp, glue);
|
||||
|
||||
// Move the current return address (which points into rust code)
|
||||
// onto the rust stack and pretend we just called into the glue.
|
||||
push_onto_thread_stack(rust_sp, *retpc);
|
||||
*retpc = glue;
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::run_on_resume(uintptr_t glue)
|
||||
{
|
||||
// This is only safe to call if we're suspended.
|
||||
check_suspended();
|
||||
|
||||
// Inject glue as resume address in the suspended frame.
|
||||
uintptr_t* rsp = (uintptr_t*) rust_sp;
|
||||
rsp += n_callee_saves;
|
||||
dom->log(rust_log::TASK|rust_log::MEM,
|
||||
"run_on_resume: overwriting retpc=0x%" PRIxPTR
|
||||
" @ rust_sp=0x%" PRIxPTR
|
||||
" with glue=0x%" PRIxPTR,
|
||||
*rsp, rsp, glue);
|
||||
*rsp = glue;
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::yield(size_t nargs)
|
||||
{
|
||||
dom->log(rust_log::TASK,
|
||||
"task 0x%" PRIxPTR " yielding", this);
|
||||
run_after_return(nargs, dom->root_crate->get_yield_glue());
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
get_callee_save_fp(uintptr_t *top_of_callee_saves)
|
||||
{
|
||||
return top_of_callee_saves[n_callee_saves - (callee_save_fp + 1)];
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::kill() {
|
||||
// Note the distinction here: kill() is when you're in an upcall
|
||||
// from task A and want to force-fail task B, you do B->kill().
|
||||
// If you want to fail yourself you do self->fail(upcall_nargs).
|
||||
dom->log(rust_log::TASK, "killing task 0x%" PRIxPTR, this);
|
||||
// Unblock the task so it can unwind.
|
||||
unblock();
|
||||
if (this == dom->root_task)
|
||||
dom->fail();
|
||||
run_on_resume(dom->root_crate->get_unwind_glue());
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::fail(size_t nargs) {
|
||||
// See note in ::kill() regarding who should call this.
|
||||
dom->log(rust_log::TASK, "task 0x%" PRIxPTR " failing", this);
|
||||
// Unblock the task so it can unwind.
|
||||
unblock();
|
||||
if (this == dom->root_task)
|
||||
dom->fail();
|
||||
run_after_return(nargs, dom->root_crate->get_unwind_glue());
|
||||
if (spawner) {
|
||||
dom->log(rust_log::TASK,
|
||||
"task 0x%" PRIxPTR
|
||||
" propagating failure to parent 0x%" PRIxPTR,
|
||||
this, spawner);
|
||||
spawner->kill();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::notify_waiting_tasks()
|
||||
{
|
||||
while (waiting_tasks.length() > 0) {
|
||||
rust_task *t = waiting_tasks.pop()->receiver;
|
||||
if (!t->dead())
|
||||
t->wakeup(this);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_task::get_fp() {
|
||||
// sp in any suspended task points to the last callee-saved reg on
|
||||
// the task stack.
|
||||
return get_callee_save_fp((uintptr_t*)rust_sp);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
rust_task::get_previous_fp(uintptr_t fp) {
|
||||
// fp happens to, coincidentally (!) also point to the last
|
||||
// callee-save on the task stack.
|
||||
return get_callee_save_fp((uintptr_t*)fp);
|
||||
}
|
||||
|
||||
frame_glue_fns*
|
||||
rust_task::get_frame_glue_fns(uintptr_t fp) {
|
||||
fp -= sizeof(uintptr_t);
|
||||
return *((frame_glue_fns**) fp);
|
||||
}
|
||||
|
||||
bool
|
||||
rust_task::running()
|
||||
{
|
||||
return state == &dom->running_tasks;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_task::blocked()
|
||||
{
|
||||
return state == &dom->blocked_tasks;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_task::blocked_on(rust_cond *on)
|
||||
{
|
||||
return blocked() && cond == on;
|
||||
}
|
||||
|
||||
bool
|
||||
rust_task::dead()
|
||||
{
|
||||
return state == &dom->dead_tasks;
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::transition(ptr_vec<rust_task> *src, ptr_vec<rust_task> *dst)
|
||||
{
|
||||
I(dom, state == src);
|
||||
dom->log(rust_log::TASK,
|
||||
"task 0x%" PRIxPTR " state change '%s' -> '%s'",
|
||||
(uintptr_t)this,
|
||||
dom->state_vec_name(src),
|
||||
dom->state_vec_name(dst));
|
||||
dom->remove_task_from_state_vec(src, this);
|
||||
dom->add_task_to_state_vec(dst, this);
|
||||
state = dst;
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::block(rust_cond *on)
|
||||
{
|
||||
I(dom, on);
|
||||
transition(&dom->running_tasks, &dom->blocked_tasks);
|
||||
dom->log(rust_log::TASK,
|
||||
"task 0x%" PRIxPTR " blocking on 0x%" PRIxPTR,
|
||||
(uintptr_t)this,
|
||||
(uintptr_t)on);
|
||||
cond = on;
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::wakeup(rust_cond *from)
|
||||
{
|
||||
transition(&dom->blocked_tasks, &dom->running_tasks);
|
||||
I(dom, cond == from);
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::die()
|
||||
{
|
||||
transition(&dom->running_tasks, &dom->dead_tasks);
|
||||
}
|
||||
|
||||
void
|
||||
rust_task::unblock()
|
||||
{
|
||||
if (blocked())
|
||||
wakeup(cond);
|
||||
}
|
||||
|
||||
rust_crate_cache *
|
||||
rust_task::get_crate_cache(rust_crate const *curr_crate)
|
||||
{
|
||||
if (cache && cache->crate != curr_crate) {
|
||||
dom->log(rust_log::TASK, "switching task crate-cache to crate 0x%"
|
||||
PRIxPTR, curr_crate);
|
||||
cache->deref();
|
||||
cache = NULL;
|
||||
}
|
||||
|
||||
if (!cache) {
|
||||
dom->log(rust_log::TASK, "fetching cache for current crate");
|
||||
cache = dom->get_cache(curr_crate);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
97
src/rt/rust_timer.cpp
Normal file
97
src/rt/rust_timer.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
// The mechanism in this file is very crude; every domain (thread) spawns its
|
||||
// own secondary timer thread, and that timer thread *never idles*. It
|
||||
// sleep-loops interrupting the domain.
|
||||
//
|
||||
// This will need replacement, particularly in order to achieve an actual
|
||||
// state of idling when we're waiting on the outside world. Though that might
|
||||
// be as simple as making a secondary waitable start/stop-timer signalling
|
||||
// system between the domain and its timer thread. We'll see.
|
||||
//
|
||||
// On the other hand, we don't presently have the ability to idle domains *at
|
||||
// all*, and without the timer thread we're unable to otherwise preempt rust
|
||||
// tasks. So ... one step at a time.
|
||||
//
|
||||
// The implementation here is "lockless" in the sense that it only involves
|
||||
// one-directional signaling of one-shot events, so the event initiator just
|
||||
// writes a nonzero word to a prederermined location and waits for the
|
||||
// receiver to see it show up in their memory.
|
||||
|
||||
#if defined(__WIN32__)
|
||||
static DWORD WINAPI
|
||||
win32_timer_loop(void *ptr)
|
||||
{
|
||||
// We were handed the rust_timer that owns us.
|
||||
rust_timer *timer = (rust_timer *)ptr;
|
||||
rust_dom &dom = timer->dom;
|
||||
dom.log(LOG_TIMER, "in timer 0x%" PRIxPTR, (uintptr_t)timer);
|
||||
while (!timer->exit_flag) {
|
||||
Sleep(TIME_SLICE_IN_MS);
|
||||
dom.log(LOG_TIMER,
|
||||
"timer 0x%" PRIxPTR
|
||||
" interrupting domain 0x%" PRIxPTR,
|
||||
(uintptr_t)timer,
|
||||
(uintptr_t)&dom);
|
||||
dom.interrupt_flag = 1;
|
||||
}
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
static void *
|
||||
pthread_timer_loop(void *ptr)
|
||||
{
|
||||
// We were handed the rust_timer that owns us.
|
||||
rust_timer *timer = (rust_timer *)ptr;
|
||||
rust_dom &dom(timer->dom);
|
||||
while (!timer->exit_flag) {
|
||||
usleep(TIME_SLICE_IN_MS * 1000);
|
||||
dom.interrupt_flag = 1;
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
return 0;
|
||||
|
||||
}
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
|
||||
|
||||
rust_timer::rust_timer(rust_dom &dom) : dom(dom), exit_flag(0)
|
||||
{
|
||||
dom.log(rust_log::TIMER, "creating timer for domain 0x%" PRIxPTR, &dom);
|
||||
#if defined(__WIN32__)
|
||||
thread = CreateThread(NULL, 0, win32_timer_loop, this, 0, NULL);
|
||||
dom.win32_require("CreateThread", thread != NULL);
|
||||
#else
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_create(&thread, &attr, pthread_timer_loop, (void *)this);
|
||||
#endif
|
||||
}
|
||||
|
||||
rust_timer::~rust_timer()
|
||||
{
|
||||
exit_flag = 1;
|
||||
#if defined(__WIN32__)
|
||||
dom.win32_require("WaitForSingleObject",
|
||||
WaitForSingleObject(thread, INFINITE)
|
||||
== WAIT_OBJECT_0);
|
||||
#else
|
||||
pthread_join(thread, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
654
src/rt/rust_upcall.cpp
Normal file
654
src/rt/rust_upcall.cpp
Normal file
@ -0,0 +1,654 @@
|
||||
|
||||
#include "rust_internal.h"
|
||||
|
||||
|
||||
// Upcalls.
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LOG_UPCALL_ENTRY(task) \
|
||||
(task)->dom->get_log().reset_indent(0); \
|
||||
(task)->dom->log(rust_log::UPCALL, \
|
||||
"upcall task: 0x%" PRIxPTR \
|
||||
" retpc: 0x%" PRIxPTR, \
|
||||
(task), __builtin_return_address(0)); \
|
||||
(task)->dom->get_log().indent();
|
||||
#else
|
||||
#define LOG_UPCALL_ENTRY(task) \
|
||||
(task)->dom->get_log().reset_indent(0); \
|
||||
(task)->dom->log(rust_log::UPCALL, \
|
||||
"upcall task: 0x%" PRIxPTR (task)); \
|
||||
(task)->dom->get_log().indent();
|
||||
#endif
|
||||
|
||||
extern "C" CDECL char const *str_buf(rust_task *task, rust_str *s);
|
||||
|
||||
extern "C" void
|
||||
upcall_grow_task(rust_task *task, size_t n_frame_bytes)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->grow(n_frame_bytes);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_log_int(rust_task *task, int32_t i)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::ULOG,
|
||||
"upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')",
|
||||
i, i, (char)i);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_log_str(rust_task *task, rust_str *str)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
const char *c = str_buf(task, str);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::ULOG,
|
||||
"upcall log_str(\"%s\")",
|
||||
c);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_trace_word(rust_task *task, uintptr_t i)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::TRACE,
|
||||
"trace: 0x%" PRIxPTR "",
|
||||
i, i, (char)i);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_trace_str(rust_task *task, char const *c)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::TRACE,
|
||||
"trace: %s",
|
||||
c);
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_port*
|
||||
upcall_new_port(rust_task *task, size_t unit_sz)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
||||
"upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)",
|
||||
(uintptr_t)task, unit_sz);
|
||||
return new (dom) rust_port(task, unit_sz);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_del_port(rust_task *task, rust_port *port)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
||||
"upcall del_port(0x%" PRIxPTR ")", (uintptr_t)port);
|
||||
I(task->dom, !port->refcnt);
|
||||
delete port;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_chan*
|
||||
upcall_new_chan(rust_task *task, rust_port *port)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
||||
"upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
|
||||
(uintptr_t)task, port);
|
||||
I(dom, port);
|
||||
return new (dom) rust_chan(task, port);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_del_chan(rust_task *task, rust_chan *chan)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
||||
"upcall del_chan(0x%" PRIxPTR ")", (uintptr_t)chan);
|
||||
I(dom, !chan->refcnt);
|
||||
delete chan;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_chan *
|
||||
upcall_clone_chan(rust_task *task, rust_task *owner, rust_chan *chan)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
||||
"upcall clone_chan(owner 0x%" PRIxPTR ", chan 0x%" PRIxPTR ")",
|
||||
(uintptr_t)owner, (uintptr_t)chan);
|
||||
return new (owner->dom) rust_chan(owner, chan->port);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Buffering protocol:
|
||||
*
|
||||
* - Reader attempts to read:
|
||||
* - Set reader to blocked-reading state.
|
||||
* - If buf with data exists:
|
||||
* - Attempt transmission.
|
||||
*
|
||||
* - Writer attempts to write:
|
||||
* - Set writer to blocked-writing state.
|
||||
* - Copy data into chan.
|
||||
* - Attempt transmission.
|
||||
*
|
||||
* - Transmission:
|
||||
* - Copy data from buf to reader
|
||||
* - Decr buf
|
||||
* - Set reader to running
|
||||
* - If buf now empty and blocked writer:
|
||||
* - Set blocked writer to running
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
attempt_transmission(rust_dom *dom,
|
||||
rust_chan *src,
|
||||
rust_task *dst)
|
||||
{
|
||||
I(dom, src);
|
||||
I(dom, dst);
|
||||
|
||||
rust_port *port = src->port;
|
||||
if (!port) {
|
||||
dom->log(rust_log::COMM,
|
||||
"src died, transmission incomplete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
circ_buf *buf = &src->buffer;
|
||||
if (buf->unread == 0) {
|
||||
dom->log(rust_log::COMM,
|
||||
"buffer empty, transmission incomplete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dst->blocked_on(port)) {
|
||||
dom->log(rust_log::COMM,
|
||||
"dst in non-reading state, transmission incomplete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t *dptr = dst->dptr;
|
||||
dom->log(rust_log::COMM,
|
||||
"receiving %d bytes into dst_task=0x%" PRIxPTR
|
||||
", dptr=0x%" PRIxPTR,
|
||||
port->unit_sz, dst, dptr);
|
||||
buf->shift(dptr);
|
||||
|
||||
// Wake up the sender if its waiting for the send operation.
|
||||
rust_task *sender = src->task;
|
||||
rust_token *token = &src->token;
|
||||
if (sender->blocked_on(token))
|
||||
sender->wakeup(token);
|
||||
|
||||
// Wake up the receiver, there is new data.
|
||||
dst->wakeup(port);
|
||||
|
||||
dom->log(rust_log::COMM, "transmission complete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_yield(rust_task *task)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::COMM, "upcall yield()");
|
||||
task->yield(1);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_join(rust_task *task, rust_task *other)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
||||
"upcall join(other=0x%" PRIxPTR ")",
|
||||
(uintptr_t)other);
|
||||
|
||||
// If the other task is already dying, we dont have to wait for it.
|
||||
if (!other->dead()) {
|
||||
other->waiting_tasks.push(&task->alarm);
|
||||
task->block(other);
|
||||
task->yield(2);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_send(rust_task *task, rust_chan *chan, void *sptr)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
||||
"upcall send(chan=0x%" PRIxPTR ", sptr=0x%" PRIxPTR ")",
|
||||
(uintptr_t)chan,
|
||||
(uintptr_t)sptr);
|
||||
|
||||
I(dom, chan);
|
||||
I(dom, sptr);
|
||||
|
||||
rust_port *port = chan->port;
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"send to port", (uintptr_t)port);
|
||||
I(dom, port);
|
||||
|
||||
rust_token *token = &chan->token;
|
||||
dom->log(rust_log::MEM|rust_log::COMM,
|
||||
"sending via token 0x%" PRIxPTR,
|
||||
(uintptr_t)token);
|
||||
|
||||
if (port->task) {
|
||||
chan->buffer.push(sptr);
|
||||
task->block(token);
|
||||
attempt_transmission(dom, chan, port->task);
|
||||
if (chan->buffer.unread && !token->pending())
|
||||
token->submit();
|
||||
} else {
|
||||
dom->log(rust_log::COMM|rust_log::ERR,
|
||||
"port has no task (possibly throw?)");
|
||||
}
|
||||
|
||||
if (!task->running())
|
||||
task->yield(3);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
||||
"upcall recv(dptr=0x" PRIxPTR ", port=0x%" PRIxPTR ")",
|
||||
(uintptr_t)dptr,
|
||||
(uintptr_t)port);
|
||||
|
||||
I(dom, port);
|
||||
I(dom, port->task);
|
||||
I(dom, task);
|
||||
I(dom, port->task == task);
|
||||
|
||||
task->block(port);
|
||||
|
||||
if (port->writers.length() > 0) {
|
||||
I(dom, task->dom);
|
||||
size_t i = rand(&dom->rctx);
|
||||
i %= port->writers.length();
|
||||
rust_token *token = port->writers[i];
|
||||
rust_chan *chan = token->chan;
|
||||
if (attempt_transmission(dom, chan, task))
|
||||
token->withdraw();
|
||||
} else {
|
||||
dom->log(rust_log::COMM,
|
||||
"no writers sending to port", (uintptr_t)port);
|
||||
}
|
||||
|
||||
if (!task->running()) {
|
||||
task->dptr = dptr;
|
||||
task->yield(3);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_fail(rust_task *task, char const *expr, char const *file, size_t line)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::ERR,
|
||||
"upcall fail '%s', %s:%" PRIdPTR,
|
||||
expr, file, line);
|
||||
task->fail(4);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_kill(rust_task *task, rust_task *target)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::TASK,
|
||||
"upcall kill target=0x%" PRIxPTR, target);
|
||||
target->kill();
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_exit(rust_task *task)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::TASK, "upcall exit");
|
||||
task->die();
|
||||
task->notify_waiting_tasks();
|
||||
task->yield(1);
|
||||
}
|
||||
|
||||
extern "C" CDECL uintptr_t
|
||||
upcall_malloc(rust_task *task, size_t nbytes)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
|
||||
void *p = task->dom->malloc(nbytes);
|
||||
task->dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall malloc(%u) = 0x%" PRIxPTR,
|
||||
nbytes, (uintptr_t)p);
|
||||
return (uintptr_t) p;
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
upcall_free(rust_task *task, void* ptr)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall free(0x%" PRIxPTR ")",
|
||||
(uintptr_t)ptr);
|
||||
dom->free(ptr);
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_str *
|
||||
upcall_new_str(rust_task *task, char const *s, size_t fill)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall new_str('%s', %" PRIdPTR ")", s, fill);
|
||||
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(3);
|
||||
return NULL;
|
||||
}
|
||||
rust_str *st = new (mem) rust_str(dom, alloc, fill, (uint8_t const *)s);
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
|
||||
s, fill, st);
|
||||
return st;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_vec *
|
||||
upcall_new_vec(rust_task *task, size_t fill)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall new_vec(%" PRIdPTR ")", fill);
|
||||
size_t alloc = next_power_of_two(sizeof(rust_vec) + fill);
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(3);
|
||||
return NULL;
|
||||
}
|
||||
rust_vec *v = new (mem) rust_vec(dom, alloc, 0, NULL);
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall new_vec(%" PRIdPTR ") = 0x%" PRIxPTR,
|
||||
fill, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
extern "C" CDECL rust_str *
|
||||
upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall vec_grow(%" PRIxPTR ", %" PRIdPTR ")", v, n_bytes);
|
||||
size_t alloc = next_power_of_two(sizeof(rust_vec) + v->fill + n_bytes);
|
||||
if (v->refcnt == 1) {
|
||||
|
||||
// Fastest path: already large enough.
|
||||
if (v->alloc >= alloc) {
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM, "no-growth path");
|
||||
return v;
|
||||
}
|
||||
|
||||
// Second-fastest path: can at least realloc.
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM, "realloc path");
|
||||
v = (rust_vec*)dom->realloc(v, alloc);
|
||||
if (!v) {
|
||||
task->fail(3);
|
||||
return NULL;
|
||||
}
|
||||
v->alloc = alloc;
|
||||
|
||||
} else {
|
||||
// Slowest path: make a new vec.
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM, "new vec path");
|
||||
void *mem = dom->malloc(alloc);
|
||||
if (!mem) {
|
||||
task->fail(3);
|
||||
return NULL;
|
||||
}
|
||||
v->deref();
|
||||
v = new (mem) rust_vec(dom, alloc, v->fill, &v->data[0]);
|
||||
}
|
||||
I(dom, sizeof(rust_vec) + v->fill <= v->alloc);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
static rust_crate_cache::c_sym *
|
||||
fetch_c_sym(rust_task *task,
|
||||
rust_crate const *curr_crate,
|
||||
size_t lib_num,
|
||||
size_t c_sym_num,
|
||||
char const *library,
|
||||
char const *symbol)
|
||||
{
|
||||
rust_crate_cache *cache = task->get_crate_cache(curr_crate);
|
||||
rust_crate_cache::lib *l = cache->get_lib(lib_num, library);
|
||||
return cache->get_c_sym(c_sym_num, l, symbol);
|
||||
}
|
||||
|
||||
extern "C" CDECL uintptr_t
|
||||
upcall_require_rust_sym(rust_task *task,
|
||||
rust_crate const *curr_crate,
|
||||
size_t lib_num, // # of lib
|
||||
size_t c_sym_num, // # of C sym "rust_crate" in lib
|
||||
size_t rust_sym_num, // # of rust sym
|
||||
char const *library,
|
||||
char const **path)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"upcall require rust sym: lib #%" PRIdPTR
|
||||
" = %s, c_sym #%" PRIdPTR
|
||||
", rust_sym #%" PRIdPTR
|
||||
", curr_crate = 0x%" PRIxPTR,
|
||||
lib_num, library, c_sym_num, rust_sym_num,
|
||||
curr_crate);
|
||||
for (char const **c = crate_rel(curr_crate, path); *c; ++c) {
|
||||
dom->log(rust_log::UPCALL, " + %s", crate_rel(curr_crate, *c));
|
||||
}
|
||||
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"require C symbol 'rust_crate' from lib #%" PRIdPTR,lib_num);
|
||||
rust_crate_cache::c_sym *c =
|
||||
fetch_c_sym(task, curr_crate, lib_num, c_sym_num,
|
||||
library, "rust_crate");
|
||||
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"require rust symbol inside crate");
|
||||
rust_crate_cache::rust_sym *s =
|
||||
task->cache->get_rust_sym(rust_sym_num, dom, curr_crate, c, path);
|
||||
|
||||
uintptr_t addr = s->get_val();
|
||||
if (addr) {
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
||||
} else {
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"failed to resolve symbol");
|
||||
task->fail(7);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
extern "C" CDECL uintptr_t
|
||||
upcall_require_c_sym(rust_task *task,
|
||||
rust_crate const *curr_crate,
|
||||
size_t lib_num, // # of lib
|
||||
size_t c_sym_num, // # of C sym
|
||||
char const *library,
|
||||
char const *symbol)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"upcall require c sym: lib #%" PRIdPTR
|
||||
" = %s, c_sym #%" PRIdPTR
|
||||
" = %s"
|
||||
", curr_crate = 0x%" PRIxPTR,
|
||||
lib_num, library, c_sym_num, symbol, curr_crate);
|
||||
|
||||
rust_crate_cache::c_sym *c =
|
||||
fetch_c_sym(task, curr_crate, lib_num, c_sym_num, library, symbol);
|
||||
|
||||
uintptr_t addr = c->get_val();
|
||||
if (addr) {
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
||||
} else {
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"failed to resolve symbol");
|
||||
task->fail(6);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
extern "C" CDECL type_desc *
|
||||
upcall_get_type_desc(rust_task *task,
|
||||
rust_crate const *curr_crate,
|
||||
size_t size,
|
||||
size_t align,
|
||||
size_t n_descs,
|
||||
type_desc const **descs)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
rust_dom *dom = task->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"upcall get_type_desc with size=%" PRIdPTR
|
||||
", align=%" PRIdPTR ", %" PRIdPTR " descs",
|
||||
size, align, n_descs);
|
||||
rust_crate_cache *cache = task->get_crate_cache(curr_crate);
|
||||
type_desc *td = cache->get_type_desc(size, align, n_descs, descs);
|
||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
||||
"returning tydesc 0x%" PRIxPTR, td);
|
||||
return td;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__WIN32__)
|
||||
static DWORD WINAPI rust_thread_start(void *ptr)
|
||||
#elif defined(__GNUC__)
|
||||
static void *rust_thread_start(void *ptr)
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
{
|
||||
// We were handed the domain we are supposed to run.
|
||||
rust_dom *dom = (rust_dom *)ptr;
|
||||
|
||||
// Start a new rust main loop for this thread.
|
||||
rust_main_loop(dom);
|
||||
|
||||
rust_srv *srv = dom->srv;
|
||||
delete dom;
|
||||
delete srv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_task *
|
||||
upcall_new_task(rust_task *spawner)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(spawner);
|
||||
|
||||
rust_dom *dom = spawner->dom;
|
||||
rust_task *task = new (dom) rust_task(dom, spawner);
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
||||
"upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR,
|
||||
spawner, task);
|
||||
return task;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_task *
|
||||
upcall_start_task(rust_task *spawner,
|
||||
rust_task *task,
|
||||
uintptr_t exit_task_glue,
|
||||
uintptr_t spawnee_fn,
|
||||
size_t callsz)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(spawner);
|
||||
|
||||
rust_dom *dom = spawner->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
||||
"upcall start_task(task 0x%" PRIxPTR
|
||||
" exit_task_glue 0x%" PRIxPTR
|
||||
", spawnee 0x%" PRIxPTR
|
||||
", callsz %" PRIdPTR ")",
|
||||
task, exit_task_glue, spawnee_fn, callsz);
|
||||
task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
||||
return task;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_task *
|
||||
upcall_new_thread(rust_task *task)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(task);
|
||||
|
||||
rust_dom *old_dom = task->dom;
|
||||
rust_dom *new_dom = new rust_dom(old_dom->srv->clone(),
|
||||
old_dom->root_crate);
|
||||
new_dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||
"upcall new_thread() = 0x%" PRIxPTR,
|
||||
new_dom->root_task);
|
||||
return new_dom->root_task;
|
||||
}
|
||||
|
||||
extern "C" CDECL rust_task *
|
||||
upcall_start_thread(rust_task *spawner,
|
||||
rust_task *root_task,
|
||||
uintptr_t exit_task_glue,
|
||||
uintptr_t spawnee_fn,
|
||||
size_t callsz)
|
||||
{
|
||||
LOG_UPCALL_ENTRY(spawner);
|
||||
|
||||
rust_dom *dom = spawner->dom;
|
||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
||||
"upcall start_thread(exit_task_glue 0x%" PRIxPTR
|
||||
", spawnee 0x%" PRIxPTR
|
||||
", callsz %" PRIdPTR ")",
|
||||
exit_task_glue, spawnee_fn, callsz);
|
||||
root_task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
||||
|
||||
#if defined(__WIN32__)
|
||||
HANDLE thread;
|
||||
thread = CreateThread(NULL, 0, rust_thread_start, root_task->dom,
|
||||
0, NULL);
|
||||
dom->win32_require("CreateThread", thread != NULL);
|
||||
#else
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, &dom->attr, rust_thread_start,
|
||||
(void *)root_task->dom);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
155
src/rt/rust_util.h
Normal file
155
src/rt/rust_util.h
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef RUST_UTIL_H
|
||||
#define RUST_UTIL_H
|
||||
|
||||
// Reference counted objects
|
||||
|
||||
template <typename T>
|
||||
rc_base<T>::rc_base() :
|
||||
refcnt(1)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
rc_base<T>::~rc_base()
|
||||
{
|
||||
}
|
||||
|
||||
// Utility type: pointer-vector.
|
||||
|
||||
template <typename T>
|
||||
ptr_vec<T>::ptr_vec(rust_dom *dom) :
|
||||
dom(dom),
|
||||
alloc(INIT_SIZE),
|
||||
fill(0),
|
||||
data(new (dom) T*[alloc])
|
||||
{
|
||||
I(dom, data);
|
||||
dom->log(rust_log::MEM,
|
||||
"new ptr_vec(data=0x%" PRIxPTR ") -> 0x%" PRIxPTR,
|
||||
(uintptr_t)data, (uintptr_t)this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ptr_vec<T>::~ptr_vec()
|
||||
{
|
||||
I(dom, data);
|
||||
dom->log(rust_log::MEM,
|
||||
"~ptr_vec 0x%" PRIxPTR ", data=0x%" PRIxPTR,
|
||||
(uintptr_t)this, (uintptr_t)data);
|
||||
I(dom, fill == 0);
|
||||
dom->free(data);
|
||||
}
|
||||
|
||||
template <typename T> T *&
|
||||
ptr_vec<T>::operator[](size_t offset) {
|
||||
I(dom, data[offset]->idx == offset);
|
||||
return data[offset];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
ptr_vec<T>::push(T *p)
|
||||
{
|
||||
I(dom, data);
|
||||
I(dom, fill <= alloc);
|
||||
if (fill == alloc) {
|
||||
alloc *= 2;
|
||||
data = (T **)dom->realloc(data, alloc * sizeof(T*));
|
||||
I(dom, data);
|
||||
}
|
||||
I(dom, fill < alloc);
|
||||
p->idx = fill;
|
||||
data[fill++] = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *
|
||||
ptr_vec<T>::pop()
|
||||
{
|
||||
return data[--fill];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
ptr_vec<T>::trim(size_t sz)
|
||||
{
|
||||
I(dom, data);
|
||||
if (sz <= (alloc / 4) &&
|
||||
(alloc / 2) >= INIT_SIZE) {
|
||||
alloc /= 2;
|
||||
I(dom, alloc >= fill);
|
||||
data = (T **)dom->realloc(data, alloc * sizeof(T*));
|
||||
I(dom, data);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
ptr_vec<T>::swapdel(T *item)
|
||||
{
|
||||
/* Swap the endpoint into i and decr fill. */
|
||||
I(dom, data);
|
||||
I(dom, fill > 0);
|
||||
I(dom, item->idx < fill);
|
||||
fill--;
|
||||
if (fill > 0) {
|
||||
T *subst = data[fill];
|
||||
size_t idx = item->idx;
|
||||
data[idx] = subst;
|
||||
subst->idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Inline fn used regularly elsewhere.
|
||||
|
||||
static inline size_t
|
||||
next_power_of_two(size_t s)
|
||||
{
|
||||
size_t tmp = s - 1;
|
||||
tmp |= tmp >> 1;
|
||||
tmp |= tmp >> 2;
|
||||
tmp |= tmp >> 4;
|
||||
tmp |= tmp >> 8;
|
||||
tmp |= tmp >> 16;
|
||||
#if SIZE_MAX == UINT64_MAX
|
||||
tmp |= tmp >> 32;
|
||||
#endif
|
||||
return tmp + 1;
|
||||
}
|
||||
|
||||
// Vectors (rust-user-code level).
|
||||
|
||||
struct
|
||||
rust_vec : public rc_base<rust_vec>
|
||||
{
|
||||
size_t alloc;
|
||||
size_t fill;
|
||||
uint8_t data[];
|
||||
rust_vec(rust_dom *dom, size_t alloc, size_t fill, uint8_t const *d) :
|
||||
alloc(alloc),
|
||||
fill(fill)
|
||||
{
|
||||
if (d || fill) {
|
||||
I(dom, d);
|
||||
I(dom, fill);
|
||||
memcpy(&data[0], d, fill);
|
||||
}
|
||||
}
|
||||
~rust_vec() {}
|
||||
};
|
||||
|
||||
// Rust types vec and str look identical from our perspective.
|
||||
typedef rust_vec rust_str;
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
||||
|
||||
#endif
|
43
src/rt/sync/fair_ticket_lock.cpp
Normal file
43
src/rt/sync/fair_ticket_lock.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This works well as long as the number of contending threads
|
||||
* is less than the number of processors. This is because of
|
||||
* the fair locking scheme. If the thread that is next in line
|
||||
* for acquiring the lock is not currently running, no other
|
||||
* thread can acquire the lock. This is terrible for performance,
|
||||
* and it seems that all fair locking schemes suffer from this
|
||||
* behavior.
|
||||
*/
|
||||
|
||||
// #define TRACE
|
||||
|
||||
fair_ticket_lock::fair_ticket_lock() {
|
||||
next_ticket = now_serving = 0;
|
||||
}
|
||||
|
||||
fair_ticket_lock::~fair_ticket_lock() {
|
||||
|
||||
}
|
||||
|
||||
void fair_ticket_lock::lock() {
|
||||
unsigned ticket = __sync_fetch_and_add(&next_ticket, 1);
|
||||
while (now_serving != ticket) {
|
||||
pause();
|
||||
}
|
||||
#ifdef TRACE
|
||||
printf("locked nextTicket: %d nowServing: %d",
|
||||
next_ticket, now_serving);
|
||||
#endif
|
||||
}
|
||||
|
||||
void fair_ticket_lock::unlock() {
|
||||
now_serving++;
|
||||
#ifdef TRACE
|
||||
printf("unlocked nextTicket: %d nowServing: %d",
|
||||
next_ticket, now_serving);
|
||||
#endif
|
||||
}
|
||||
|
||||
void fair_ticket_lock::pause() {
|
||||
asm volatile("pause\n" : : : "memory");
|
||||
}
|
||||
|
15
src/rt/sync/fair_ticket_lock.h
Normal file
15
src/rt/sync/fair_ticket_lock.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef FAIR_TICKET_LOCK_H
|
||||
#define FAIR_TICKET_LOCK_H
|
||||
|
||||
class fair_ticket_lock {
|
||||
unsigned next_ticket;
|
||||
unsigned now_serving;
|
||||
void pause();
|
||||
public:
|
||||
fair_ticket_lock();
|
||||
virtual ~fair_ticket_lock();
|
||||
void lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
#endif /* FAIR_TICKET_LOCK_H */
|
37
src/rt/sync/lock_free_queue.cpp
Normal file
37
src/rt/sync/lock_free_queue.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Interrupt transparent queue, Schoen et. al, "On Interrupt-Transparent
|
||||
* Synchronization in an Embedded Object-Oriented Operating System", 2000.
|
||||
* enqueue() is allowed to interrupt enqueue() and dequeue(), however,
|
||||
* dequeue() is not allowed to interrupt itself.
|
||||
*/
|
||||
|
||||
#include "lock_free_queue.h"
|
||||
|
||||
lock_free_queue::lock_free_queue() :
|
||||
tail(this) {
|
||||
}
|
||||
|
||||
void lock_free_queue::enqueue(lock_free_queue_node *item) {
|
||||
item->next = (lock_free_queue_node *) 0;
|
||||
lock_free_queue_node *last = tail;
|
||||
tail = item;
|
||||
while (last->next)
|
||||
last = last->next;
|
||||
last->next = item;
|
||||
}
|
||||
|
||||
lock_free_queue_node *lockfree_queue::dequeue() {
|
||||
lock_free_queue_node *item = next;
|
||||
if (item && !(next = item->next)) {
|
||||
tail = (lock_free_queue_node *) this;
|
||||
if (item->next) {
|
||||
lock_free_queue_node *lost = item->next;
|
||||
lock_free_queue_node *help;
|
||||
do {
|
||||
help = lost->next;
|
||||
enqueue(lost);
|
||||
} while ((lost = help) != (lock_free_queue_node *) 0);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
15
src/rt/sync/lock_free_queue.h
Normal file
15
src/rt/sync/lock_free_queue.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef LOCK_FREE_QUEUE_H
|
||||
#define LOCK_FREE_QUEUE_H
|
||||
|
||||
class lock_free_queue_node {
|
||||
lock_free_queue_node *next;
|
||||
};
|
||||
|
||||
class lock_free_queue {
|
||||
public:
|
||||
lock_free_queue();
|
||||
void enqueue(lock_free_queue_node *item);
|
||||
lock_free_queue_node *dequeue();
|
||||
};
|
||||
|
||||
#endif /* LOCK_FREE_QUEUE_H */
|
47
src/rt/sync/spin_lock.cpp
Normal file
47
src/rt/sync/spin_lock.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Your average spin lock.
|
||||
*/
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
// #define TRACE
|
||||
|
||||
spin_lock::spin_lock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
spin_lock::~spin_lock() {
|
||||
}
|
||||
|
||||
static inline unsigned xchg32(void *ptr, unsigned x) {
|
||||
__asm__ __volatile__("xchgl %0,%1"
|
||||
:"=r" ((unsigned) x)
|
||||
:"m" (*(volatile unsigned *)ptr), "0" (x)
|
||||
:"memory");
|
||||
return x;
|
||||
}
|
||||
|
||||
void spin_lock::lock() {
|
||||
while (true) {
|
||||
if (!xchg32(&ticket, 1)) {
|
||||
return;
|
||||
}
|
||||
while (ticket) {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
#ifdef TRACE
|
||||
printf(" lock: %d", ticket);
|
||||
#endif
|
||||
}
|
||||
|
||||
void spin_lock::unlock() {
|
||||
ticket = 0;
|
||||
#ifdef TRACE
|
||||
printf("unlock:");
|
||||
#endif
|
||||
}
|
||||
|
||||
void spin_lock::pause() {
|
||||
asm volatile("pause\n" : : : "memory");
|
||||
}
|
14
src/rt/sync/spin_lock.h
Normal file
14
src/rt/sync/spin_lock.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef UNFAIR_TICKET_LOCK_H
|
||||
#define UNFAIR_TICKET_LOCK_H
|
||||
|
||||
class spin_lock {
|
||||
unsigned ticket;
|
||||
void pause();
|
||||
public:
|
||||
spin_lock();
|
||||
virtual ~spin_lock();
|
||||
void lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
#endif /* UNFAIR_TICKET_LOCK_H */
|
766
src/rt/uthash/uthash.h
Normal file
766
src/rt/uthash/uthash.h
Normal file
@ -0,0 +1,766 @@
|
||||
/*
|
||||
Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTHASH_H
|
||||
#define UTHASH_H
|
||||
|
||||
#include <string.h> /* memcmp,strlen */
|
||||
#include <stddef.h> /* ptrdiff_t */
|
||||
#include <inttypes.h> /* uint32_t etc */
|
||||
|
||||
#define UTHASH_VERSION 1.6
|
||||
|
||||
/* C++ requires extra stringent casting */
|
||||
#if defined __cplusplus
|
||||
#define TYPEOF(x) (typeof(x))
|
||||
#else
|
||||
#define TYPEOF(x)
|
||||
#endif
|
||||
|
||||
|
||||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
|
||||
#define uthash_bkt_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_bucket's */
|
||||
#define uthash_bkt_free(ptr) free(ptr) /* free fcn for UT_hash_bucket's */
|
||||
#define uthash_tbl_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_table */
|
||||
#define uthash_tbl_free(ptr) free(ptr) /* free fcn for UT_hash_table */
|
||||
|
||||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
|
||||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
|
||||
|
||||
/* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
|
||||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
|
||||
|
||||
/* calculate the element whose hash handle address is hhe */
|
||||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho))
|
||||
|
||||
#define HASH_FIND(hh,head,keyptr,keylen,out) \
|
||||
do { \
|
||||
unsigned _hf_bkt,_hf_hashv; \
|
||||
out=TYPEOF(out)head; \
|
||||
if (head) { \
|
||||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
|
||||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
|
||||
keyptr,keylen,out); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HASH_MAKE_TABLE(hh,head) \
|
||||
do { \
|
||||
(head)->hh.tbl = (UT_hash_table*)uthash_tbl_malloc( \
|
||||
sizeof(UT_hash_table)); \
|
||||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
|
||||
(head)->hh.tbl->tail = &((head)->hh); \
|
||||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
|
||||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
|
||||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
|
||||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_bkt_malloc( \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl->buckets, 0, \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
|
||||
HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
|
||||
|
||||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
|
||||
do { \
|
||||
unsigned _ha_bkt; \
|
||||
(add)->hh.next = NULL; \
|
||||
(add)->hh.key = (char*)keyptr; \
|
||||
(add)->hh.keylen = keylen_in; \
|
||||
if (!(head)) { \
|
||||
head = (add); \
|
||||
(head)->hh.prev = NULL; \
|
||||
HASH_MAKE_TABLE(hh,head); \
|
||||
} else { \
|
||||
(head)->hh.tbl->tail->next = (add); \
|
||||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
|
||||
(head)->hh.tbl->tail = &((add)->hh); \
|
||||
} \
|
||||
(head)->hh.tbl->num_items++; \
|
||||
(add)->hh.tbl = (head)->hh.tbl; \
|
||||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
|
||||
(add)->hh.hashv, _ha_bkt); \
|
||||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
|
||||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
|
||||
do { \
|
||||
bkt = ((hashv) & ((num_bkts) - 1)); \
|
||||
} while(0)
|
||||
|
||||
/* delete "delptr" from the hash table.
|
||||
* "the usual" patch-up process for the app-order doubly-linked-list.
|
||||
* The use of _hd_hh_del below deserves special explanation.
|
||||
* These used to be expressed using (delptr) but that led to a bug
|
||||
* if someone used the same symbol for the head and deletee, like
|
||||
* HASH_DELETE(hh,users,users);
|
||||
* We want that to work, but by changing the head (users) below
|
||||
* we were forfeiting our ability to further refer to the deletee (users)
|
||||
* in the patch-up process. Solution: use scratch space in the table to
|
||||
* copy the deletee pointer, then the latter references are via that
|
||||
* scratch pointer rather than through the repointed (users) symbol.
|
||||
*/
|
||||
#define HASH_DELETE(hh,head,delptr) \
|
||||
do { \
|
||||
unsigned _hd_bkt; \
|
||||
struct UT_hash_handle *_hd_hh_del; \
|
||||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
|
||||
uthash_bkt_free((head)->hh.tbl->buckets ); \
|
||||
uthash_tbl_free((head)->hh.tbl); \
|
||||
head = NULL; \
|
||||
} else { \
|
||||
_hd_hh_del = &((delptr)->hh); \
|
||||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
|
||||
(head)->hh.tbl->tail = \
|
||||
(UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho); \
|
||||
} \
|
||||
if ((delptr)->hh.prev) { \
|
||||
((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
|
||||
} else { \
|
||||
head = TYPEOF(head)((delptr)->hh.next); \
|
||||
} \
|
||||
if (_hd_hh_del->next) { \
|
||||
((UT_hash_handle*)((char*)_hd_hh_del->next + \
|
||||
(head)->hh.tbl->hho))->prev = \
|
||||
_hd_hh_del->prev; \
|
||||
} \
|
||||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
|
||||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
|
||||
(head)->hh.tbl->num_items--; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
|
||||
#define HASH_FIND_STR(head,findstr,out) \
|
||||
HASH_FIND(hh,head,findstr,strlen(findstr),out)
|
||||
#define HASH_ADD_STR(head,strfield,add) \
|
||||
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
|
||||
#define HASH_FIND_INT(head,findint,out) \
|
||||
HASH_FIND(hh,head,findint,sizeof(int),out)
|
||||
#define HASH_ADD_INT(head,intfield,add) \
|
||||
HASH_ADD(hh,head,intfield,sizeof(int),add)
|
||||
#define HASH_DEL(head,delptr) \
|
||||
HASH_DELETE(hh,head,delptr)
|
||||
|
||||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
||||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
|
||||
*/
|
||||
#ifdef HASH_DEBUG
|
||||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
|
||||
#define HASH_FSCK(hh,head) \
|
||||
do { \
|
||||
unsigned _bkt_i; \
|
||||
unsigned _count, _bkt_count; \
|
||||
char *_prev; \
|
||||
struct UT_hash_handle *_thh; \
|
||||
if (head) { \
|
||||
_count = 0; \
|
||||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
|
||||
_bkt_count = 0; \
|
||||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
|
||||
_prev = NULL; \
|
||||
while (_thh) { \
|
||||
if (_prev != (char*)(_thh->hh_prev)) { \
|
||||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
|
||||
_thh->hh_prev, _prev ); \
|
||||
} \
|
||||
_bkt_count++; \
|
||||
_prev = (char*)(_thh); \
|
||||
_thh = _thh->hh_next; \
|
||||
} \
|
||||
_count += _bkt_count; \
|
||||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
|
||||
HASH_OOPS("invalid bucket count %d, actual %d\n", \
|
||||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
|
||||
} \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid hh item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
/* traverse hh in app order; check next/prev integrity, count */ \
|
||||
_count = 0; \
|
||||
_prev = NULL; \
|
||||
_thh = &(head)->hh; \
|
||||
while (_thh) { \
|
||||
_count++; \
|
||||
if (_prev !=(char*)(_thh->prev)) { \
|
||||
HASH_OOPS("invalid prev %p, actual %p\n", \
|
||||
_thh->prev, _prev ); \
|
||||
} \
|
||||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
|
||||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
|
||||
(head)->hh.tbl->hho) : NULL ); \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid app item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_FSCK(hh,head)
|
||||
#endif
|
||||
|
||||
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
|
||||
* the descriptor to which this macro is defined for tuning the hash function.
|
||||
* The app can #include <unistd.h> to get the prototype for write(2). */
|
||||
#ifdef HASH_EMIT_KEYS
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
|
||||
do { \
|
||||
unsigned _klen = fieldlen; \
|
||||
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
|
||||
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
|
||||
#endif
|
||||
|
||||
/* default to MurmurHash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
|
||||
#ifdef HASH_FUNCTION
|
||||
#define HASH_FCN HASH_FUNCTION
|
||||
#else
|
||||
#define HASH_FCN HASH_MUR
|
||||
#endif
|
||||
|
||||
/* The Bernstein hash function, used in Perl prior to v5.6 */
|
||||
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hb_keylen=keylen; \
|
||||
char *_hb_key=(char*)key; \
|
||||
(hashv) = 0; \
|
||||
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
|
||||
bkt = (hashv) & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
|
||||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
|
||||
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _sx_i; \
|
||||
char *_hs_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
|
||||
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _fn_i; \
|
||||
char *_hf_key=(char*)key; \
|
||||
hashv = 2166136261UL; \
|
||||
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
|
||||
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _ho_i; \
|
||||
char *_ho_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
|
||||
hashv += _ho_key[_ho_i]; \
|
||||
hashv += (hashv << 10); \
|
||||
hashv ^= (hashv >> 6); \
|
||||
} \
|
||||
hashv += (hashv << 3); \
|
||||
hashv ^= (hashv >> 11); \
|
||||
hashv += (hashv << 15); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_JEN_MIX(a,b,c) \
|
||||
do { \
|
||||
a -= b; a -= c; a ^= ( c >> 13 ); \
|
||||
b -= c; b -= a; b ^= ( a << 8 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 13 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 12 ); \
|
||||
b -= c; b -= a; b ^= ( a << 16 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 5 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 3 ); \
|
||||
b -= c; b -= a; b ^= ( a << 10 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 15 ); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hj_i,_hj_j,_hj_k; \
|
||||
char *_hj_key=(char*)key; \
|
||||
hashv = 0xfeedbeef; \
|
||||
_hj_i = _hj_j = 0x9e3779b9; \
|
||||
_hj_k = keylen; \
|
||||
while (_hj_k >= 12) { \
|
||||
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[2] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[3] << 24 ) ); \
|
||||
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[6] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[7] << 24 ) ); \
|
||||
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[10] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[11] << 24 ) ); \
|
||||
\
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
\
|
||||
_hj_key += 12; \
|
||||
_hj_k -= 12; \
|
||||
} \
|
||||
hashv += keylen; \
|
||||
switch ( _hj_k ) { \
|
||||
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
|
||||
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
|
||||
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
|
||||
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
|
||||
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
|
||||
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
|
||||
case 5: _hj_j += _hj_key[4]; \
|
||||
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
|
||||
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
|
||||
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
|
||||
case 1: _hj_i += _hj_key[0]; \
|
||||
} \
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* The Paul Hsieh hash function */
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
char *_sfh_key=(char*)key; \
|
||||
hashv = 0xcafebabe; \
|
||||
uint32_t _sfh_tmp, _sfh_len = keylen; \
|
||||
\
|
||||
int _sfh_rem = _sfh_len & 3; \
|
||||
_sfh_len >>= 2; \
|
||||
\
|
||||
/* Main loop */ \
|
||||
for (;_sfh_len > 0; _sfh_len--) { \
|
||||
hashv += get16bits (_sfh_key); \
|
||||
_sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
|
||||
hashv = (hashv << 16) ^ _sfh_tmp; \
|
||||
_sfh_key += 2*sizeof (uint16_t); \
|
||||
hashv += hashv >> 11; \
|
||||
} \
|
||||
\
|
||||
/* Handle end cases */ \
|
||||
switch (_sfh_rem) { \
|
||||
case 3: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 16; \
|
||||
hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
|
||||
hashv += hashv >> 11; \
|
||||
break; \
|
||||
case 2: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 11; \
|
||||
hashv += hashv >> 17; \
|
||||
break; \
|
||||
case 1: hashv += *_sfh_key; \
|
||||
hashv ^= hashv << 10; \
|
||||
hashv += hashv >> 1; \
|
||||
} \
|
||||
\
|
||||
/* Force "avalanching" of final 127 bits */ \
|
||||
hashv ^= hashv << 3; \
|
||||
hashv += hashv >> 5; \
|
||||
hashv ^= hashv << 4; \
|
||||
hashv += hashv >> 17; \
|
||||
hashv ^= hashv << 25; \
|
||||
hashv += hashv >> 6; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
/* Austin Appleby's MurmurHash */
|
||||
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const unsigned int _mur_m = 0x5bd1e995; \
|
||||
const int _mur_r = 24; \
|
||||
hashv = 0xcafebabe ^ keylen; \
|
||||
char *_mur_key = (char *)key; \
|
||||
uint32_t _mur_tmp, _mur_len = keylen; \
|
||||
\
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
_mur_tmp = *(uint32_t *)_mur_key; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
_mur_tmp ^= _mur_tmp >> _mur_r; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_tmp; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
\
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
}; \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
\
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* key comparison function; return 0 if keys equal */
|
||||
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
|
||||
|
||||
/* iterate over items in a known bucket to find desired item */
|
||||
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
|
||||
out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \
|
||||
while (out) { \
|
||||
if (out->hh.keylen == keylen_in) { \
|
||||
if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
|
||||
} \
|
||||
out= TYPEOF(out)((out->hh.hh_next) ? \
|
||||
ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \
|
||||
}
|
||||
|
||||
/* add an item to a bucket */
|
||||
#define HASH_ADD_TO_BKT(head,addhh) \
|
||||
do { \
|
||||
head.count++; \
|
||||
(addhh)->hh_next = head.hh_head; \
|
||||
(addhh)->hh_prev = NULL; \
|
||||
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
|
||||
(head).hh_head=addhh; \
|
||||
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
|
||||
&& (addhh)->tbl->noexpand != 1) { \
|
||||
HASH_EXPAND_BUCKETS((addhh)->tbl); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* remove an item from a given bucket */
|
||||
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
|
||||
(head).count--; \
|
||||
if ((head).hh_head == hh_del) { \
|
||||
(head).hh_head = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_prev) { \
|
||||
hh_del->hh_prev->hh_next = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_next) { \
|
||||
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
|
||||
}
|
||||
|
||||
/* Bucket expansion has the effect of doubling the number of buckets
|
||||
* and redistributing the items into the new buckets. Ideally the
|
||||
* items will distribute more or less evenly into the new buckets
|
||||
* (the extent to which this is true is a measure of the quality of
|
||||
* the hash function as it applies to the key domain).
|
||||
*
|
||||
* With the items distributed into more buckets, the chain length
|
||||
* (item count) in each bucket is reduced. Thus by expanding buckets
|
||||
* the hash keeps a bound on the chain length. This bounded chain
|
||||
* length is the essence of how a hash provides constant time lookup.
|
||||
*
|
||||
* The calculation of tbl->ideal_chain_maxlen below deserves some
|
||||
* explanation. First, keep in mind that we're calculating the ideal
|
||||
* maximum chain length based on the *new* (doubled) bucket count.
|
||||
* In fractions this is just n/b (n=number of items,b=new num buckets).
|
||||
* Since the ideal chain length is an integer, we want to calculate
|
||||
* ceil(n/b). We don't depend on floating point arithmetic in this
|
||||
* hash, so to calculate ceil(n/b) with integers we could write
|
||||
*
|
||||
* ceil(n/b) = (n/b) + ((n%b)?1:0)
|
||||
*
|
||||
* and in fact a previous version of this hash did just that.
|
||||
* But now we have improved things a bit by recognizing that b is
|
||||
* always a power of two. We keep its base 2 log handy (call it lb),
|
||||
* so now we can write this with a bit shift and logical AND:
|
||||
*
|
||||
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
|
||||
*
|
||||
*/
|
||||
#define HASH_EXPAND_BUCKETS(tbl) \
|
||||
do { \
|
||||
unsigned _he_bkt; \
|
||||
unsigned _he_bkt_i; \
|
||||
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
|
||||
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
|
||||
_he_new_buckets = (UT_hash_bucket*)uthash_bkt_malloc( \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset(_he_new_buckets, 0, \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
tbl->ideal_chain_maxlen = \
|
||||
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
|
||||
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
|
||||
tbl->nonideal_items = 0; \
|
||||
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
|
||||
{ \
|
||||
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
|
||||
while (_he_thh) { \
|
||||
_he_hh_nxt = _he_thh->hh_next; \
|
||||
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
|
||||
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
|
||||
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
|
||||
tbl->nonideal_items++; \
|
||||
_he_newbkt->expand_mult = _he_newbkt->count / \
|
||||
tbl->ideal_chain_maxlen; \
|
||||
} \
|
||||
_he_thh->hh_prev = NULL; \
|
||||
_he_thh->hh_next = _he_newbkt->hh_head; \
|
||||
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
|
||||
_he_thh; \
|
||||
_he_newbkt->hh_head = _he_thh; \
|
||||
_he_thh = _he_hh_nxt; \
|
||||
} \
|
||||
} \
|
||||
tbl->num_buckets *= 2; \
|
||||
tbl->log2_num_buckets++; \
|
||||
uthash_bkt_free( tbl->buckets ); \
|
||||
tbl->buckets = _he_new_buckets; \
|
||||
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
|
||||
(tbl->ineff_expands+1) : 0; \
|
||||
if (tbl->ineff_expands > 1) { \
|
||||
tbl->noexpand=1; \
|
||||
uthash_noexpand_fyi(tbl); \
|
||||
} \
|
||||
uthash_expand_fyi(tbl); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
|
||||
/* Note that HASH_SORT assumes the hash handle name to be hh.
|
||||
* HASH_SRT was added to allow the hash handle name to be passed in. */
|
||||
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
|
||||
#define HASH_SRT(hh,head,cmpfcn) \
|
||||
do { \
|
||||
unsigned _hs_i; \
|
||||
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
|
||||
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
|
||||
if (head) { \
|
||||
_hs_insize = 1; \
|
||||
_hs_looping = 1; \
|
||||
_hs_list = &((head)->hh); \
|
||||
while (_hs_looping) { \
|
||||
_hs_p = _hs_list; \
|
||||
_hs_list = NULL; \
|
||||
_hs_tail = NULL; \
|
||||
_hs_nmerges = 0; \
|
||||
while (_hs_p) { \
|
||||
_hs_nmerges++; \
|
||||
_hs_q = _hs_p; \
|
||||
_hs_psize = 0; \
|
||||
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
|
||||
_hs_psize++; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
if (! (_hs_q) ) break; \
|
||||
} \
|
||||
_hs_qsize = _hs_insize; \
|
||||
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
|
||||
if (_hs_psize == 0) { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else if (( \
|
||||
cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
|
||||
TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
|
||||
) <= 0) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} \
|
||||
if ( _hs_tail ) { \
|
||||
_hs_tail->next = ((_hs_e) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
|
||||
} else { \
|
||||
_hs_list = _hs_e; \
|
||||
} \
|
||||
_hs_e->prev = ((_hs_tail) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
|
||||
_hs_tail = _hs_e; \
|
||||
} \
|
||||
_hs_p = _hs_q; \
|
||||
} \
|
||||
_hs_tail->next = NULL; \
|
||||
if ( _hs_nmerges <= 1 ) { \
|
||||
_hs_looping=0; \
|
||||
(head)->hh.tbl->tail = _hs_tail; \
|
||||
(head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \
|
||||
} \
|
||||
_hs_insize *= 2; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* This function selects items from one hash into another hash.
|
||||
* The end result is that the selected items have dual presence
|
||||
* in both hashes. There is no copy of the items made; rather
|
||||
* they are added into the new hash through a secondary hash
|
||||
* hash handle that must be present in the structure. */
|
||||
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
|
||||
do { \
|
||||
unsigned _src_bkt, _dst_bkt; \
|
||||
void *_last_elt=NULL, *_elt; \
|
||||
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
|
||||
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
|
||||
if (src) { \
|
||||
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
|
||||
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
|
||||
_src_hh; \
|
||||
_src_hh = _src_hh->hh_next) { \
|
||||
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
|
||||
if (cond(_elt)) { \
|
||||
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
|
||||
_dst_hh->key = _src_hh->key; \
|
||||
_dst_hh->keylen = _src_hh->keylen; \
|
||||
_dst_hh->hashv = _src_hh->hashv; \
|
||||
_dst_hh->prev = _last_elt; \
|
||||
_dst_hh->next = NULL; \
|
||||
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
|
||||
if (!dst) { \
|
||||
dst = TYPEOF(dst)_elt; \
|
||||
HASH_MAKE_TABLE(hh_dst,dst); \
|
||||
} else { \
|
||||
_dst_hh->tbl = (dst)->hh_dst.tbl; \
|
||||
} \
|
||||
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
|
||||
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
|
||||
(dst)->hh_dst.tbl->num_items++; \
|
||||
_last_elt = _elt; \
|
||||
_last_elt_hh = _dst_hh; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
HASH_FSCK(hh_dst,dst); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_CLEAR(hh,head) \
|
||||
do { \
|
||||
if (head) { \
|
||||
uthash_bkt_free((head)->hh.tbl->buckets ); \
|
||||
uthash_tbl_free((head)->hh.tbl); \
|
||||
(head)=NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* obtain a count of items in the hash */
|
||||
#define HASH_COUNT(head) HASH_CNT(hh,head)
|
||||
#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
|
||||
|
||||
typedef struct UT_hash_bucket {
|
||||
struct UT_hash_handle *hh_head;
|
||||
unsigned count;
|
||||
|
||||
/* expand_mult is normally set to 0. In this situation, the max chain length
|
||||
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
|
||||
* the bucket's chain exceeds this length, bucket expansion is triggered).
|
||||
* However, setting expand_mult to a non-zero value delays bucket expansion
|
||||
* (that would be triggered by additions to this particular bucket)
|
||||
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
|
||||
* (The multiplier is simply expand_mult+1). The whole idea of this
|
||||
* multiplier is to reduce bucket expansions, since they are expensive, in
|
||||
* situations where we know that a particular bucket tends to be overused.
|
||||
* It is better to let its chain length grow to a longer yet-still-bounded
|
||||
* value, than to do an O(n) bucket expansion too often.
|
||||
*/
|
||||
unsigned expand_mult;
|
||||
|
||||
} UT_hash_bucket;
|
||||
|
||||
typedef struct UT_hash_table {
|
||||
UT_hash_bucket *buckets;
|
||||
unsigned num_buckets, log2_num_buckets;
|
||||
unsigned num_items;
|
||||
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
|
||||
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
|
||||
|
||||
/* in an ideal situation (all buckets used equally), no bucket would have
|
||||
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
|
||||
unsigned ideal_chain_maxlen;
|
||||
|
||||
/* nonideal_items is the number of items in the hash whose chain position
|
||||
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
|
||||
* hash distribution; reaching them in a chain traversal takes >ideal steps */
|
||||
unsigned nonideal_items;
|
||||
|
||||
/* ineffective expands occur when a bucket doubling was performed, but
|
||||
* afterward, more than half the items in the hash had nonideal chain
|
||||
* positions. If this happens on two consecutive expansions we inhibit any
|
||||
* further expansion, as it's not helping; this happens when the hash
|
||||
* function isn't a good fit for the key domain. When expansion is inhibited
|
||||
* the hash will still work, albeit no longer in constant time. */
|
||||
unsigned ineff_expands, noexpand;
|
||||
|
||||
|
||||
} UT_hash_table;
|
||||
|
||||
|
||||
typedef struct UT_hash_handle {
|
||||
struct UT_hash_table *tbl;
|
||||
void *prev; /* prev element in app order */
|
||||
void *next; /* next element in app order */
|
||||
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
|
||||
struct UT_hash_handle *hh_next; /* next hh in bucket order */
|
||||
void *key; /* ptr to enclosing struct's key */
|
||||
unsigned keylen; /* enclosing struct's key len */
|
||||
unsigned hashv; /* result of hash-fcn(key) */
|
||||
} UT_hash_handle;
|
||||
|
||||
#endif /* UTHASH_H */
|
280
src/rt/uthash/utlist.h
Normal file
280
src/rt/uthash/utlist.h
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright (c) 2007-2009, Troy D. Hanson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTLIST_H
|
||||
#define UTLIST_H
|
||||
|
||||
#define UTLIST_VERSION 1.0
|
||||
|
||||
/* C++ requires extra stringent casting */
|
||||
#if defined __cplusplus
|
||||
#define LTYPEOF(x) (typeof(x))
|
||||
#else
|
||||
#define LTYPEOF(x)
|
||||
#endif
|
||||
/*
|
||||
* This file contains macros to manipulate singly and doubly-linked lists.
|
||||
*
|
||||
* 1. LL_ macros: singly-linked lists.
|
||||
* 2. DL_ macros: doubly-linked lists.
|
||||
* 3. CDL_ macros: circular doubly-linked lists.
|
||||
*
|
||||
* To use singly-linked lists, your structure must have a "next" pointer.
|
||||
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
|
||||
* Either way, the pointer to the head of the list must be initialized to NULL.
|
||||
*
|
||||
* ----------------.EXAMPLE -------------------------
|
||||
* struct item {
|
||||
* int id;
|
||||
* struct item *prev, *next;
|
||||
* }
|
||||
*
|
||||
* struct item *list = NULL:
|
||||
*
|
||||
* int main() {
|
||||
* struct item *item;
|
||||
* ... allocate and populate item ...
|
||||
* DL_APPEND(list, item);
|
||||
* }
|
||||
* --------------------------------------------------
|
||||
*
|
||||
* For doubly-linked lists, the append and delete macros are O(1)
|
||||
* For singly-linked lists, append and delete are O(n) but prepend is O(1)
|
||||
* The sort macro is O(n log(n)) for all types of single/double/circular lists.
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* The SORT macros *
|
||||
*****************************************************************************/
|
||||
#define LL_SORT(l,cmp) \
|
||||
LISTSORT(l,0,0,FIELD_OFFSET(l,next),cmp)
|
||||
#define DL_SORT(l,cmp) \
|
||||
LISTSORT(l,0,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp)
|
||||
#define CDL_SORT(l,cmp) \
|
||||
LISTSORT(l,1,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp)
|
||||
|
||||
/* The macros can't assume or cast to the caller's list element type. So we use
|
||||
* a couple tricks when we need to deal with those element's prev/next pointers.
|
||||
* Basically we use char pointer arithmetic to get those field offsets. */
|
||||
#define FIELD_OFFSET(ptr,field) ((char*)&((ptr)->field) - (char*)(ptr))
|
||||
#define LNEXT(e,no) (*(char**)(((char*)e) + no))
|
||||
#define LPREV(e,po) (*(char**)(((char*)e) + po))
|
||||
/******************************************************************************
|
||||
* The LISTSORT macro is an adaptation of Simon Tatham's O(n log(n)) mergesort*
|
||||
* Unwieldy variable names used here to avoid shadowing passed-in variables. *
|
||||
*****************************************************************************/
|
||||
#define LISTSORT(list, is_circular, po, no, cmp) \
|
||||
do { \
|
||||
void *_ls_p, *_ls_q, *_ls_e, *_ls_tail, *_ls_oldhead; \
|
||||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
||||
int _ls_is_double = (po==0) ? 0 : 1; \
|
||||
if (list) { \
|
||||
_ls_insize = 1; \
|
||||
_ls_looping = 1; \
|
||||
while (_ls_looping) { \
|
||||
_ls_p = list; \
|
||||
_ls_oldhead = list; \
|
||||
list = NULL; \
|
||||
_ls_tail = NULL; \
|
||||
_ls_nmerges = 0; \
|
||||
while (_ls_p) { \
|
||||
_ls_nmerges++; \
|
||||
_ls_q = _ls_p; \
|
||||
_ls_psize = 0; \
|
||||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
||||
_ls_psize++; \
|
||||
if (is_circular) { \
|
||||
_ls_q = ((LNEXT(_ls_q,no) == _ls_oldhead) ? NULL : LNEXT(_ls_q,no)); \
|
||||
} else { \
|
||||
_ls_q = LNEXT(_ls_q,no); \
|
||||
} \
|
||||
if (!_ls_q) break; \
|
||||
} \
|
||||
_ls_qsize = _ls_insize; \
|
||||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
||||
if (_ls_psize == 0) { \
|
||||
_ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \
|
||||
if (is_circular && _ls_q == _ls_oldhead) { _ls_q = NULL; } \
|
||||
} else if (_ls_qsize == 0 || !_ls_q) { \
|
||||
_ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \
|
||||
if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \
|
||||
} else if (cmp(LTYPEOF(list)_ls_p,LTYPEOF(list)_ls_q) <= 0) { \
|
||||
_ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \
|
||||
if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \
|
||||
} else { \
|
||||
_ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \
|
||||
if (is_circular && (_ls_q == _ls_oldhead)) { _ls_q = NULL; } \
|
||||
} \
|
||||
if (_ls_tail) { \
|
||||
LNEXT(_ls_tail,no) = (char*)_ls_e; \
|
||||
} else { \
|
||||
list = LTYPEOF(list)_ls_e; \
|
||||
} \
|
||||
if (_ls_is_double) { \
|
||||
LPREV(_ls_e,po) = (char*)_ls_tail; \
|
||||
} \
|
||||
_ls_tail = _ls_e; \
|
||||
} \
|
||||
_ls_p = _ls_q; \
|
||||
} \
|
||||
if (is_circular) { \
|
||||
LNEXT(_ls_tail,no) = (char*)list; \
|
||||
if (_ls_is_double) { \
|
||||
LPREV(list,po) = (char*)_ls_tail; \
|
||||
} \
|
||||
} else { \
|
||||
LNEXT(_ls_tail,no) = NULL; \
|
||||
} \
|
||||
if (_ls_nmerges <= 1) { \
|
||||
_ls_looping=0; \
|
||||
} \
|
||||
_ls_insize *= 2; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/******************************************************************************
|
||||
* singly linked list macros (non-circular) *
|
||||
*****************************************************************************/
|
||||
#define LL_PREPEND(head,add) \
|
||||
do { \
|
||||
(add)->next = head; \
|
||||
head = add; \
|
||||
} while (0)
|
||||
|
||||
#define LL_APPEND(head,add) \
|
||||
do { \
|
||||
(add)->next=NULL; \
|
||||
if (head) { \
|
||||
char *_lla_el = (char*)(head); \
|
||||
unsigned _lla_no = FIELD_OFFSET(head,next); \
|
||||
while (LNEXT(_lla_el,_lla_no)) { _lla_el = LNEXT(_lla_el,_lla_no); } \
|
||||
LNEXT(_lla_el,_lla_no)=(char*)(add); \
|
||||
} else { \
|
||||
(head)=(add); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LL_DELETE(head,del) \
|
||||
do { \
|
||||
if ((head) == (del)) { \
|
||||
(head)=(head)->next; \
|
||||
} else { \
|
||||
char *_lld_el = (char*)(head); \
|
||||
unsigned _lld_no = FIELD_OFFSET(head,next); \
|
||||
while (LNEXT(_lld_el,_lld_no) && (LNEXT(_lld_el,_lld_no) != (char*)(del))) { \
|
||||
_lld_el = LNEXT(_lld_el,_lld_no); \
|
||||
} \
|
||||
if (LNEXT(_lld_el,_lld_no)) { \
|
||||
LNEXT(_lld_el,_lld_no) = (char*)((del)->next); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LL_FOREACH(head,el) \
|
||||
for(el=head;el;el=el->next)
|
||||
|
||||
/******************************************************************************
|
||||
* doubly linked list macros (non-circular) *
|
||||
*****************************************************************************/
|
||||
#define DL_PREPEND(head,add) \
|
||||
do { \
|
||||
(add)->next = head; \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(head)->prev = (add); \
|
||||
} else { \
|
||||
(add)->prev = (add); \
|
||||
} \
|
||||
(head) = (add); \
|
||||
} while (0)
|
||||
|
||||
#define DL_APPEND(head,add) \
|
||||
do { \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(head)->prev->next = (add); \
|
||||
(head)->prev = (add); \
|
||||
(add)->next = NULL; \
|
||||
} else { \
|
||||
(head)=(add); \
|
||||
(head)->prev = (head); \
|
||||
(head)->next = NULL; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define DL_DELETE(head,del) \
|
||||
do { \
|
||||
if ((del)->prev == (del)) { \
|
||||
(head)=NULL; \
|
||||
} else if ((del)==(head)) { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
(head) = (del)->next; \
|
||||
} else { \
|
||||
(del)->prev->next = (del)->next; \
|
||||
if ((del)->next) { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
} else { \
|
||||
(head)->prev = (del)->prev; \
|
||||
} \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
|
||||
#define DL_FOREACH(head,el) \
|
||||
for(el=head;el;el=el->next)
|
||||
|
||||
/******************************************************************************
|
||||
* circular doubly linked list macros *
|
||||
*****************************************************************************/
|
||||
#define CDL_PREPEND(head,add) \
|
||||
do { \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(add)->next = (head); \
|
||||
(head)->prev = (add); \
|
||||
(add)->prev->next = (add); \
|
||||
} else { \
|
||||
(add)->prev = (add); \
|
||||
(add)->next = (add); \
|
||||
} \
|
||||
(head)=(add); \
|
||||
} while (0)
|
||||
|
||||
#define CDL_DELETE(head,del) \
|
||||
do { \
|
||||
if ( ((head)==(del)) && ((head)->next == (head))) { \
|
||||
(head) = 0L; \
|
||||
} else { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
(del)->prev->next = (del)->next; \
|
||||
if ((del) == (head)) (head)=(del)->next; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define CDL_FOREACH(head,el) \
|
||||
for(el=head;el;el= (el->next==head ? 0L : el->next))
|
||||
|
||||
|
||||
#endif /* UTLIST_H */
|
||||
|
69
src/rt/util/array_list.h
Normal file
69
src/rt/util/array_list.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef ARRAY_LIST_H
|
||||
#define ARRAY_LIST_H
|
||||
|
||||
/**
|
||||
* A simple, resizable array list.
|
||||
*/
|
||||
template<typename T> class array_list {
|
||||
static const size_t INITIAL_CAPACITY = 8;
|
||||
size_t _size;
|
||||
T * _data;
|
||||
size_t _capacity;
|
||||
public:
|
||||
array_list();
|
||||
~array_list();
|
||||
size_t size();
|
||||
void append(T value);
|
||||
T replace(T old_value, T new_value);
|
||||
size_t index_of(T value);
|
||||
T & operator[](size_t index);
|
||||
};
|
||||
|
||||
template<typename T> array_list<T>::array_list() {
|
||||
_capacity = INITIAL_CAPACITY;
|
||||
_data = (T *) malloc(sizeof(T) * _capacity);
|
||||
}
|
||||
|
||||
template<typename T> array_list<T>::~array_list() {
|
||||
delete _data;
|
||||
}
|
||||
|
||||
template<typename T> size_t array_list<T>::size() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
template<typename T> void array_list<T>::append(T value) {
|
||||
if (_size == _capacity) {
|
||||
_capacity = _capacity * 2;
|
||||
_data = (T *) realloc(_data, _capacity * sizeof(T));
|
||||
}
|
||||
_data[_size++] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the old_value in the list with the new_value.
|
||||
* Returns the old_value if the replacement succeeded, or NULL otherwise.
|
||||
*/
|
||||
template<typename T> T array_list<T>::replace(T old_value, T new_value) {
|
||||
int index = index_of(old_value);
|
||||
if (index < 0) {
|
||||
return NULL;
|
||||
}
|
||||
_data[index] = new_value;
|
||||
return old_value;
|
||||
}
|
||||
|
||||
template<typename T> size_t array_list<T>::index_of(T value) {
|
||||
for (size_t i = 0; i < _size; i++) {
|
||||
if (_data[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<typename T> T & array_list<T>::operator[](size_t index) {
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
#endif /* ARRAY_LIST_H */
|
3926
src/rt/valgrind.h
Normal file
3926
src/rt/valgrind.h
Normal file
File diff suppressed because it is too large
Load Diff
25
src/test/bench/shootout/ackermann.rs
Normal file
25
src/test/bench/shootout/ackermann.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// -*- rust -*-
|
||||
|
||||
fn ack(int m, int n) -> int {
|
||||
if (m == 0) {
|
||||
ret n+1;
|
||||
} else {
|
||||
if (n == 0) {
|
||||
ret ack(m-1, 1);
|
||||
} else {
|
||||
ret ack(m-1, ack(m, n-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
check (ack(0,0) == 1);
|
||||
check (ack(3,2) == 29);
|
||||
check (ack(3,4) == 125);
|
||||
|
||||
// This takes a while; but a comparison may amuse: on win32 at least, the
|
||||
// posted C version of the 'benchmark' running ack(4,1) overruns its stack
|
||||
// segment and crashes. We just grow our stack (to 4mb) as we go.
|
||||
|
||||
// check (ack(4,1) == 65533);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user