mirror of https://github.com/rust-lang/rust.git
Remove rustboot from the repository.
This commit is contained in:
parent
ef75860a0a
commit
6997adf763
|
@ -176,7 +176,6 @@ fi
|
|||
step_msg "making directories"
|
||||
for i in \
|
||||
doc \
|
||||
boot/fe boot/me boot/be boot/driver boot/util \
|
||||
rt rt/isaac rt/bigint rt/sync rt/test \
|
||||
rustllvm \
|
||||
dl stage0 stage1 stage2 stage3 \
|
||||
|
@ -203,11 +202,6 @@ probe CFG_CLANG clang++
|
|||
probe CFG_GCC gcc
|
||||
probe CFG_LLVM_CONFIG llvm-config
|
||||
probe CFG_VALGRIND valgrind
|
||||
probe CFG_OCAMLC ocamlc
|
||||
probe CFG_OCAMLOPT ocamlopt
|
||||
probe CFG_OCAMLC_OPT ocamlc.opt
|
||||
probe CFG_OCAMLOPT_OPT ocamlopt.opt
|
||||
probe CFG_FLEXLINK flexlink
|
||||
probe CFG_MAKEINFO makeinfo
|
||||
probe CFG_TEXI2PDF texi2pdf
|
||||
probe CFG_TEX tex
|
||||
|
|
98
mk/boot.mk
98
mk/boot.mk
|
@ -1,98 +0,0 @@
|
|||
######################################################################
|
||||
# Bootstrap compiler variables and rules
|
||||
######################################################################
|
||||
|
||||
ifdef CFG_BOOT_PROFILE
|
||||
$(info cfg: forcing native bootstrap compiler (CFG_BOOT_PROFILE))
|
||||
CFG_BOOT_NATIVE := 1
|
||||
CFG_OCAMLOPT_PROFILE_FLAGS := -p
|
||||
endif
|
||||
|
||||
ifdef CFG_BOOT_DEBUG
|
||||
$(info cfg: forcing bytecode bootstrap compiler (CFG_BOOT_DEBUG))
|
||||
CFG_BOOT_NATIVE :=
|
||||
endif
|
||||
|
||||
ifdef CFG_BOOT_NATIVE
|
||||
$(info cfg: building native bootstrap compiler)
|
||||
else
|
||||
$(info cfg: building bytecode bootstrap compiler)
|
||||
endif
|
||||
|
||||
GENERATED := boot/fe/lexer.ml boot/version.ml
|
||||
|
||||
|
||||
# We must list them in link order.
|
||||
# Nobody calculates the link-order DAG automatically, sadly.
|
||||
|
||||
BOOT_MLS := \
|
||||
$(addsuffix .ml, \
|
||||
boot/version \
|
||||
$(addprefix boot/util/, fmt common bits) \
|
||||
$(addprefix boot/driver/, session) \
|
||||
$(addprefix boot/fe/, ast token lexer parser \
|
||||
extfmt pexp item cexp fuzz) \
|
||||
$(addprefix boot/be/, asm il abi) \
|
||||
$(addprefix boot/me/, walk semant resolve alias \
|
||||
simplify type dead layer typestate \
|
||||
loop layout transutil trans dwarf) \
|
||||
$(addprefix boot/be/, x86 ra pe elf macho) \
|
||||
$(addprefix boot/driver/, lib glue main)) \
|
||||
|
||||
BOOT_CMOS := $(BOOT_MLS:.ml=.cmo)
|
||||
BOOT_CMXS := $(BOOT_MLS:.ml=.cmx)
|
||||
BOOT_OBJS := $(BOOT_MLS:.ml=.o)
|
||||
BOOT_CMIS := $(BOOT_MLS:.ml=.cmi)
|
||||
|
||||
BS := $(S)src/boot
|
||||
|
||||
BOOT_ML_DEP_INCS := -I $(BS)/fe -I $(BS)/me \
|
||||
-I $(BS)/be -I $(BS)/driver \
|
||||
-I $(BS)/util -I boot
|
||||
|
||||
BOOT_ML_INCS := -I boot/fe -I boot/me \
|
||||
-I boot/be -I boot/driver \
|
||||
-I boot/util -I boot
|
||||
|
||||
BOOT_ML_LIBS := unix.cma nums.cma bigarray.cma
|
||||
BOOT_ML_NATIVE_LIBS := unix.cmxa nums.cmxa bigarray.cmxa
|
||||
BOOT_OCAMLC_FLAGS := -g $(BOOT_ML_INCS) -w Ael -warn-error Ael
|
||||
BOOT_OCAMLOPT_FLAGS := -g $(BOOT_ML_INCS) -w Ael -warn-error Ael
|
||||
|
||||
ifdef CFG_FLEXLINK
|
||||
BOOT_OCAMLOPT_FLAGS += -cclib -L/usr/lib
|
||||
endif
|
||||
|
||||
BOOT := $(Q)OCAMLRUNPARAM="b1" boot/rustboot$(X) $(CFG_BOOT_FLAGS) -L stage0
|
||||
|
||||
|
||||
ifdef CFG_BOOT_NATIVE
|
||||
boot/rustboot$(X): $(BOOT_CMXS) $(MKFILES)
|
||||
@$(call E, link: $@)
|
||||
$(Q)ocamlopt$(OPT) -o $@ $(BOOT_OCAMLOPT_FLAGS) $(BOOT_ML_NATIVE_LIBS) \
|
||||
$(BOOT_CMXS)
|
||||
else
|
||||
boot/rustboot$(X): $(BOOT_CMOS) $(MKFILES)
|
||||
@$(call E, link: $@)
|
||||
$(Q)ocamlc$(OPT) -o $@ $(BOOT_OCAMLC_FLAGS) $(BOOT_ML_LIBS) $(BOOT_CMOS)
|
||||
endif
|
||||
|
||||
boot/version.ml: $(MKFILES)
|
||||
@$(call E, git: $@)
|
||||
$(Q)(cd $(S) && git log -1 \
|
||||
--pretty=format:'let version = "prerelease (%h %ci)";;') >$@ || exit 1
|
||||
|
||||
%.cmo: %.ml $(MKFILES)
|
||||
@$(call E, compile: $@)
|
||||
$(Q)ocamlc$(OPT) -c -o $@ $(BOOT_OCAMLC_FLAGS) $<
|
||||
|
||||
%.cmo: %.cmi $(MKFILES)
|
||||
|
||||
%.cmx %.o: %.ml $(MKFILES)
|
||||
@$(call E, compile: $@)
|
||||
$(Q)ocamlopt$(OPT) -c -o $@ $(BOOT_OCAMLOPT_FLAGS) $<
|
||||
|
||||
%.ml: %.mll $(MKFILES)
|
||||
@$(call E, lex-gen: $@)
|
||||
$(Q)ocamllex$(OPT) -q -o $@ $<
|
||||
|
|
@ -17,7 +17,6 @@ clean:
|
|||
@$(call E, cleaning)
|
||||
$(Q)rm -f $(RUNTIME_OBJS) $(RUNTIME_DEF)
|
||||
$(Q)rm -f $(RUSTLLVM_LIB_OBJS) $(RUSTLLVM_OBJS_OBJS) $(RUSTLLVM_DEF)
|
||||
$(Q)rm -f $(BOOT_CMOS) $(BOOT_CMIS) $(BOOT_CMXS) $(BOOT_OBJS)
|
||||
$(Q)rm -f $(ML_DEPFILES) $(C_DEPFILES) $(CRATE_DEPFILES)
|
||||
$(Q)rm -f $(ML_DEPFILES:%.d=%.d.tmp)
|
||||
$(Q)rm -f $(C_DEPFILES:%.d=%.d.tmp)
|
||||
|
|
|
@ -87,9 +87,6 @@ ifdef CFG_WINDOWSY
|
|||
CFG_PATH_MUNGE := $(strip perl -i.bak -p \
|
||||
-e 's@\\(\S)@/\1@go;' \
|
||||
-e 's@^/([a-zA-Z])/@\1:/@o;')
|
||||
ifdef CFG_FLEXLINK
|
||||
CFG_BOOT_NATIVE := 1
|
||||
endif
|
||||
CFG_GCCISH_CFLAGS += -march=i686 -O2
|
||||
CFG_GCCISH_LINK_FLAGS += -shared -fPIC
|
||||
CFG_DEF_SUFFIX := .def
|
||||
|
@ -106,8 +103,6 @@ ifdef CFG_UNIXY
|
|||
$(CFG_LDENV)=$(call CFG_TESTLIB,$(1)):$(CFG_LDPATH) \
|
||||
$(CFG_VALGRIND) $(1)
|
||||
|
||||
CFG_BOOT_NATIVE := 1
|
||||
|
||||
ifdef MINGW_CROSS
|
||||
CFG_EXE_SUFFIX := .exe
|
||||
CFG_LIB_NAME=$(1).dll
|
||||
|
@ -118,7 +113,6 @@ ifdef CFG_UNIXY
|
|||
|
||||
CFG_INFO := $(info cfg: mingw-cross)
|
||||
CFG_GCCISH_CROSS := i586-mingw32msvc-
|
||||
CFG_BOOT_FLAGS += -t win32-x86-pe
|
||||
ifdef CFG_VALGRIND
|
||||
CFG_VALGRIND += wine
|
||||
endif
|
||||
|
|
11
src/README
11
src/README
|
@ -2,16 +2,7 @@ This is preliminary version of the Rust compiler(s).
|
|||
|
||||
Source layout:
|
||||
|
||||
boot/ The bootstrap compiler
|
||||
boot/README - More-detailed guide to it.
|
||||
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/driver - Compiler driver
|
||||
|
||||
comp/ The self-hosted compiler ("rustc": incomplete)
|
||||
comp/* - Similar structure as in boot/
|
||||
comp/ The self-hosted compiler
|
||||
|
||||
rt/ The runtime system
|
||||
rt/rust_*.cpp - The majority of the runtime services
|
||||
|
|
405
src/boot/README
405
src/boot/README
|
@ -1,405 +0,0 @@
|
|||
An informal guide to reading and working on the rustboot compiler.
|
||||
==================================================================
|
||||
|
||||
First off, know that our current state of development is "bootstrapping";
|
||||
this means we've got two compilers on the go and one of them is being used
|
||||
to develop the other. Rustboot is written in ocaml and rustc in rust. The
|
||||
one you *probably* ought to be working on at present is rustc. Rustboot is
|
||||
more for historical comparison and bug-fixing whenever necessary to un-block
|
||||
development of rustc.
|
||||
|
||||
There's a document similar to this next door, then, in comp/README. The
|
||||
comp directory is where we do work on rustc.
|
||||
|
||||
If you wish to expand on this document, or have one of the
|
||||
slightly-more-familiar authors add anything else to it, please get in touch or
|
||||
file a bug. Your concerns are probably the same as someone else's.
|
||||
|
||||
|
||||
|
||||
High-level concepts, invariants, 30,000-ft view
|
||||
===============================================
|
||||
|
||||
Rustboot has 3 main subdirectories: fe, me, and be (front, mid, back
|
||||
end). Helper modules and ubiquitous types are found in util/.
|
||||
|
||||
The entry-point for the compiler is driver/main.ml, and this file sequences
|
||||
the various parts together.
|
||||
|
||||
|
||||
The 4 central data structures:
|
||||
------------------------------
|
||||
|
||||
#1: fe/ast.ml defines the AST. The AST is treated as immutable after parsing
|
||||
despite containing some mutable types (hashtbl and such). Many -- though
|
||||
not all -- nodes within this data structure are wrapped in the type 'a
|
||||
identified. This is important. An "identified" AST node is one that the
|
||||
parser has marked with a unique node_id value. This node_id is used both
|
||||
to denote a source location and, more importantly, to key into a large
|
||||
number of tables later in the compiler. Most additional calculated
|
||||
properties of a program that the compiler derives are keyed to the node_id
|
||||
of an identified node.
|
||||
|
||||
The types 'a identified, node_id and such are in util/common.ml
|
||||
|
||||
|
||||
#2: me/semant.ml defines the Semant.ctxt structure. This is a record of
|
||||
tables, almost all of which are keyed by node_id. See previous comment
|
||||
regrding node_id. The Semant module is open in most of the modules within
|
||||
the me/ directory, and they all refer liberally to the ctxt tables, either
|
||||
directly or via helper functions in semant. Semant also defines the
|
||||
mid-end pass-management logic, lookup routines, type folds, and a variety
|
||||
of other miscallaneous semantic-analysis helpers.
|
||||
|
||||
|
||||
#3: be/il.ml defines the IL. This is a small, typed IL based on a type system
|
||||
that is relatively LLVM-ish, and a control-flow system that is *not*
|
||||
expression/SSA based like LLVM. It's much dumber than that. The root of
|
||||
the interesting types in this file is the type 'emitter', which is a
|
||||
growable buffer along with a few counters. An emitter is essentially a
|
||||
buffer of quads. A quad, in turn, is a primitive virtual instruction
|
||||
('quad' because it is in its limit a 3-address machine, plus opcode) which
|
||||
we then ... tend to turn directly into x86 anyways. Sorry; it wasn't clear
|
||||
during initial construction that we'd wind up stopping at x86, so the IL
|
||||
is probably superfluous, but there it is.
|
||||
|
||||
The IL types are operand = cell | immediate, and cell = reg | mem. Plus a
|
||||
certain quantity of special-casing and noise for constant-pointer
|
||||
propagation and addressing modes and whatnot.
|
||||
|
||||
|
||||
#4: be/asm.ml defines the Asm.frag type, which is a "chunk of binary-ish
|
||||
stuff" to put in an output file. Words, bytes, lazily-resolved fixups,
|
||||
constant expressions, 0-terminated strings, alignment boundaries, etc. You
|
||||
will hopefully not need to produce a lot of this yourself; most of this is
|
||||
already being emitted.
|
||||
|
||||
An important type that gets resolved here is fixup, from util/common.ml.
|
||||
Fixups are things you can wrap around a frag using an Asm.DEF frag, which
|
||||
get their size and position (both in-file and in-memory) calculated at
|
||||
asm-time; but you can refer to them before they're resolved. So any time
|
||||
the compiler needs to refer to "the place / size this thingy will be, when
|
||||
it finally gets boiled down to frags and emitted" we generate a fixup and
|
||||
use that. Functions and static data structures, for example, tend to get
|
||||
fixups assigned to them early on in the middle-end of the compiler.
|
||||
|
||||
|
||||
Control and information flow within the compiler:
|
||||
-------------------------------------------------
|
||||
|
||||
- driver/main.ml assumes control on startup. Options are parsed, platform is
|
||||
detected, etc.
|
||||
|
||||
|
||||
- fe/lexer.ml does lexing in any case; fe/parser.ml holds the fundamental
|
||||
parser-state and parser-combinator functions. Parsing rules are split
|
||||
between 3 files: fe/cexp.ml, fe/pexp.ml, and fe/item.ml. This split
|
||||
represents the general structure of the grammar(s):
|
||||
|
||||
- The outermost grammar is called "cexp" (crate expression), and is an
|
||||
expression language that describes the crate directives found in crate
|
||||
files. It's evaluated inside the compiler.
|
||||
|
||||
- The next grammar is "item", which is a statement language that describes
|
||||
the directives, declarations and statements found in source files. If
|
||||
you compile a naked source file, you jump straight to item and then
|
||||
synthesize a simple crate structure around the result.
|
||||
|
||||
- The innermost grammar is "pexp" (parsed expression), and is an
|
||||
expression language used for the shared expression grammar within both
|
||||
cexp and item. Pexps within cexp are evaluated in the compiler
|
||||
(non-constant, complex cexps are errors) whereas pexps within items are
|
||||
desugared to statements and primitive expressions.
|
||||
|
||||
- The AST is the output from the item grammar. Pexp and cexp do not escape
|
||||
the front-end.
|
||||
|
||||
|
||||
- driver/main.ml then builds a Semant.ctxt and threads it through the various
|
||||
middle-end passes. Each pass defines one or more visitors, which is an FRU
|
||||
copy of the empty_visitor in me/walk.ml. Each visitor performs a particular
|
||||
task, encapsulates some local state in local variables, and leaves its
|
||||
results in a table. If the table it's calculating is pass-local, it will be
|
||||
a local binding within the pass; if it's to be shared with later passes, it
|
||||
will be a table in Semant.ctxt. Pass order is therefore somewhat important,
|
||||
so I'll describe it here:
|
||||
|
||||
- me/resolve.ml looks up names and connects them to definitions. This
|
||||
includes expanding all types (as types can occur within names, as part
|
||||
of a parametric name) and performing all import/export/visibility
|
||||
judgments. After resolve, we should not be doing any further name-based
|
||||
lookups (with one exception: typestate does some more name
|
||||
lookup. Subtle reason, will return to it).
|
||||
|
||||
Resolve populates several of the tables near the top of Semant.ctxt:
|
||||
|
||||
ctxt_all_cast_types
|
||||
ctxt_all_defns
|
||||
ctxt_all_item_names
|
||||
ctxt_all_item_types
|
||||
ctxt_all_lvals
|
||||
ctxt_all_stmts
|
||||
ctxt_all_type_items
|
||||
ctxt_block_items
|
||||
ctxt_block_slots
|
||||
ctxt_frame_args
|
||||
ctxt_lval_to_referent
|
||||
ctxt_node_referenced
|
||||
ctxt_required_items
|
||||
ctxt_slot_is_arg
|
||||
ctxt_slot_keys
|
||||
|
||||
The most obviously critical of these are lval_to_referent and all_defns,
|
||||
which connect subsequent visitors from a reference node to its referent
|
||||
node, and catalogue all the possible things a referent may be.
|
||||
|
||||
Part of resolving that is perhaps not obvious is the task of resolving
|
||||
and normalizing recursive types. This is what TY_iso is for. Recursive
|
||||
types in rust have to pass through a tag type on their recursive edge;
|
||||
TY_iso is an iso-recursive group of tags that refer only to one another;
|
||||
within a TY_iso, the type term "TY_idx n" refers to "the nth member of
|
||||
the current TY_iso". Resolve is responsible for finding such groups and
|
||||
tying them into such closed-form knots.
|
||||
|
||||
TY_name should be completely eliminated in any of the types exiting
|
||||
resolve.
|
||||
|
||||
|
||||
- me/type.ml is a unification-based typechecker and inference engine. This
|
||||
is as textbook-y as we could make it. It rewrites "auto" slots in the
|
||||
ctxt_all_defns table when it completes (these are the slots with None as
|
||||
their Ast.slot_ty).
|
||||
|
||||
This file is organized around tyspecs and tyvars. A tyspec is a
|
||||
constraint on an unknown type that is implied by its use; tyspecs are
|
||||
generated during the AST-walk, placed in ref cells (tyvars), and the
|
||||
cells are and unified with one another. If two tyvars unify, then a new
|
||||
constraint is created with the tighter of the two and the two previous
|
||||
tyvars are updated to point to the unified spec. Ideally all constraints
|
||||
eventually run into a source of a concrete type (or a type otherwise
|
||||
uniquely-determined by its tyspecs). If not, the type is underdetermined
|
||||
and we get a type error. Similarly if two tyvars that are supposed to
|
||||
unify clash in some way (integer unify-with string, say) then there is
|
||||
also a type error.
|
||||
|
||||
|
||||
- me/typestate.ml is a dataflow-based typestate checker. It is responsible
|
||||
for ensuring all preconditions are met, including init-before-use. It
|
||||
also determines slot lifecycle boundaries, and populates the context
|
||||
tables:
|
||||
|
||||
ctxt_constr_ids
|
||||
ctxt_constrs
|
||||
ctxt_copy_stmt_is_init
|
||||
ctxt_post_stmt_slot_drops
|
||||
ctxt_postconditions
|
||||
ctxt_poststates
|
||||
ctxt_preconditions
|
||||
ctxt_prestates
|
||||
|
||||
It is organized around constr_keys, a bunch of bitsets, and a CFG.
|
||||
|
||||
A constr_key is a normalized value representing a single constraint that
|
||||
we wish to be able to refer to within a typestate. Every constr_key gets
|
||||
a bit number assigned to it. A condition (and a typestate) is a
|
||||
bit-vector, in which the set bits indicate the constr_keys (indexed by
|
||||
associatd number) that hold in the condition/typestate.
|
||||
|
||||
There are 4 such bitsets generated for each node in the CFG:
|
||||
precondition/postcondition and prestate/poststate. The visitors here
|
||||
figure out all the constr_keys we'll need, then assign all the pre/post
|
||||
conditions, generate the CFG, calculate the typestates from the CFG, and
|
||||
check that every typestate satisfies its precondition.
|
||||
|
||||
(Due to the peculiarity that types are pure terms and are not 'a
|
||||
identified in our AST, we have to do some name-lookup in here as well
|
||||
when normalizing the const_keys).
|
||||
|
||||
|
||||
- Effect is relatively simple: it calculates the effect of each type and
|
||||
item, and checks that they either match their declarations or are
|
||||
authorized to be lying.
|
||||
|
||||
|
||||
- Loop is even simpler: it calculates loop-depth information for later use
|
||||
generating foreach loops. It populates the context tables:
|
||||
|
||||
ctxt_block_is_loop_body
|
||||
ctxt_slot_loop_depths
|
||||
ctxt_stmt_loop_depths
|
||||
|
||||
|
||||
- Alias checks slot-aliasing to ensure none of the rules are broken about
|
||||
simultaneous aliases and such. It also populates the table
|
||||
ctxt_slot_is_aliased.
|
||||
|
||||
|
||||
- Layout determines the layout of frames, arguments, objects, closures and
|
||||
such. This includes deciding which slot should go in a vreg and
|
||||
generating fixups for all frame-spill regions. It populates the context
|
||||
tables:
|
||||
|
||||
ctxt_block_is_loop_body
|
||||
ctxt_call_sizes
|
||||
ctxt_frame_blocks
|
||||
ctxt_frame_sizes
|
||||
ctxt_slot_is_obj_state
|
||||
ctxt_slot_offsets
|
||||
ctxt_slot_vregs
|
||||
ctxt_spill_fixups
|
||||
|
||||
There is a useful chunk of ASCII-art in the leading comment of layout,
|
||||
if you want to see how a frame goes together, I recommend reading it.
|
||||
|
||||
|
||||
- Trans is the big one. This is the "translate AST to IL" pass, and it's a
|
||||
bit of a dumping ground, sadly. Probably 4x the size of any other
|
||||
pass. Stuff that is common to the x86 and LLVM backends is factored out
|
||||
into transutil.ml, but it hardly helps. Suggestions welcome for
|
||||
splitting it further.
|
||||
|
||||
Trans works *imperatively*. It maintains a stack of emitters, one per
|
||||
function (or helper-function) and emits Il.quads into the top-of-stack
|
||||
emitter into while it walks the statements of each function. If at any
|
||||
point it needs to pause to emit a helper function ("glue function") it
|
||||
pushes a new emitter onto the stack and emits into that.
|
||||
|
||||
Trans populates the context tables:
|
||||
|
||||
ctxt_all_item_code
|
||||
ctxt_block_fixups
|
||||
ctxt_data
|
||||
ctxt_file_code
|
||||
ctxt_file_fixups
|
||||
ctxt_fn_fixups
|
||||
ctxt_glue_code
|
||||
|
||||
The entries in the tables ending in _code are of type Semant.code, which
|
||||
is an abstract type covering both function and glue-function code; each
|
||||
holds an executable block of quads, plus an aggregate count of vregs and
|
||||
a reference to the spill fixup for that code.
|
||||
|
||||
|
||||
- Once it completes trans, driver/main.ml does the "finishing touches":
|
||||
register allocates each emitted code value (be/ra.ml), emits dwarf for the
|
||||
crate (me/dwarf.ml), selects instructions (be/x86.ml), then selects one of
|
||||
the object-file backends (be/elf.ml, be/macho.ml or be/pe.ml) and emits the
|
||||
selected Asm.frag to it. Hopefully little of this will require further work;
|
||||
the most incomplete module here is probably dwarf.ml but the remainder are
|
||||
mostly stable and don't tend to change much, aside from picking bugs out of
|
||||
them.
|
||||
|
||||
|
||||
|
||||
Details and curiosities to note along the way:
|
||||
==============================================
|
||||
|
||||
- Where you might expect there to be a general recursive expression type for
|
||||
'expr', you'll find only a very limited non-recursive 3-way switch: binary,
|
||||
unary, or atom; where atom is either a literal or an lval. This is because
|
||||
all the "big" expressions (pexps) were boiled off during the desugaring
|
||||
phase in the frontend.
|
||||
|
||||
|
||||
- There are multiple ways to refer to a path. Names, lvals and cargs all
|
||||
appear to have similar structure (and do). They're all subsets of the
|
||||
general path grammar, so all follow the rough shape of being either a base
|
||||
anchor-path or an ext (extension) path with structural recursion to the
|
||||
left.
|
||||
|
||||
Cargs (constraint arguments) are the sort of paths that can be passed to
|
||||
constraints in the typestate system, and can contain the special symbol "*"
|
||||
in the grammar, meaning "thing I am attached to". This is the symbol
|
||||
BASE_formal in the carg_base type.
|
||||
|
||||
Names are the sort of paths that refer to types or other items. Not slots.
|
||||
|
||||
Lvals are the sort of paths that *might* refer to slots, but we don't
|
||||
generally know. So they can contain the dynamic-indexing component
|
||||
COMP_atom. For example, x.(1 + 2).y is an lval.
|
||||
|
||||
|
||||
- Only one of these forms is 'a identified: an lval. And moreover, only the
|
||||
lval *base* is identified; the remainder of the path has to be projected
|
||||
forward through the referent after lookup. This also means that when you
|
||||
lookup anything else by name, you have to be using the result immediately,
|
||||
not storing it in a table for later.
|
||||
|
||||
|
||||
- Types are not 'a identified. This means that you (generally) cannot refer to
|
||||
a *particular* occurrence of a type in the AST and associate information
|
||||
with it. Instead, we treat types as "pure terms" (not carrying identity) and
|
||||
calculate properties of them on the fly. For this we use a general fold
|
||||
defined in me/semant.ml, the family of functions held in a ty_fold
|
||||
structure, and passed to fold_ty.
|
||||
|
||||
|
||||
- There is a possibly-surprising type called "size" in util/common. This is a
|
||||
type representing a "size expression" that may depend on runtime
|
||||
information, such as the type descriptors passed to a frame at runtime. This
|
||||
exists because our type-parameterization scheme is, at the moment,
|
||||
implemented by passing type descriptors around at runtime, not
|
||||
code-expansion a la C++ templates. So any time we have a translated indexing
|
||||
operation or such that depends on a type parameter, we wind up with a size
|
||||
expression including SIZE_param_size or SIZE_param_align, and have to do
|
||||
size arithmetic at runtime. Upstream of trans, we generate sizes willy-nilly
|
||||
and then decide in trans, x86, and dwarf whether they can be emitted
|
||||
statically or via runtime calculation at the point of use.
|
||||
|
||||
|
||||
- Trans generates position-independent code (PIC). This means that it never
|
||||
refers to the exact position of a fixup in memory at load-time, always the
|
||||
distance-to-a-fixup from some other fixup, and/or current PC. On x86 this
|
||||
means we wind up copying the "get next pc thunk" trick used on linux
|
||||
systems, and/or storing "crate relative" addresses. The runtime and compiler
|
||||
"know" (unfortunately sometimes quite obscurely) that an immediate pointer
|
||||
should be encoded as relative-to a given displacement base, and work with
|
||||
those as necessary. Similarly, they emit code to reify pointer immediates
|
||||
(add the displacements to displacement-bases) before handing them off to
|
||||
(say) C library functions that expect "real" pointers. This is all somewhat
|
||||
messy.
|
||||
|
||||
|
||||
- There is one central static data structure, "rust_crate", which is emitted
|
||||
into the final loadable object and contains pointers to all subsequent
|
||||
information the runtime may be interested in. It also serves as the
|
||||
displacement base for a variety of PIC-ish displacements stored
|
||||
elsewhere. When the runtime loads a crate, it dlsym()s rust_crate, and then
|
||||
digs around in there. It's the entry-point for crawling the crate's
|
||||
structure from outside. Importantly: it also contains pointers to the dwarf.
|
||||
|
||||
|
||||
- Currently we drive linking off dwarf. That is: when a crate needs to 'use'
|
||||
an item from another dwarf crate, we dlopen / LoadLibrary and find the
|
||||
"rust_crate" value, follow its pointers to dwarf tables, and scan around the
|
||||
dwarf DIE tree resolving the hierarchical name of the used item. This may
|
||||
change, we decided to recycle dwarf for this purpose early in the language
|
||||
evolution and may, given the number of simplifications that have occurred
|
||||
along the way, be able to fall back to C "mangled name" linkage at some
|
||||
point. Though that decision carries a number of serious constraints, and
|
||||
should not be taken lightly.
|
||||
|
||||
|
||||
|
||||
Probably-bad ideas we will want to do differently in the self-hosted compiler:
|
||||
==============================================================================
|
||||
|
||||
- We desugar too early in rustboot and should preserve the pexp structure
|
||||
until later. Dherman is likely to argue for movement to a more
|
||||
expression-focused grammar. This may well happen.
|
||||
|
||||
- Multiple kinds of paths enforced by numerous nearly-isomorphic ML type
|
||||
constructors is pointless once we're in rust; we can just make type
|
||||
abbreviations that carry constraints like path : is_name(*) or such.
|
||||
|
||||
- Storing auxiliary information in semant tables is awkward, and we should
|
||||
figure out a suitably rusty idiom for decorating AST nodes in-place.
|
||||
Inter-pass dependencies should be managed by augmenting the AST with
|
||||
ever-more constraints (is_resolved(ast), is_typechecked(ast), etc.)
|
||||
|
||||
- Trans should be organized as pure and value-producing code, not imperatively
|
||||
emitting quads into emitters. LLVM will enforce this anyways. See what
|
||||
happened in lltrans.ml if you're curious what it'll look (more) like.
|
||||
|
||||
- The PIC scheme will have to change, hopefully get much easier.
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
|
||||
(*
|
||||
* 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;;
|
||||
|
||||
(* FIXME: this needs updating if you ever want to work on 64 bit. *)
|
||||
let const_refcount = 0x7badfaceL;;
|
||||
|
||||
let task_field_refcnt = rc_base_field_refcnt;;
|
||||
let task_field_stk = task_field_refcnt + 2;;
|
||||
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 = 1;;
|
||||
|
||||
let frame_glue_fns_field_mark = 0;;
|
||||
let frame_glue_fns_field_drop = 1;;
|
||||
let frame_glue_fns_field_reloc = 2;;
|
||||
|
||||
let box_rc_field_refcnt = 0;;
|
||||
let box_rc_field_body = 1;;
|
||||
|
||||
let box_gc_alloc_base = (-3);;
|
||||
let box_gc_field_prev = (-3);;
|
||||
let box_gc_field_next = (-2);;
|
||||
let box_gc_field_ctrl = (-1);;
|
||||
let box_gc_field_refcnt = 0;;
|
||||
let box_gc_field_body = 1;;
|
||||
|
||||
let box_rc_header_size = 1;;
|
||||
let box_gc_header_size = 4;;
|
||||
|
||||
let box_gc_malloc_return_adjustment = 3;;
|
||||
|
||||
let stk_field_valgrind_id = 0;;
|
||||
let stk_field_limit = stk_field_valgrind_id + 1;;
|
||||
let stk_field_data = stk_field_limit + 1;;
|
||||
|
||||
(* Both obj and fn are two-word "bindings": One word points to some static
|
||||
* dispatch information (vtbl, thunk, callee), and the other points to some
|
||||
* box of bound data (object-body or closure).
|
||||
*)
|
||||
|
||||
let binding_field_dispatch = 0;;
|
||||
let binding_field_bound_data = 1;;
|
||||
|
||||
let obj_field_vtbl = binding_field_dispatch;;
|
||||
let obj_field_box = binding_field_bound_data;;
|
||||
|
||||
let obj_body_elt_tydesc = 0;;
|
||||
let obj_body_elt_fields = 1;;
|
||||
|
||||
let fn_field_code = binding_field_dispatch;;
|
||||
let fn_field_box = binding_field_bound_data;;
|
||||
|
||||
(* NB: bound ty params come last to facilitate ignoring them on
|
||||
* closure-dropping. *)
|
||||
let closure_body_elt_bound_args_tydesc = 0;;
|
||||
let closure_body_elt_target = 1;;
|
||||
let closure_body_elt_bound_args = 2;;
|
||||
let closure_body_elt_bound_ty_params = 3;;
|
||||
|
||||
let tag_elt_discriminant = 0;;
|
||||
let tag_elt_variant = 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_take_glue = 3;;
|
||||
let tydesc_field_drop_glue = 4;;
|
||||
let tydesc_field_free_glue = 5;;
|
||||
let tydesc_field_sever_glue = 6;;
|
||||
let tydesc_field_mark_glue = 7;;
|
||||
let tydesc_field_obj_drop_glue = 8;;
|
||||
let tydesc_field_cmp_glue = 9;; (* FIXME these two aren't in the *)
|
||||
let tydesc_field_hash_glue = 10;; (* runtime's type_desc struct. *)
|
||||
let tydesc_field_stateflag = 11;;
|
||||
|
||||
let vec_elt_rc = 0;;
|
||||
let vec_elt_alloc = 1;;
|
||||
let vec_elt_fill = 2;;
|
||||
let vec_elt_pad = 3;;
|
||||
let vec_elt_data = 4;;
|
||||
|
||||
let calltup_elt_out_ptr = 0;;
|
||||
let calltup_elt_task_ptr = 1;;
|
||||
let calltup_elt_indirect_args = 2;;
|
||||
let calltup_elt_ty_params = 3;;
|
||||
let calltup_elt_args = 4;;
|
||||
let calltup_elt_iterator_args = 5;;
|
||||
|
||||
let iterator_args_elt_block_fn = 0;;
|
||||
let iterator_args_elt_outer_frame_ptr = 1;;
|
||||
|
||||
let indirect_args_elt_closure = 0;;
|
||||
|
||||
(* Current worst case is by vec grow glue *)
|
||||
let worst_case_glue_call_args = 8;;
|
||||
|
||||
(*
|
||||
* ABI tags used to inform the runtime which sort of frame to set up for new
|
||||
* spawned functions. FIXME: There is almost certainly a better abstraction to
|
||||
* use.
|
||||
*)
|
||||
let abi_x86_rustboot_cdecl = 1;;
|
||||
let abi_x86_rustc_fastcall = 2;;
|
||||
|
||||
type abi =
|
||||
{
|
||||
abi_word_sz: int64;
|
||||
abi_word_bits: Il.bits;
|
||||
abi_word_ty: Common.ty_mach;
|
||||
|
||||
abi_tag: int;
|
||||
|
||||
abi_has_pcrel_data: bool;
|
||||
abi_has_pcrel_code: bool;
|
||||
|
||||
abi_n_hardregs: int;
|
||||
abi_str_of_hardreg: (int -> string);
|
||||
|
||||
abi_emit_target_specific: (Il.emitter -> Il.quad -> unit);
|
||||
abi_constrain_vregs: (Il.quad -> (Il.vreg,Bits.t) Hashtbl.t -> unit);
|
||||
|
||||
abi_emit_fn_prologue: (Il.emitter
|
||||
-> Common.size (* framesz *)
|
||||
-> Common.size (* callsz *)
|
||||
-> Common.nabi
|
||||
-> Common.fixup (* grow_task *)
|
||||
-> bool (* is_obj_fn *)
|
||||
-> bool (* minimal *)
|
||||
-> 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 option (* 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_gc: (Il.emitter -> 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_callee_saves_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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,831 +0,0 @@
|
|||
(*
|
||||
|
||||
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;;
|
||||
open Fmt;;
|
||||
|
||||
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)
|
||||
;;
|
||||
|
||||
let rec string_of_expr64 (e64:expr64) : string =
|
||||
let bin op a b =
|
||||
Printf.sprintf "(%s %s %s)" (string_of_expr64 a) op (string_of_expr64 b)
|
||||
in
|
||||
let bini op a b =
|
||||
Printf.sprintf "(%s %s %d)" (string_of_expr64 a) op b
|
||||
in
|
||||
match e64 with
|
||||
IMM i when (i64_lt i 0L) -> Printf.sprintf "-0x%Lx" (Int64.neg i)
|
||||
| IMM i -> Printf.sprintf "0x%Lx" i
|
||||
| ADD (a,b) -> bin "+" a b
|
||||
| SUB (a,b) -> bin "-" a b
|
||||
| MUL (a,b) -> bin "*" a b
|
||||
| DIV (a,b) -> bin "/" a b
|
||||
| REM (a,b) -> bin "%" a b
|
||||
| MAX (a,b) ->
|
||||
Printf.sprintf "(max %s %s)"
|
||||
(string_of_expr64 a) (string_of_expr64 b)
|
||||
| ALIGN (a,b) ->
|
||||
Printf.sprintf "(align %s %s)"
|
||||
(string_of_expr64 a) (string_of_expr64 b)
|
||||
| SLL (a,b) -> bini "<<" a b
|
||||
| SLR (a,b) -> bini ">>" a b
|
||||
| SAR (a,b) -> bini ">>>" a b
|
||||
| AND (a,b) -> bin "&" a b
|
||||
| XOR (a,b) -> bin "xor" a b
|
||||
| OR (a,b) -> bin "|" a b
|
||||
| NOT a -> Printf.sprintf "(not %s)" (string_of_expr64 a)
|
||||
| NEG a -> Printf.sprintf "-%s" (string_of_expr64 a)
|
||||
| F_POS f -> Printf.sprintf "<%s>.fpos" f.fixup_name
|
||||
| F_SZ f -> Printf.sprintf "<%s>.fsz" f.fixup_name
|
||||
| M_POS f -> Printf.sprintf "<%s>.mpos" f.fixup_name
|
||||
| M_SZ f -> Printf.sprintf "<%s>.msz" f.fixup_name
|
||||
| EXT _ -> "??ext??"
|
||||
;;
|
||||
|
||||
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; }
|
||||
;;
|
||||
|
||||
|
||||
let rec fmt_frag (ff:Format.formatter) (f:frag) : unit =
|
||||
match f with
|
||||
MARK -> fmt ff "MARK"
|
||||
| SEQ fs -> fmt_bracketed_arr_sep "[" "]" ", " fmt_frag ff fs
|
||||
| PAD i -> fmt ff "PAD(%d)" i
|
||||
| BSS i -> fmt ff "BSZ(%Ld)" i
|
||||
| MEMPOS i -> fmt ff "MEMPOS(%Ld)" i
|
||||
| BYTE i -> fmt ff "0x%x" i
|
||||
| BYTES iz ->
|
||||
fmt ff "BYTES";
|
||||
fmt_bracketed_arr_sep "(" ")" ", "
|
||||
(fun ff i -> fmt ff "0x%x" i) ff iz
|
||||
| CHAR c -> fmt ff "CHAR(%s)" (Char.escaped c)
|
||||
| STRING s -> fmt ff "STRING(%s)" (String.escaped s)
|
||||
| ZSTRING s -> fmt ff "ZSTRING(%s)" (String.escaped s)
|
||||
| ULEB128 e -> fmt ff "ULEB128(%s)" (string_of_expr64 e)
|
||||
| SLEB128 e -> fmt ff "SLEB128(%s)" (string_of_expr64 e)
|
||||
| WORD (tm, e) ->
|
||||
fmt ff "%s:%s"
|
||||
(string_of_ty_mach tm) (string_of_expr64 e)
|
||||
| ALIGN_FILE (i, f) ->
|
||||
fmt ff "ALIGN_FILE(%d, " i;
|
||||
fmt_frag ff f;
|
||||
fmt ff ")"
|
||||
| ALIGN_MEM (i, f) ->
|
||||
fmt ff "ALIGN_MEM(%d, " i;
|
||||
fmt_frag ff f;
|
||||
fmt ff ")"
|
||||
| DEF (fix, f) ->
|
||||
fmt ff "DEF(%s, " fix.fixup_name;
|
||||
fmt_frag ff f;
|
||||
fmt ff ")"
|
||||
| RELAX r ->
|
||||
fmt ff "RELAX(";
|
||||
fmt_arr_sep ", " fmt_frag ff r.relax_options
|
||||
;;
|
||||
|
||||
let sprintf_frag = Fmt.sprintf_fmt fmt_frag;;
|
||||
|
||||
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 with val %s"
|
||||
(string_of_ty_mach m)
|
||||
(fmt_to_str fmt_frag frag));
|
||||
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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1784
src/boot/be/elf.ml
1784
src/boot/be/elf.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,937 +0,0 @@
|
|||
open Common;;
|
||||
|
||||
(* FIXME (issue #1): thread a session object through this eventually. *)
|
||||
let log_iltypes = ref false;;
|
||||
|
||||
(* IL type system, very rudimentary. *)
|
||||
|
||||
type bits =
|
||||
Bits8
|
||||
| Bits16
|
||||
| Bits32
|
||||
| Bits64
|
||||
;;
|
||||
|
||||
type scalar_ty =
|
||||
ValTy of bits
|
||||
| AddrTy of referent_ty
|
||||
|
||||
and referent_ty =
|
||||
ScalarTy of scalar_ty
|
||||
| StructTy of referent_ty array
|
||||
| UnionTy of referent_ty array
|
||||
| ParamTy of ty_param_idx (* Thing of current-frame type-param #n *)
|
||||
| OpaqueTy (* Unknown memory-resident thing. *)
|
||||
| CodeTy (* Executable machine code. *)
|
||||
| NilTy (* 0 bits of space. *)
|
||||
;;
|
||||
|
||||
let (voidptr_t:scalar_ty) = AddrTy OpaqueTy;;
|
||||
let (codeptr_t:scalar_ty) = AddrTy CodeTy;;
|
||||
|
||||
(* Operands. *)
|
||||
|
||||
type vreg = int ;;
|
||||
type hreg = int ;;
|
||||
type label = int ;;
|
||||
type spill = int ;;
|
||||
|
||||
type reg =
|
||||
Vreg of vreg
|
||||
| Hreg of hreg
|
||||
;;
|
||||
|
||||
type mem =
|
||||
Abs of Asm.expr64
|
||||
| RegIn of (reg * (Asm.expr64 option))
|
||||
| Spill of spill
|
||||
;;
|
||||
|
||||
type typed_reg = (reg * scalar_ty);;
|
||||
type typed_mem = (mem * referent_ty);;
|
||||
type typed_imm = (Asm.expr64 * ty_mach);;
|
||||
type typed_imm_ptr = (fixup * referent_ty);;
|
||||
|
||||
type cell =
|
||||
Reg of typed_reg
|
||||
| Mem of typed_mem
|
||||
;;
|
||||
|
||||
(*
|
||||
* ImmPtr (a, rty) can be assigned to anything of scalar_ty
|
||||
* AddrTy rty; the difference is that ImmAddr carries its value
|
||||
* so can be used in cases where we want to have an immediate
|
||||
* address constant-propagated through the code to the backend.
|
||||
*)
|
||||
type operand =
|
||||
Cell of cell
|
||||
| Imm of typed_imm
|
||||
| ImmPtr of typed_imm_ptr
|
||||
;;
|
||||
|
||||
|
||||
type code =
|
||||
CodeLabel of label (* Index into current quad block. *)
|
||||
| CodePtr of operand
|
||||
| CodeNone
|
||||
;;
|
||||
|
||||
(* NB: for the most part, we let the register allocator assign spills
|
||||
* from vregs, and we permanently allocate aliased slots to stack
|
||||
* locations by static aliasing information early, in layout.
|
||||
*
|
||||
* The one awkward case this doesn't handle is when someone tries to
|
||||
* pass a literal-atom to an alias-slot. This *requires* a memory slot
|
||||
* but we only realize it rather late, much later than we'd normally
|
||||
* have thougt to desugar the literal into a temporary.
|
||||
*
|
||||
* So in these cases, we let the trans module explicitly demand a
|
||||
* "Spill n" operand, which the register allocator mops up before it
|
||||
* gets started on the vregs.
|
||||
*
|
||||
* NOTE: if we were more clever we'd integrate vregs and spills like
|
||||
* this together along with the general notion of a temporary way back
|
||||
* at the desugaring stage, and use some kind of size-class
|
||||
* consolidation so that spills with non-overlapping lifetimes could
|
||||
* share memory. But we're not that clever yet.
|
||||
*)
|
||||
|
||||
|
||||
(* Helpers. *)
|
||||
|
||||
let direct_code_ptr fix =
|
||||
(CodePtr (ImmPtr (fix, CodeTy)))
|
||||
;;
|
||||
|
||||
let cell_referent_ty c =
|
||||
match c with
|
||||
Reg (_, st) -> ScalarTy st
|
||||
| Mem (_, rt) -> rt
|
||||
;;
|
||||
|
||||
let cell_is_nil c =
|
||||
match c with
|
||||
Mem (_, NilTy) -> true
|
||||
| Reg (_, AddrTy NilTy) -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let operand_is_nil o =
|
||||
match o with
|
||||
Cell c -> cell_is_nil c
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let mem_off (mem:mem) (off:Asm.expr64) : mem =
|
||||
let addto e = Asm.ADD (off, e) in
|
||||
match mem with
|
||||
Abs e -> Abs (addto e)
|
||||
| RegIn (r, None) -> RegIn (r, Some off)
|
||||
| RegIn (r, Some e) -> RegIn (r, Some (addto e))
|
||||
| Spill _ ->
|
||||
bug () "Adding offset %s to spill slot"
|
||||
(Asm.string_of_expr64 off)
|
||||
;;
|
||||
|
||||
let mem_off_imm (mem:mem) (imm:int64) : mem =
|
||||
mem_off mem (Asm.IMM imm)
|
||||
;;
|
||||
|
||||
|
||||
(* Quads. *)
|
||||
|
||||
type binop =
|
||||
ADD | SUB
|
||||
| IMUL | UMUL
|
||||
| IDIV | UDIV
|
||||
| IMOD | UMOD
|
||||
| AND | OR | XOR
|
||||
| LSL | LSR | ASR
|
||||
;;
|
||||
|
||||
type unop =
|
||||
NEG | NOT
|
||||
| UMOV | IMOV
|
||||
| ZERO
|
||||
;;
|
||||
|
||||
type jmpop =
|
||||
JE | JNE
|
||||
| JZ | JNZ (* FIXME: Synonyms with JE/JNE in x86, others? *)
|
||||
| JL | JLE | JG | JGE (* Signed. *)
|
||||
| JB | JBE | JA | JAE (* Unsigned. *)
|
||||
| JC | JNC | JO | JNO
|
||||
| JMP
|
||||
;;
|
||||
|
||||
type binary =
|
||||
{
|
||||
binary_op: binop;
|
||||
binary_dst: cell;
|
||||
binary_lhs: operand;
|
||||
binary_rhs: operand
|
||||
}
|
||||
;;
|
||||
|
||||
type unary =
|
||||
{
|
||||
unary_op: unop;
|
||||
unary_dst: cell;
|
||||
unary_src: operand
|
||||
}
|
||||
;;
|
||||
|
||||
type cmp =
|
||||
{
|
||||
cmp_lhs: operand;
|
||||
cmp_rhs: operand
|
||||
}
|
||||
;;
|
||||
|
||||
type lea =
|
||||
{
|
||||
lea_dst: cell;
|
||||
lea_src: operand
|
||||
}
|
||||
;;
|
||||
|
||||
type jmp =
|
||||
{
|
||||
jmp_op: jmpop;
|
||||
jmp_targ: code;
|
||||
}
|
||||
;;
|
||||
|
||||
type call =
|
||||
{
|
||||
call_dst: cell;
|
||||
call_targ: code
|
||||
}
|
||||
|
||||
type quad' =
|
||||
Binary of binary
|
||||
| Unary of unary
|
||||
| Lea of lea
|
||||
| Cmp of cmp
|
||||
| Jmp of jmp
|
||||
| Push of operand
|
||||
| Pop of cell
|
||||
| Call of call
|
||||
| Debug (* Debug-break pseudo-instruction. *)
|
||||
| Enter of fixup (* Enter-fixup-block pseudo-instruction. *)
|
||||
| Leave (* Leave-fixup-block pseudo-instruction. *)
|
||||
| Ret (* Return to caller. *)
|
||||
| Nop (* Keep this quad here, emit CPU nop. *)
|
||||
| Dead (* Keep this quad but emit nothing. *)
|
||||
| Regfence (* Clobber all hregs. *)
|
||||
| End (* Space past the end of quads to emit. *)
|
||||
;;
|
||||
|
||||
type quad =
|
||||
{ quad_fixup: fixup option;
|
||||
quad_body: quad'; }
|
||||
|
||||
type quads = quad array ;;
|
||||
|
||||
(* Query functions. *)
|
||||
|
||||
let cell_is_scalar (c:cell) : bool =
|
||||
match c with
|
||||
Reg (_, _) -> true
|
||||
| Mem (_, ScalarTy _) -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
|
||||
let bits_of_ty_mach (tm:ty_mach) : bits =
|
||||
match tm with
|
||||
| TY_u8 -> Bits8
|
||||
| TY_i8 -> Bits8
|
||||
| TY_u16 -> Bits16
|
||||
| TY_i16 -> Bits16
|
||||
| TY_u32 -> Bits32
|
||||
| TY_i32 -> Bits32
|
||||
| TY_u64 -> Bits64
|
||||
| TY_i64 -> Bits64
|
||||
| TY_f32 -> Bits32
|
||||
| TY_f64 -> Bits64
|
||||
;;
|
||||
|
||||
let cell_scalar_ty (c:cell) : scalar_ty =
|
||||
match c with
|
||||
Reg (_, st) -> st
|
||||
| Mem (_, ScalarTy st) -> st
|
||||
| _ -> bug () "mem of non-scalar in Il.cell_scalar_ty"
|
||||
;;
|
||||
|
||||
let operand_scalar_ty (op:operand) : scalar_ty =
|
||||
match op with
|
||||
Cell c -> cell_scalar_ty c
|
||||
| Imm (_, t) -> ValTy (bits_of_ty_mach t)
|
||||
| ImmPtr (_, t) -> AddrTy t
|
||||
;;
|
||||
|
||||
|
||||
let scalar_ty_bits (word_bits:bits) (st:scalar_ty) : bits =
|
||||
match st with
|
||||
ValTy bits -> bits
|
||||
| AddrTy _ -> word_bits
|
||||
;;
|
||||
|
||||
let cell_bits (word_bits:bits) (c:cell) : bits =
|
||||
match c with
|
||||
Reg (_, st) -> scalar_ty_bits word_bits st
|
||||
| Mem (_, ScalarTy st) -> scalar_ty_bits word_bits st
|
||||
| Mem _ -> bug () "mem of non-scalar in Il.cell_bits"
|
||||
;;
|
||||
|
||||
let operand_bits (word_bits:bits) (op:operand) : bits =
|
||||
match op with
|
||||
Cell cell -> cell_bits word_bits cell
|
||||
| Imm (_, tm) -> bits_of_ty_mach tm
|
||||
| ImmPtr _ -> word_bits
|
||||
;;
|
||||
|
||||
let bits_size (bits:bits) : int64 =
|
||||
match bits with
|
||||
Bits8 -> 1L
|
||||
| Bits16 -> 2L
|
||||
| Bits32 -> 4L
|
||||
| Bits64 -> 8L
|
||||
;;
|
||||
|
||||
let bits_align (bits:bits) : int64 =
|
||||
match bits with
|
||||
Bits8 -> 1L
|
||||
| Bits16 -> 2L
|
||||
| Bits32 -> 4L
|
||||
| Bits64 -> 8L
|
||||
;;
|
||||
|
||||
let scalar_ty_size (word_bits:bits) (st:scalar_ty) : int64 =
|
||||
bits_size (scalar_ty_bits word_bits st)
|
||||
;;
|
||||
|
||||
let scalar_ty_align (word_bits:bits) (st:scalar_ty) : int64 =
|
||||
bits_align (scalar_ty_bits word_bits st)
|
||||
;;
|
||||
|
||||
let rec referent_ty_layout (word_bits:bits) (rt:referent_ty) : (size * size) =
|
||||
match rt with
|
||||
ScalarTy st -> (SIZE_fixed (scalar_ty_size word_bits st),
|
||||
SIZE_fixed (scalar_ty_align word_bits st))
|
||||
| StructTy rts ->
|
||||
begin
|
||||
let accum (off,align) rt : (size * size) =
|
||||
let (elt_size, elt_align) = referent_ty_layout word_bits rt in
|
||||
let elt_off = align_sz elt_align off in
|
||||
(add_sz elt_off elt_size, max_sz elt_align align)
|
||||
in
|
||||
Array.fold_left accum (SIZE_fixed 0L, SIZE_fixed 1L) rts
|
||||
end
|
||||
| UnionTy rts ->
|
||||
begin
|
||||
let accum (sz,align) rt : (size * size) =
|
||||
let (elt_size, elt_align) = referent_ty_layout word_bits rt in
|
||||
(max_sz sz elt_size, max_sz elt_align align)
|
||||
in
|
||||
Array.fold_left accum (SIZE_fixed 0L, SIZE_fixed 1L) rts
|
||||
end
|
||||
| OpaqueTy -> bug () "opaque ty in referent_ty_layout"
|
||||
| CodeTy -> bug () "code ty in referent_ty_layout"
|
||||
| ParamTy i -> (SIZE_param_size i, SIZE_param_align i)
|
||||
| NilTy -> (SIZE_fixed 0L, SIZE_fixed 1L)
|
||||
|
||||
and referent_ty_size (word_bits:bits) (rt:referent_ty) : size =
|
||||
(fst (referent_ty_layout word_bits rt))
|
||||
|
||||
and referent_ty_align (word_bits:bits) (rt:referent_ty) : size =
|
||||
(snd (referent_ty_layout word_bits rt))
|
||||
|
||||
;;
|
||||
|
||||
let get_element_offset
|
||||
(word_bits:bits)
|
||||
(elts:referent_ty array)
|
||||
(i:int)
|
||||
: size =
|
||||
let elts_before = Array.sub elts 0 i in
|
||||
let elt_rty = elts.(i) in
|
||||
let elts_before_size = referent_ty_size word_bits (StructTy elts_before) in
|
||||
let elt_align = referent_ty_align word_bits elt_rty in
|
||||
let elt_off = align_sz elt_align elts_before_size in
|
||||
elt_off
|
||||
;;
|
||||
|
||||
(* Processor. *)
|
||||
|
||||
type quad_processor =
|
||||
{ qp_reg: (quad_processor -> reg -> reg);
|
||||
qp_mem: (quad_processor -> mem -> mem);
|
||||
qp_cell_read: (quad_processor -> cell -> cell);
|
||||
qp_cell_write: (quad_processor -> cell -> cell);
|
||||
qp_code: (quad_processor -> code -> code);
|
||||
qp_op: (quad_processor -> operand -> operand); }
|
||||
;;
|
||||
|
||||
let identity_processor =
|
||||
let qp_cell = (fun qp c -> match c with
|
||||
Reg (r, b) -> Reg (qp.qp_reg qp r, b)
|
||||
| Mem (a, b) -> Mem (qp.qp_mem qp a, b))
|
||||
in
|
||||
{ qp_reg = (fun _ r -> r);
|
||||
qp_mem = (fun qp a -> match a with
|
||||
RegIn (r, o) -> RegIn (qp.qp_reg qp r, o)
|
||||
| Abs _
|
||||
| Spill _ -> a);
|
||||
qp_cell_read = qp_cell;
|
||||
qp_cell_write = qp_cell;
|
||||
qp_code = (fun qp c -> match c with
|
||||
CodePtr op -> CodePtr (qp.qp_op qp op)
|
||||
| CodeLabel _
|
||||
| CodeNone -> c);
|
||||
qp_op = (fun qp op -> match op with
|
||||
Cell c -> Cell (qp.qp_cell_read qp c)
|
||||
| ImmPtr _ -> op
|
||||
| Imm _ -> op) }
|
||||
;;
|
||||
|
||||
let process_quad (qp:quad_processor) (q:quad) : quad =
|
||||
{ q with
|
||||
quad_body = match q.quad_body with
|
||||
Binary b ->
|
||||
Binary { b with
|
||||
binary_dst = qp.qp_cell_write qp b.binary_dst;
|
||||
binary_lhs = qp.qp_op qp b.binary_lhs;
|
||||
binary_rhs = qp.qp_op qp b.binary_rhs }
|
||||
| Unary u ->
|
||||
Unary { u with
|
||||
unary_dst = qp.qp_cell_write qp u.unary_dst;
|
||||
unary_src = qp.qp_op qp u.unary_src }
|
||||
|
||||
| Lea le ->
|
||||
Lea { lea_dst = qp.qp_cell_write qp le.lea_dst;
|
||||
lea_src = qp.qp_op qp le.lea_src }
|
||||
|
||||
| Cmp c ->
|
||||
Cmp { cmp_lhs = qp.qp_op qp c.cmp_lhs;
|
||||
cmp_rhs = qp.qp_op qp c.cmp_rhs }
|
||||
|
||||
| Jmp j ->
|
||||
Jmp { j with
|
||||
jmp_targ = qp.qp_code qp j.jmp_targ }
|
||||
|
||||
| Push op ->
|
||||
Push (qp.qp_op qp op)
|
||||
|
||||
| Pop c ->
|
||||
Pop (qp.qp_cell_write qp c)
|
||||
|
||||
| Call c ->
|
||||
Call { call_dst = qp.qp_cell_write qp c.call_dst;
|
||||
call_targ = qp.qp_code qp c.call_targ }
|
||||
|
||||
| Ret -> Ret
|
||||
| Nop -> Nop
|
||||
| Debug -> Debug
|
||||
| Regfence -> Regfence
|
||||
| Enter f -> Enter f
|
||||
| Leave -> Leave
|
||||
| Dead -> Dead
|
||||
| End -> End }
|
||||
;;
|
||||
|
||||
let visit_quads (qp:quad_processor) (qs:quads) : unit =
|
||||
Array.iter (fun x ->ignore ( process_quad qp x); ()) qs
|
||||
;;
|
||||
|
||||
let process_quads (qp:quad_processor) (qs:quads) : quads =
|
||||
Array.map (process_quad qp) qs
|
||||
;;
|
||||
|
||||
let rewrite_quads (qp:quad_processor) (qs:quads) : unit =
|
||||
for i = 0 to ((Array.length qs) - 1) do
|
||||
qs.(i) <- process_quad qp qs.(i)
|
||||
done
|
||||
;;
|
||||
|
||||
|
||||
(* A little partial-evaluator to help lowering sizes. *)
|
||||
|
||||
let rec size_to_expr64 (a:size) : Asm.expr64 option =
|
||||
let binary a b f =
|
||||
match (size_to_expr64 a, size_to_expr64 b) with
|
||||
(Some a, Some b) -> Some (f a b)
|
||||
| _ -> None
|
||||
in
|
||||
match a with
|
||||
SIZE_fixed i -> Some (Asm.IMM i)
|
||||
| SIZE_fixup_mem_sz f -> Some (Asm.M_SZ f)
|
||||
| SIZE_fixup_mem_pos f -> Some (Asm.M_POS f)
|
||||
| SIZE_rt_neg s ->
|
||||
begin
|
||||
match (size_to_expr64 s) with
|
||||
None -> None
|
||||
| Some s -> Some (Asm.NEG s)
|
||||
end
|
||||
| SIZE_rt_add (a, b) -> binary a b (fun a b -> Asm.ADD (a,b))
|
||||
| SIZE_rt_mul (a, b) -> binary a b (fun a b -> Asm.MUL (a,b))
|
||||
| SIZE_rt_max (a, b) -> binary a b (fun a b -> Asm.MAX (a,b))
|
||||
| SIZE_rt_align (a, b) -> binary a b (fun a b -> Asm.ALIGN (a,b))
|
||||
| _ -> None
|
||||
;;
|
||||
|
||||
|
||||
(* Formatters. *)
|
||||
|
||||
let string_of_bits (b:bits) : string =
|
||||
match b with
|
||||
Bits8 -> "b8"
|
||||
| Bits16 -> "b16"
|
||||
| Bits32 -> "b32"
|
||||
| Bits64 -> "b64"
|
||||
;;
|
||||
|
||||
let rec string_of_scalar_ty (s:scalar_ty) : string =
|
||||
match s with
|
||||
ValTy b -> (string_of_bits b)
|
||||
| AddrTy r -> (string_of_referent_ty r) ^ "*"
|
||||
|
||||
and string_of_referent_ty (r:referent_ty) : string =
|
||||
match r with
|
||||
ScalarTy s -> (string_of_scalar_ty s)
|
||||
| StructTy rs ->
|
||||
Printf.sprintf "[%s]"
|
||||
(String.concat ","
|
||||
(Array.to_list (Array.map string_of_referent_ty rs)))
|
||||
| UnionTy rs ->
|
||||
Printf.sprintf "(%s)"
|
||||
(String.concat "|"
|
||||
(Array.to_list (Array.map string_of_referent_ty rs)))
|
||||
| ParamTy i -> Printf.sprintf "#%d" i
|
||||
| OpaqueTy -> "?"
|
||||
| CodeTy -> "!"
|
||||
| NilTy -> "()"
|
||||
;;
|
||||
|
||||
|
||||
type hreg_formatter = hreg -> string;;
|
||||
|
||||
let string_of_reg (f:hreg_formatter) (r:reg) : string =
|
||||
match r with
|
||||
Vreg i -> Printf.sprintf "<v%d>" i
|
||||
| Hreg i -> f i
|
||||
;;
|
||||
|
||||
let string_of_off (e:Asm.expr64 option) : string =
|
||||
match e with
|
||||
None -> ""
|
||||
| Some (Asm.IMM i) when (i64_lt i 0L) ->
|
||||
Printf.sprintf " - 0x%Lx" (Int64.neg i)
|
||||
| Some e' -> " + " ^ (Asm.string_of_expr64 e')
|
||||
;;
|
||||
|
||||
let string_of_mem (f:hreg_formatter) (a:mem) : string =
|
||||
match a with
|
||||
Abs e ->
|
||||
Printf.sprintf "[%s]" (Asm.string_of_expr64 e)
|
||||
| RegIn (r, off) ->
|
||||
Printf.sprintf "[%s%s]" (string_of_reg f r) (string_of_off off)
|
||||
| Spill i ->
|
||||
Printf.sprintf "[<spill %d>]" i
|
||||
;;
|
||||
let string_of_cell (f:hreg_formatter) (c:cell) : string =
|
||||
match c with
|
||||
Reg (r,ty) ->
|
||||
if !log_iltypes
|
||||
then
|
||||
Printf.sprintf "%s:%s" (string_of_reg f r) (string_of_scalar_ty ty)
|
||||
else
|
||||
Printf.sprintf "%s" (string_of_reg f r)
|
||||
| Mem (a,ty) ->
|
||||
if !log_iltypes
|
||||
then
|
||||
Printf.sprintf "%s:%s"
|
||||
(string_of_mem f a) (string_of_referent_ty ty)
|
||||
else
|
||||
Printf.sprintf "%s" (string_of_mem f a)
|
||||
;;
|
||||
|
||||
let string_of_operand (f:hreg_formatter) (op:operand) : string =
|
||||
match op with
|
||||
Cell c -> string_of_cell f c
|
||||
| ImmPtr (f, ty) ->
|
||||
if !log_iltypes
|
||||
then
|
||||
Printf.sprintf "$<%s>.mpos:%s*"
|
||||
f.fixup_name (string_of_referent_ty ty)
|
||||
else
|
||||
Printf.sprintf "$<%s>.mpos" f.fixup_name
|
||||
| Imm (i, ty) ->
|
||||
if !log_iltypes
|
||||
then
|
||||
Printf.sprintf "$%s:%s"
|
||||
(Asm.string_of_expr64 i) (string_of_ty_mach ty)
|
||||
else
|
||||
Printf.sprintf "$%s" (Asm.string_of_expr64 i)
|
||||
;;
|
||||
|
||||
|
||||
let string_of_code (f:hreg_formatter) (c:code) : string =
|
||||
match c with
|
||||
CodeLabel lab -> Printf.sprintf "<label %d>" lab
|
||||
| CodePtr op -> string_of_operand f op
|
||||
| CodeNone -> "<none>"
|
||||
;;
|
||||
|
||||
|
||||
let string_of_binop (op:binop) : string =
|
||||
match op with
|
||||
ADD -> "add"
|
||||
| SUB -> "sub"
|
||||
| IMUL -> "imul"
|
||||
| UMUL -> "umul"
|
||||
| IDIV -> "idiv"
|
||||
| UDIV -> "udiv"
|
||||
| IMOD -> "imod"
|
||||
| UMOD -> "umod"
|
||||
| AND -> "and"
|
||||
| OR -> "or"
|
||||
| XOR -> "xor"
|
||||
| LSL -> "lsl"
|
||||
| LSR -> "lsr"
|
||||
| ASR -> "asr"
|
||||
;;
|
||||
|
||||
let string_of_unop (op:unop) : string =
|
||||
match op with
|
||||
NEG -> "neg"
|
||||
| NOT -> "not"
|
||||
| UMOV -> "umov"
|
||||
| IMOV -> "imov"
|
||||
| ZERO -> "zero"
|
||||
;;
|
||||
|
||||
let string_of_jmpop (op:jmpop) : string =
|
||||
match op with
|
||||
JE -> "je"
|
||||
| JNE -> "jne"
|
||||
| JL -> "jl"
|
||||
| JLE -> "jle"
|
||||
| JG -> "jg"
|
||||
| JGE -> "jge"
|
||||
| JB -> "jb"
|
||||
| JBE -> "jbe"
|
||||
| JA -> "ja"
|
||||
| JAE -> "jae"
|
||||
| JC -> "jc"
|
||||
| JNC ->"jnc"
|
||||
| JO -> "jo"
|
||||
| JNO -> "jno"
|
||||
| JZ -> "jz"
|
||||
| JNZ ->"jnz"
|
||||
| JMP -> "jmp"
|
||||
;;
|
||||
|
||||
let string_of_quad (f:hreg_formatter) (q:quad) : string =
|
||||
match q.quad_body with
|
||||
Binary b ->
|
||||
Printf.sprintf "%s = %s %s %s"
|
||||
(string_of_cell f b.binary_dst)
|
||||
(string_of_operand f b.binary_lhs)
|
||||
(string_of_binop b.binary_op)
|
||||
(string_of_operand f b.binary_rhs)
|
||||
|
||||
| Unary u ->
|
||||
Printf.sprintf "%s = %s %s"
|
||||
(string_of_cell f u.unary_dst)
|
||||
(string_of_unop u.unary_op)
|
||||
(string_of_operand f u.unary_src)
|
||||
|
||||
| Cmp c ->
|
||||
Printf.sprintf "cmp %s %s"
|
||||
(string_of_operand f c.cmp_lhs)
|
||||
(string_of_operand f c.cmp_rhs)
|
||||
|
||||
| Lea le ->
|
||||
Printf.sprintf "lea %s %s"
|
||||
(string_of_cell f le.lea_dst)
|
||||
(string_of_operand f le.lea_src)
|
||||
|
||||
| Jmp j ->
|
||||
Printf.sprintf "%s %s"
|
||||
(string_of_jmpop j.jmp_op)
|
||||
(string_of_code f j.jmp_targ)
|
||||
|
||||
| Push op ->
|
||||
Printf.sprintf "push %s"
|
||||
(string_of_operand f op)
|
||||
|
||||
| Pop c ->
|
||||
Printf.sprintf "%s = pop"
|
||||
(string_of_cell f c)
|
||||
|
||||
| Call c ->
|
||||
Printf.sprintf "%s = call %s"
|
||||
(string_of_cell f c.call_dst)
|
||||
(string_of_code f c.call_targ)
|
||||
|
||||
| Ret -> "ret"
|
||||
| Nop -> "nop"
|
||||
| Dead -> "dead"
|
||||
| Debug -> "debug"
|
||||
| Regfence -> "regfence"
|
||||
| Enter _ -> "enter lexical block"
|
||||
| Leave -> "leave lexical block"
|
||||
| End -> "---"
|
||||
;;
|
||||
|
||||
|
||||
|
||||
(* Emitters. *)
|
||||
|
||||
|
||||
type emitter = { mutable emit_pc: int;
|
||||
mutable emit_next_vreg: int option;
|
||||
mutable emit_next_spill: int;
|
||||
emit_target_specific: (emitter -> quad -> unit);
|
||||
mutable emit_quads: quads;
|
||||
emit_annotations: (int,string) Hashtbl.t;
|
||||
emit_size_cache: (size,operand) Hashtbl.t;
|
||||
emit_node: node_id option;
|
||||
}
|
||||
|
||||
|
||||
let badq = { quad_fixup = None;
|
||||
quad_body = End }
|
||||
;;
|
||||
|
||||
|
||||
let deadq = { quad_fixup = None;
|
||||
quad_body = Dead }
|
||||
;;
|
||||
|
||||
|
||||
let new_emitter
|
||||
(emit_target_specific:emitter -> quad -> unit)
|
||||
(vregs_ok:bool)
|
||||
(node:node_id option)
|
||||
: emitter =
|
||||
{
|
||||
emit_pc = 0;
|
||||
emit_next_vreg = (if vregs_ok then Some 0 else None);
|
||||
emit_next_spill = 0;
|
||||
emit_target_specific = emit_target_specific;
|
||||
emit_quads = Array.create 4 badq;
|
||||
emit_annotations = Hashtbl.create 0;
|
||||
emit_size_cache = Hashtbl.create 0;
|
||||
emit_node = node;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
let num_vregs (e:emitter) : int =
|
||||
match e.emit_next_vreg with
|
||||
None -> 0
|
||||
| Some i -> i
|
||||
;;
|
||||
|
||||
let next_vreg_num (e:emitter) : vreg =
|
||||
match e.emit_next_vreg with
|
||||
None -> bug () "Il.next_vreg_num on non-vreg emitter"
|
||||
| Some i ->
|
||||
e.emit_next_vreg <- Some (i + 1);
|
||||
i
|
||||
;;
|
||||
|
||||
let next_vreg (e:emitter) : reg =
|
||||
Vreg (next_vreg_num e)
|
||||
;;
|
||||
|
||||
let next_vreg_cell (e:emitter) (s:scalar_ty) : cell =
|
||||
Reg ((next_vreg e), s)
|
||||
;;
|
||||
|
||||
let next_spill (e:emitter) : spill =
|
||||
let i = e.emit_next_spill in
|
||||
e.emit_next_spill <- i + 1;
|
||||
i
|
||||
;;
|
||||
|
||||
let next_spill_slot (e:emitter) (r:referent_ty) : typed_mem =
|
||||
(Spill (next_spill e), r);
|
||||
;;
|
||||
|
||||
|
||||
let grow_if_necessary e =
|
||||
let len = Array.length e.emit_quads in
|
||||
if e.emit_pc >= len - 1
|
||||
then
|
||||
let n = Array.create (2 * len) badq in
|
||||
Array.blit e.emit_quads 0 n 0 len;
|
||||
e.emit_quads <- n
|
||||
;;
|
||||
|
||||
|
||||
let binary (op:binop) (dst:cell) (lhs:operand) (rhs:operand) : quad' =
|
||||
Binary { binary_op = op;
|
||||
binary_dst = dst;
|
||||
binary_lhs = lhs;
|
||||
binary_rhs = rhs }
|
||||
;;
|
||||
|
||||
let unary (op:unop) (dst:cell) (src:operand) : quad' =
|
||||
Unary { unary_op = op;
|
||||
unary_dst = dst;
|
||||
unary_src = src }
|
||||
|
||||
let jmp (op:jmpop) (targ:code) : quad' =
|
||||
Jmp { jmp_op = op;
|
||||
jmp_targ = targ; }
|
||||
;;
|
||||
|
||||
|
||||
let lea (dst:cell) (src:operand) : quad' =
|
||||
Lea { lea_dst = dst;
|
||||
lea_src = src; }
|
||||
;;
|
||||
|
||||
let cmp (lhs:operand) (rhs:operand) : quad' =
|
||||
Cmp { cmp_lhs = lhs;
|
||||
cmp_rhs = rhs; }
|
||||
;;
|
||||
|
||||
let call (dst:cell) (targ:code) : quad' =
|
||||
Call { call_dst = dst;
|
||||
call_targ = targ; }
|
||||
;;
|
||||
|
||||
let umov (dst:cell) (src:operand) : quad' =
|
||||
if (cell_is_nil dst || operand_is_nil src)
|
||||
then Dead
|
||||
else unary UMOV dst src
|
||||
;;
|
||||
|
||||
let imov (dst:cell) (src:operand) : quad' =
|
||||
if (cell_is_nil dst || operand_is_nil src)
|
||||
then Dead
|
||||
else unary IMOV dst src
|
||||
;;
|
||||
|
||||
let zero (dst:cell) (count:operand) : quad' =
|
||||
unary ZERO dst count
|
||||
;;
|
||||
|
||||
let is_mov uop =
|
||||
match uop with
|
||||
UMOV | IMOV -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let mk_quad (q':quad') : quad =
|
||||
{ quad_body = q';
|
||||
quad_fixup = None }
|
||||
;;
|
||||
|
||||
let append_quad
|
||||
(e:emitter)
|
||||
(q:quad)
|
||||
: unit =
|
||||
grow_if_necessary e;
|
||||
e.emit_quads.(e.emit_pc) <- q;
|
||||
e.emit_pc <- e.emit_pc + 1
|
||||
;;
|
||||
|
||||
let default_mov q' =
|
||||
match q' with
|
||||
Binary b ->
|
||||
begin
|
||||
match b.binary_op with
|
||||
IDIV | IMUL | IMOD -> IMOV
|
||||
| _ -> UMOV
|
||||
end
|
||||
| Unary u ->
|
||||
begin
|
||||
match u.unary_op with
|
||||
IMOV -> IMOV
|
||||
| _ -> UMOV
|
||||
end
|
||||
| _ -> UMOV
|
||||
;;
|
||||
|
||||
let emit_full
|
||||
(e:emitter)
|
||||
(fix:fixup option)
|
||||
(q':quad')
|
||||
: unit =
|
||||
e.emit_target_specific e { quad_body = q';
|
||||
quad_fixup = fix }
|
||||
;;
|
||||
|
||||
let emit (e:emitter) (q':quad') : unit =
|
||||
emit_full e None q'
|
||||
;;
|
||||
|
||||
let patch_jump (e:emitter) (jmp:int) (targ:int) : unit =
|
||||
let q = e.emit_quads.(jmp) in
|
||||
match q.quad_body with
|
||||
Jmp j ->
|
||||
assert (j.jmp_targ = CodeNone);
|
||||
e.emit_quads.(jmp) <-
|
||||
{ q with quad_body =
|
||||
Jmp { j with jmp_targ = CodeLabel targ } }
|
||||
| _ -> ()
|
||||
;;
|
||||
|
||||
(* More query functions. *)
|
||||
|
||||
let get_element_ptr
|
||||
(word_bits:bits)
|
||||
(fmt:hreg_formatter)
|
||||
(mem_cell:cell)
|
||||
(i:int)
|
||||
: cell =
|
||||
match mem_cell with
|
||||
Mem (mem, StructTy elts) when i >= 0 && i < (Array.length elts) ->
|
||||
assert ((Array.length elts) != 0);
|
||||
begin
|
||||
let elt_rty = elts.(i) in
|
||||
let elt_off = get_element_offset word_bits elts i in
|
||||
match elt_off with
|
||||
SIZE_fixed fixed_off ->
|
||||
Mem (mem_off_imm mem fixed_off, elt_rty)
|
||||
| _ -> bug ()
|
||||
"get_element_ptr %d on dynamic-size cell: offset %s"
|
||||
i (string_of_size elt_off)
|
||||
end
|
||||
|
||||
| _ -> bug () "get_element_ptr %d on cell %s" i
|
||||
(string_of_cell fmt mem_cell)
|
||||
;;
|
||||
|
||||
let cell_cast (cell:cell) (rty:referent_ty) : cell =
|
||||
match cell with
|
||||
Mem (mem, _) -> Mem (mem, rty)
|
||||
| Reg (reg, _) ->
|
||||
begin
|
||||
match rty with
|
||||
ScalarTy st -> Reg (reg, st)
|
||||
| _ -> bug () "expected scalar type in Il.cell_cast on register"
|
||||
end
|
||||
|
||||
|
||||
let ptr_cast (cell:cell) (rty:referent_ty) : cell =
|
||||
match cell with
|
||||
Mem (mem, ScalarTy (AddrTy _)) -> Mem (mem, ScalarTy (AddrTy rty))
|
||||
| Reg (reg, AddrTy _) -> Reg (reg, AddrTy rty)
|
||||
| _ -> bug () "expected address cell in Il.ptr_cast"
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1194
src/boot/be/macho.ml
1194
src/boot/be/macho.ml
File diff suppressed because it is too large
Load Diff
1175
src/boot/be/pe.ml
1175
src/boot/be/pe.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,688 +0,0 @@
|
|||
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 =
|
||||
match q.Il.quad_body with
|
||||
Il.Jmp jmp ->
|
||||
begin
|
||||
match jmp.Il.jmp_targ with
|
||||
Il.CodeLabel lab -> [ lab ]
|
||||
| _ -> []
|
||||
end
|
||||
| _ -> []
|
||||
;;
|
||||
|
||||
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; jmp_targ = _ } -> true
|
||||
| Il.Ret -> true
|
||||
| _ -> false
|
||||
;;
|
||||
|
||||
let calculate_live_bitvectors
|
||||
(cx:ctxt)
|
||||
: ((Bits.t array) * (Bits.t array)) =
|
||||
|
||||
iflog cx (fun _ -> 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 new_true_bitv _ = Bits.create n_vregs true 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_not_defined_vrs:Bits.t array) =
|
||||
Array.init n_quads new_true_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
|
||||
|
||||
(* Working bit-vector. *)
|
||||
let scratch = new_bitv() in
|
||||
let changed = ref true 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_not_defined_vrs.(i) v false)
|
||||
(quad_defined_vregs q);
|
||||
done;
|
||||
|
||||
while !changed do
|
||||
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 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 not_defined = quad_not_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 not_defined);
|
||||
ignore (Bits.intersect scratch live_out);
|
||||
note_change (Bits.union live_in scratch);
|
||||
|
||||
done;
|
||||
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
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
log cx "[%s] %s %s"
|
||||
(padded_num i len) (padded_str lab (!maxlablen)) qs)
|
||||
done
|
||||
;;
|
||||
|
||||
let calculate_vreg_constraints
|
||||
(cx:ctxt)
|
||||
(constraints:(Il.vreg,Bits.t) Hashtbl.t)
|
||||
(q:quad)
|
||||
: unit =
|
||||
let abi = cx.ctxt_abi in
|
||||
Hashtbl.clear constraints;
|
||||
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 %s"
|
||||
(string_of_quad hr_str q);
|
||||
let qp_reg _ r =
|
||||
begin
|
||||
match r with
|
||||
Il.Hreg _ -> ()
|
||||
| Il.Vreg v ->
|
||||
match htab_search constraints v with
|
||||
None -> log cx "<v%d> unconstrained" v
|
||||
| Some c ->
|
||||
let hregs = Bits.to_list c 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
|
||||
;;
|
||||
|
||||
(* 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) =
|
||||
calculate_live_bitvectors cx
|
||||
in
|
||||
(* vreg idx -> hreg bits.t *)
|
||||
let (vreg_constraints:(Il.vreg,Bits.t) Hashtbl.t) =
|
||||
Hashtbl.create 0
|
||||
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) ||
|
||||
(Bits.get (live_in_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
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
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 get_vreg_constraints v =
|
||||
match htab_search vreg_constraints v with
|
||||
None -> all_hregs
|
||||
| Some c -> c
|
||||
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 = get_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 _ = calculate_vreg_constraints cx vreg_constraints quad 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 the quad has any nontrivial vreg constraints, regfence.
|
||||
* This is awful but it saves us from cached/constrained
|
||||
* interference as was found in issue #152. *)
|
||||
if List.exists
|
||||
(fun v -> not (Bits.equal (get_vreg_constraints v) all_hregs))
|
||||
used
|
||||
then
|
||||
begin
|
||||
(* Regfence. *)
|
||||
spill_all_regs i;
|
||||
(* Check for over-constrained-ness after any such regfence. *)
|
||||
let vreg_constrs v =
|
||||
(v, Bits.to_list (get_vreg_constraints v))
|
||||
in
|
||||
let constrs = List.map vreg_constrs (used @ defined) in
|
||||
let constrs_collide (v1,c1) =
|
||||
if List.length c1 <> 1
|
||||
then false
|
||||
else
|
||||
List.exists
|
||||
(fun (v2,c2) -> if v1 = v2 then false else c1 = c2)
|
||||
constrs
|
||||
in
|
||||
if List.exists constrs_collide constrs
|
||||
then raise (Ra_error ("over-constrained vregs"));
|
||||
end;
|
||||
|
||||
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\n" s;
|
||||
(quads, 0)
|
||||
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
2582
src/boot/be/x86.ml
2582
src/boot/be/x86.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +0,0 @@
|
|||
(*
|
||||
* 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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
|
@ -1,464 +0,0 @@
|
|||
open Common;;
|
||||
open Fmt;;
|
||||
|
||||
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 ()
|
||||
;;
|
||||
|
||||
(*
|
||||
* Stuff associated with 'crate interfaces'.
|
||||
*
|
||||
* The interface of a crate used to be defined by the accompanying DWARF
|
||||
* structure in the object file. This was an experiment -- we talked to
|
||||
* DWARF hackers before hand and they thought it worth trying -- which did
|
||||
* work, and had the advantage of economy of metadata-emitting, but several
|
||||
* downsides:
|
||||
*
|
||||
* - The reader -- which we want a copy of at runtime in the linker -- has
|
||||
* to know how to read DWARF. It's not the simplest format.
|
||||
*
|
||||
* - The complexity of the encoding meant we didn't always keep pace with
|
||||
* the AST, and maintaining any degree of inter-change compatibility was
|
||||
* going ot be a serious challenge.
|
||||
*
|
||||
* - Diagnostic tools are atrocious, as is the definition of
|
||||
* well-formedness. It's largely trial and error when talking to gdb,
|
||||
* say.
|
||||
*
|
||||
* - Because it was doing double-duty as *driving linkage*, we were never
|
||||
* going to get to the linkage efficiency of native symbols (hash
|
||||
* lookup) anyway. Runtime linkage -- even when lazy -- really ought to
|
||||
* be fast.
|
||||
*
|
||||
* - LLVM, our "main" backend (in rustc) does not really want to make
|
||||
* promises about preserving dwarf.
|
||||
*
|
||||
* - LLVM also *is* going to emit native symbols; complete with relocs and
|
||||
* such. We'd actually have to do *extra work* to inhibit that.
|
||||
*
|
||||
* - Most tools are set up to think of DWARF as "debug", meaning
|
||||
* "optional", and may well strip it or otherwise mangle it.
|
||||
*
|
||||
* - Many tools want native symbols anyways, and don't know how to look at
|
||||
* DWARF.
|
||||
*
|
||||
* - All the tooling arguments go double on win32. Pretty much only
|
||||
* objdump and gdb understand DWARF-in-PE. Everything else is just blank
|
||||
* stares.
|
||||
*
|
||||
* For all these reasons we're moving to a self-made format for describing
|
||||
* our interfaces. This will be stored in the .note.rust section as we
|
||||
* presently store the meta tags. The encoding is ASCII-compatible (the set
|
||||
* of "numbers" to encode is small enough, especially compared to dwarf,
|
||||
* that we can just use a text form) and is very easy to read with a simple
|
||||
* byte-at-a-time parser.
|
||||
*
|
||||
*)
|
||||
|
||||
(*
|
||||
* Encoding goals:
|
||||
*
|
||||
* - Simple. Minimal state or read-ambiguity in reader.
|
||||
*
|
||||
* - Compact. Shouldn't add a lot to the size of the binary to glue this
|
||||
* on to it.
|
||||
*
|
||||
* - Front-end-y. Doesn't need to contain much beyond parse-level of the
|
||||
* crate's exported items; it'll be fed into the front-end of the
|
||||
* pipeline anyway. No need to have all types or names resolved.
|
||||
*
|
||||
* - Testable. Human-legible and easy to identify/fix/test errors in.
|
||||
*
|
||||
* - Very fast to read the 'identifying' prefix (version, meta tags, hash)
|
||||
*
|
||||
* - Tolerably fast to read in its entirety.
|
||||
*
|
||||
* - Safe from version-drift (or at least able to notice it and abort).
|
||||
*
|
||||
* Anti-goals:
|
||||
*
|
||||
* - Random access.
|
||||
*
|
||||
* - Generality to other languages.
|
||||
*
|
||||
* Structure:
|
||||
*
|
||||
* - Line oriented.
|
||||
*
|
||||
* - Whitespace-separated and whitespace-agnostic. Indent for legibility.
|
||||
*
|
||||
* - Each line is a record. A record is either a full item, an item bracket,
|
||||
* a comment, or metadata.
|
||||
*
|
||||
* - First byte describes type of record, unless first byte is +, in which
|
||||
* case it's oh-no-we-ran-out-of-tags and it's followed by 2 type-bytes.
|
||||
* (Continue to +++ if you happen to run out *there* as well. You
|
||||
* won't.)
|
||||
*
|
||||
* - Metadata type is !
|
||||
*
|
||||
* - Comment type is #
|
||||
*
|
||||
* - Full item types are: y for type, c for const, f for fn, i for iter,
|
||||
* g for tag constructor.
|
||||
*
|
||||
* - Item brackets are those that open/close a scope of
|
||||
* sub-records. These would be obj (o), mod (m), tag (t) to open. The
|
||||
* closer is always '.'. So a mod looks like:
|
||||
*
|
||||
* m foo
|
||||
* c bar
|
||||
* .
|
||||
*
|
||||
* - After first byte of openers and full items is whitespace, then an
|
||||
* ident.
|
||||
*
|
||||
* - After that, if it's a ty, fn, iter, obj or tag, there may be [, a
|
||||
* list of comma-separated ty param names, and ].
|
||||
*
|
||||
* - After that, if it's a fn, iter, obj or tag constructor, there is a (,
|
||||
* a list of comma-separated type-encoded slot/ident pairs, and a ).
|
||||
*
|
||||
* - After that, if it's a fn or iter, there's a '->' and a type-encoded
|
||||
* output.
|
||||
*
|
||||
* - After that, a newline '\n'.
|
||||
*
|
||||
* - Type encoding is a longer issue! We'll get to that.
|
||||
*)
|
||||
|
||||
let fmt_iface (ff:Format.formatter) (crate:Ast.crate) : unit =
|
||||
let fmt_ty_param ff (p:Ast.ty_param identified) : unit =
|
||||
fmt ff "%s" (fst p.node)
|
||||
in
|
||||
let rec fmt_ty ff (t:Ast.ty) : unit =
|
||||
match t with
|
||||
Ast.TY_any -> fmt ff "a"
|
||||
| Ast.TY_nil -> fmt ff "n"
|
||||
| Ast.TY_bool -> fmt ff "b"
|
||||
| Ast.TY_mach tm -> fmt ff "%s" (string_of_ty_mach tm)
|
||||
| Ast.TY_int -> fmt ff "i"
|
||||
| Ast.TY_uint -> fmt ff "u"
|
||||
| Ast.TY_char -> fmt ff "c"
|
||||
| Ast.TY_str -> fmt ff "s"
|
||||
|
||||
| Ast.TY_tup ttup ->
|
||||
fmt_bracketed_arr_sep "(" ")" ","
|
||||
fmt_ty ff ttup
|
||||
| Ast.TY_vec ty ->
|
||||
fmt ff "v["; fmt_ty ff ty; fmt ff "]"
|
||||
| Ast.TY_chan ty ->
|
||||
fmt ff "C["; fmt_ty ff ty; fmt ff "]"
|
||||
|
||||
| Ast.TY_port ty ->
|
||||
fmt ff "P["; fmt_ty ff ty; fmt ff "]"
|
||||
|
||||
| Ast.TY_task ->
|
||||
fmt ff "T"
|
||||
|
||||
| Ast.TY_named n -> fmt ff ":"; fmt_name ff n
|
||||
| Ast.TY_type -> fmt ff "Y"
|
||||
|
||||
| Ast.TY_box t -> fmt ff "@@"; fmt_ty ff t
|
||||
| Ast.TY_mutable t -> fmt ff "~"; fmt_ty ff t
|
||||
|
||||
(* FIXME: finish this. *)
|
||||
| Ast.TY_rec _
|
||||
| Ast.TY_tag _
|
||||
| Ast.TY_fn _
|
||||
| Ast.TY_obj _
|
||||
| Ast.TY_native _
|
||||
| Ast.TY_param _
|
||||
| Ast.TY_constrained _ -> fmt ff "Z"
|
||||
|
||||
and fmt_name ff n =
|
||||
match n with
|
||||
Ast.NAME_base (Ast.BASE_ident id) -> fmt ff "%s" id
|
||||
| Ast.NAME_base (Ast.BASE_temp _) -> failwith "temp in fmt_name"
|
||||
| Ast.NAME_base (Ast.BASE_app (id, tys)) ->
|
||||
fmt ff "%s" id;
|
||||
fmt_bracketed_arr_sep "[" "]" ","
|
||||
fmt_ty ff tys;
|
||||
| Ast.NAME_ext (n, Ast.COMP_ident id) ->
|
||||
fmt_name ff n;
|
||||
fmt ff ".%s" id
|
||||
| Ast.NAME_ext (n, Ast.COMP_app (id, tys)) ->
|
||||
fmt_name ff n;
|
||||
fmt ff ".%s" id;
|
||||
fmt_bracketed_arr_sep "[" "]" ","
|
||||
fmt_ty ff tys;
|
||||
| Ast.NAME_ext (n, Ast.COMP_idx i) ->
|
||||
fmt_name ff n;
|
||||
fmt ff "._%d" i
|
||||
in
|
||||
let rec fmt_mod_item (id:Ast.ident) (mi:Ast.mod_item) : unit =
|
||||
let i c = fmt ff "@\n%c %s" c id in
|
||||
|
||||
let o c = fmt ff "@\n"; fmt_obox ff; fmt ff "%c %s" c id in
|
||||
let p _ =
|
||||
if (Array.length mi.node.Ast.decl_params) <> 0
|
||||
then
|
||||
fmt_bracketed_arr_sep "[" "]" ","
|
||||
fmt_ty_param ff mi.node.Ast.decl_params
|
||||
in
|
||||
let c _ = fmt_cbox ff; fmt ff "@\n." in
|
||||
match mi.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_type _ -> i 'y'; p()
|
||||
| Ast.MOD_ITEM_tag _ -> i 'g'; p()
|
||||
| Ast.MOD_ITEM_fn _ -> i 'f'; p();
|
||||
| Ast.MOD_ITEM_const _ -> i 'c'
|
||||
| Ast.MOD_ITEM_obj _ ->
|
||||
o 'o'; p();
|
||||
c ()
|
||||
| Ast.MOD_ITEM_mod (_, items) ->
|
||||
o 'm';
|
||||
fmt_mod_items items;
|
||||
c ()
|
||||
and fmt_mod_items items =
|
||||
sorted_htab_iter fmt_mod_item items
|
||||
in
|
||||
let (_,items) = crate.node.Ast.crate_items in
|
||||
fmt_mod_items items
|
||||
;;
|
||||
|
||||
(* Mechanisms for scanning libraries. *)
|
||||
|
||||
(* FIXME (issue #67): 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
|
||||
| FreeBSD_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
|
||||
| FreeBSD_x86_elf -> Elf.get_sections
|
||||
in
|
||||
Some (ar, (get_sections sess ar))
|
||||
end
|
||||
;;
|
||||
|
||||
let get_meta
|
||||
(sess:Session.sess)
|
||||
(filename:filename)
|
||||
: Session.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)
|
||||
: Ast.mod_items =
|
||||
let dies = get_dies sess filename in
|
||||
let items = Hashtbl.create 0 in
|
||||
let nref = sess.Session.sess_node_id_counter in
|
||||
let oref = sess.Session.sess_opaque_id_counter 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)
|
||||
(crate_item_cache:(crate_id, Ast.mod_items) Hashtbl.t)
|
||||
: (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"
|
||||
| FreeBSD_x86_elf -> ".so"
|
||||
in
|
||||
let rec meta_matches i f_meta =
|
||||
if i >= (Array.length meta)
|
||||
then true
|
||||
else
|
||||
match meta.(i) with
|
||||
(* FIXME (issue #68): 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 basename = Unix.readdir dh in
|
||||
let file = dir ^ "/" ^ basename in
|
||||
log sess "considering file %s" file;
|
||||
if (Filename.check_suffix file suffix) &&
|
||||
(file_matches file)
|
||||
then
|
||||
begin
|
||||
log sess "matched against library %s" file;
|
||||
|
||||
let meta = get_meta sess file in
|
||||
let crate_id =
|
||||
match meta with
|
||||
None -> Session.make_crate_id sess
|
||||
| Some meta ->
|
||||
iflog sess begin fun _ ->
|
||||
Array.iter
|
||||
(fun (k, v) -> log sess "%s = %S" k v)
|
||||
meta
|
||||
end;
|
||||
htab_search_or_default
|
||||
sess.Session.sess_crate_meta
|
||||
meta
|
||||
(fun () -> Session.make_crate_id sess)
|
||||
in
|
||||
Queue.add (file, crate_id) 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, crate_id) = Queue.pop found in
|
||||
let items =
|
||||
htab_search_or_default crate_item_cache crate_id
|
||||
(fun () -> get_file_mod sess abi filename)
|
||||
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"
|
||||
| FreeBSD_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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,530 +0,0 @@
|
|||
|
||||
open Common;;
|
||||
|
||||
let (targ:Common.target) =
|
||||
match Sys.os_type with
|
||||
|
||||
| "Win32"
|
||||
| "Cygwin" -> Win32_x86_pe
|
||||
|
||||
| "Unix"
|
||||
when Unix.system "test `uname -s` = 'Linux'" = Unix.WEXITED 0 ->
|
||||
Linux_x86_elf
|
||||
| "Unix"
|
||||
when Unix.system "test `uname -s` = 'Darwin'" = Unix.WEXITED 0 ->
|
||||
MacOS_x86_macho
|
||||
| "Unix"
|
||||
when Unix.system "test `uname -s` = 'FreeBSD'" = Unix.WEXITED 0 ->
|
||||
FreeBSD_x86_elf
|
||||
| _ ->
|
||||
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;
|
||||
Session.sess_minimal = false;
|
||||
Session.sess_use_pexps = false;
|
||||
(* FIXME (issue #69): 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_sig = false;
|
||||
Session.sess_log_passes = false;
|
||||
Session.sess_log_resolve = false;
|
||||
Session.sess_log_type = false;
|
||||
Session.sess_log_simplify = false;
|
||||
Session.sess_log_layer = 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_path = None;
|
||||
Session.sess_log_out = stdout;
|
||||
Session.sess_log_err = stderr;
|
||||
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_quads = false;
|
||||
Session.sess_report_gc = false;
|
||||
Session.sess_report_deps = false;
|
||||
Session.sess_next_crate_id = 0;
|
||||
Session.sess_fuzz_item_count = 5;
|
||||
Session.sess_timings = Hashtbl.create 0;
|
||||
Session.sess_quad_counts = Hashtbl.create 0;
|
||||
Session.sess_lib_dirs = Queue.create ();
|
||||
Session.sess_crate_meta = Hashtbl.create 0;
|
||||
Session.sess_node_id_counter = ref (Node 0);
|
||||
Session.sess_opaque_id_counter = ref (Opaque 0);
|
||||
Session.sess_temp_id_counter = ref (Temp 0);
|
||||
}
|
||||
;;
|
||||
|
||||
let exit_if_failed _ =
|
||||
if sess.Session.sess_failed
|
||||
then exit 1
|
||||
else ()
|
||||
;;
|
||||
|
||||
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 -> ""
|
||||
| FreeBSD_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 in
|
||||
Printf.fprintf stdout "%s\n" (Fmt.fmt_to_str Ast.fmt_mod_items items);
|
||||
exit_if_failed ();
|
||||
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 print_version _ =
|
||||
Printf.fprintf stdout "rustboot %s\n" Version.version;
|
||||
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";
|
||||
"freebsd-x86-elf"],
|
||||
fun s -> (sess.Session.sess_targ <-
|
||||
(match s with
|
||||
"win32-x86-pe" -> Win32_x86_pe
|
||||
| "macos-x86-macho" -> MacOS_x86_macho
|
||||
| "freebsd-x86-elf" -> FreeBSD_x86_elf
|
||||
| _ -> 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"
|
||||
| FreeBSD_x86_elf -> "freebsd-x86-elf"
|
||||
) ^ ")"));
|
||||
("-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_sig <- true)
|
||||
"-lsig" "log signature");
|
||||
(flag (fun _ -> sess.Session.sess_log_passes <- true)
|
||||
"-lpasses" "log passes at high-level");
|
||||
(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_simplify <- true)
|
||||
"-lsimplify" "log simplification");
|
||||
(flag (fun _ -> sess.Session.sess_log_layer <- true)
|
||||
"-llayer" "log layer 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");
|
||||
|
||||
("-lpath", Arg.String
|
||||
(fun s -> sess.Session.sess_log_path <- Some (split_string '.' s)),
|
||||
"module path to restrict logging to");
|
||||
|
||||
(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_quads <- true)
|
||||
"-rquads" "report categories of quad emitted");
|
||||
(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");
|
||||
("-version", Arg.Unit (fun _ -> print_version()),
|
||||
"print version information, then exit");
|
||||
|
||||
(flag (fun _ -> sess.Session.sess_use_pexps <- true)
|
||||
"-pexp" "use pexp portion of AST");
|
||||
|
||||
(flag (fun _ -> sess.Session.sess_minimal <- true)
|
||||
"-minimal" ("reduce code size by disabling various features"
|
||||
^ " (use at own risk)"));
|
||||
|
||||
("-zc", Arg.Int (fun i -> sess.Session.sess_fuzz_item_count <- i),
|
||||
"count of items to generate when fuzzing");
|
||||
|
||||
("-zs", Arg.Int (fun i -> Fuzz.fuzz (Some i) sess),
|
||||
"run fuzzer with given seed");
|
||||
|
||||
(flag (fun _ -> Fuzz.fuzz None sess)
|
||||
"-z" "run fuzzer with random seed")
|
||||
|
||||
] @ (Glue.alt_argspecs sess)
|
||||
;;
|
||||
|
||||
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 parse_input_crate
|
||||
(crate_cache:(crate_id, Ast.mod_items) Hashtbl.t)
|
||||
: 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)
|
||||
crate_cache
|
||||
else
|
||||
if Filename.check_suffix infile ".rs"
|
||||
then
|
||||
Cexp.parse_src_file sess
|
||||
(Lib.get_mod sess abi)
|
||||
(Lib.infer_lib_name sess)
|
||||
crate_cache
|
||||
else
|
||||
begin
|
||||
Printf.fprintf stderr
|
||||
"Error: unrecognized input file type: %s\n"
|
||||
infile;
|
||||
exit 1
|
||||
end
|
||||
in
|
||||
exit_if_failed();
|
||||
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
|
||||
| FreeBSD_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
|
||||
;;
|
||||
|
||||
let (crate:Ast.crate) =
|
||||
try
|
||||
let crate_cache = Hashtbl.create 1 in
|
||||
parse_input_crate crate_cache
|
||||
with
|
||||
Not_implemented (ido, str) ->
|
||||
Session.report_err sess ido str;
|
||||
{ node = Ast.empty_crate'; id = Common.Node 0 }
|
||||
;;
|
||||
|
||||
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" (Fmt.fmt_to_str Ast.fmt_crate crate)
|
||||
end
|
||||
;;
|
||||
|
||||
if sess.Session.sess_log_sig
|
||||
then
|
||||
begin
|
||||
Printf.fprintf stdout "Post-parse signature:\n";
|
||||
Format.set_margin 80;
|
||||
Printf.fprintf stdout "%s\n" (Fmt.fmt_to_str Lib.fmt_iface 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;
|
||||
Simplify.process_crate;
|
||||
Type.process_crate;
|
||||
Typestate.process_crate;
|
||||
Layer.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 ->
|
||||
X86.log sess "selecting insns for %s"
|
||||
code.Semant.code_fixup.fixup_name;
|
||||
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 _ =
|
||||
X86.log sess "selecting insns for %s"
|
||||
code.Semant.code_fixup.fixup_name
|
||||
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
|
||||
| FreeBSD_x86_elf -> Elf.emit_file
|
||||
in
|
||||
Session.time_inner "emit" sess
|
||||
(fun _ -> emitter sess crate code data sem_cx dwarf);
|
||||
exit_if_failed ()
|
||||
;;
|
||||
|
||||
try
|
||||
if sess.Session.sess_alt_backend
|
||||
then Glue.alt_pipeline sess sem_cx crate
|
||||
else main_pipeline ()
|
||||
with
|
||||
Not_implemented (ido, str) ->
|
||||
Session.report_err sess ido str
|
||||
;;
|
||||
|
||||
exit_if_failed ()
|
||||
;;
|
||||
|
||||
if sess.Session.sess_report_timing
|
||||
then
|
||||
begin
|
||||
let cumulative = ref 0.0 in
|
||||
Printf.fprintf stdout "timing:\n\n";
|
||||
Array.iter
|
||||
begin
|
||||
fun name ->
|
||||
let t = Hashtbl.find sess.Session.sess_timings name in
|
||||
Printf.fprintf stdout "%20s: %f\n" name t;
|
||||
cumulative := (!cumulative) +. t
|
||||
end
|
||||
(sorted_htab_keys sess.Session.sess_timings);
|
||||
Printf.fprintf stdout "\n%20s: %f\n" "cumulative" (!cumulative)
|
||||
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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,149 +0,0 @@
|
|||
(*
|
||||
* 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 meta = (string * string) array;;
|
||||
|
||||
type sess =
|
||||
{
|
||||
mutable sess_in: filename option;
|
||||
mutable sess_out: filename option;
|
||||
mutable sess_library_mode: bool;
|
||||
mutable sess_alt_backend: bool;
|
||||
mutable sess_minimal: bool;
|
||||
mutable sess_use_pexps: bool;
|
||||
mutable sess_targ: target;
|
||||
mutable sess_log_lex: bool;
|
||||
mutable sess_log_parse: bool;
|
||||
mutable sess_log_ast: bool;
|
||||
mutable sess_log_sig: bool;
|
||||
mutable sess_log_passes: bool;
|
||||
mutable sess_log_resolve: bool;
|
||||
mutable sess_log_type: bool;
|
||||
mutable sess_log_simplify: bool;
|
||||
mutable sess_log_layer: 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_path: (string list) option;
|
||||
mutable sess_log_out: out_channel;
|
||||
mutable sess_log_err: 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_quads: bool;
|
||||
mutable sess_report_gc: bool;
|
||||
mutable sess_report_deps: bool;
|
||||
mutable sess_next_crate_id: int;
|
||||
mutable sess_fuzz_item_count: int;
|
||||
|
||||
sess_timings: (string, float) Hashtbl.t;
|
||||
sess_quad_counts: (string, int ref) Hashtbl.t;
|
||||
sess_spans: (node_id,span) Hashtbl.t;
|
||||
sess_lib_dirs: filename Queue.t;
|
||||
sess_crate_meta: (meta, crate_id) Hashtbl.t;
|
||||
|
||||
sess_node_id_counter: node_id ref;
|
||||
sess_opaque_id_counter: opaque_id ref;
|
||||
sess_temp_id_counter: temp_id ref;
|
||||
}
|
||||
;;
|
||||
|
||||
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_err
|
||||
;;
|
||||
|
||||
|
||||
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
|
||||
;;
|
||||
|
||||
let report_err sess ido str =
|
||||
let spano = match ido with
|
||||
None -> None
|
||||
| Some id -> get_span sess id
|
||||
in
|
||||
match spano with
|
||||
None ->
|
||||
fail sess "error: %s\n%!" str
|
||||
| Some span ->
|
||||
fail sess "%s: error: %s\n%!"
|
||||
(string_of_span span) str
|
||||
;;
|
||||
|
||||
let make_crate_id (sess:sess) : crate_id =
|
||||
let crate_id = Crate sess.sess_next_crate_id in
|
||||
sess.sess_next_crate_id <- sess.sess_next_crate_id + 1;
|
||||
crate_id
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1795
src/boot/fe/ast.ml
1795
src/boot/fe/ast.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,771 +0,0 @@
|
|||
|
||||
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 * Ast.pexp) array;;
|
||||
|
||||
type meta_pat = (Ast.ident * (Ast.pexp option)) array;;
|
||||
|
||||
type auth = (Ast.name * Ast.auth);;
|
||||
|
||||
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: Ast.pexp;
|
||||
alt_arms: (Ast.pexp * cexp array) array;
|
||||
alt_else: cexp array }
|
||||
|
||||
and cexp_let =
|
||||
{ let_ident: Ast.ident;
|
||||
let_value: Ast.pexp;
|
||||
let_body: cexp array; }
|
||||
|
||||
and cexp_src =
|
||||
{ src_ident: Ast.ident;
|
||||
src_path: Ast.pexp option }
|
||||
|
||||
and cexp_dir =
|
||||
{ dir_ident: Ast.ident;
|
||||
dir_path: Ast.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: Ast.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 * Ast.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 (Ast.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 =
|
||||
Array.map
|
||||
begin
|
||||
fun (ident, item) ->
|
||||
htab_put items ident item
|
||||
end
|
||||
(Item.parse_native_mod_item_from_signature ps)
|
||||
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 au = Pexp.parse_auth ps in
|
||||
expect ps SEMI;
|
||||
let bpos = lexpos ps in
|
||||
CEXP_auth (span ps apos bpos (name, au))
|
||||
|
||||
| _ -> raise (unexpected ps)
|
||||
|
||||
|
||||
and parse_eq_pexp_opt (ps:pstate) : Ast.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_int of int64
|
||||
| PVAL_bool of bool
|
||||
;;
|
||||
|
||||
type cdir =
|
||||
CDIR_meta of ((Ast.ident * string) array)
|
||||
| CDIR_syntax of Ast.name
|
||||
| CDIR_mod of (Ast.ident * Ast.mod_item)
|
||||
| CDIR_auth of auth
|
||||
|
||||
type env = { env_bindings: ((Ast.ident * pval) list) ref;
|
||||
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_int i -> "int " ^ (Int64.to_string i)
|
||||
| PVAL_bool b -> if b then "bool true" else "bool false"
|
||||
in
|
||||
(* FIXME (issue #70): 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; id = _ } ->
|
||||
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; id = _ } ->
|
||||
let ident = cl.let_ident in
|
||||
let v = eval_pexp env cl.let_value in
|
||||
let old_bindings = !(env.env_bindings) in
|
||||
env.env_bindings := (ident,v)::old_bindings;
|
||||
let res = eval_cexps env cl.let_body in
|
||||
env.env_bindings := old_bindings;
|
||||
res
|
||||
|
||||
| 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_crate_cache
|
||||
ps.pstate_sess
|
||||
ps.pstate_get_mod
|
||||
ps.pstate_get_cenv_tok
|
||||
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_crate_cache
|
||||
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:Ast.pexp) : pval =
|
||||
match exp.node with
|
||||
| Ast.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_int av) in
|
||||
let bv = (need_int bv) in
|
||||
PVAL_int
|
||||
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
|
||||
|
||||
| Ast.PEXP_unop (uop, a) ->
|
||||
begin
|
||||
match uop with
|
||||
Ast.UNOP_not ->
|
||||
PVAL_bool (not (eval_pexp_to_bool env a))
|
||||
| Ast.UNOP_neg ->
|
||||
PVAL_int (Int64.neg (eval_pexp_to_int env a))
|
||||
| _ -> bug () "Unexpected unop in Cexp.eval_pexp"
|
||||
end
|
||||
|
||||
| Ast.PEXP_lval (Ast.PLVAL_base (Ast.BASE_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
|
||||
|
||||
| Ast.PEXP_lit (Ast.LIT_bool b) ->
|
||||
PVAL_bool b
|
||||
|
||||
| Ast.PEXP_lit (Ast.LIT_int i) ->
|
||||
PVAL_int i
|
||||
|
||||
| Ast.PEXP_str s ->
|
||||
PVAL_str s
|
||||
|
||||
| _ -> bug () "unexpected Pexp in Cexp.eval_pexp"
|
||||
|
||||
|
||||
and eval_pexp_to_str (env:env) (exp:Ast.pexp) : string =
|
||||
match eval_pexp env exp with
|
||||
PVAL_str s -> s
|
||||
| v -> unexpected_val "str" v
|
||||
|
||||
and need_int (cv:pval) : int64 =
|
||||
match cv with
|
||||
PVAL_int n -> n
|
||||
| v -> unexpected_val "int" v
|
||||
|
||||
and eval_pexp_to_int (env:env) (exp:Ast.pexp) : int64 =
|
||||
need_int (eval_pexp env exp)
|
||||
|
||||
and eval_pexp_to_bool (env:env) (exp:Ast.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 "%s: error: %s\n%!"
|
||||
(Session.string_of_pos (lexpos ps)) str;
|
||||
List.iter
|
||||
(fun (cx,pos) ->
|
||||
Session.fail sess "%s: (parse context): %s\n%!"
|
||||
(Session.string_of_pos pos) cx)
|
||||
ps.pstate_ctxt;
|
||||
let apos = lexpos ps in
|
||||
span ps apos apos Ast.empty_crate'
|
||||
;;
|
||||
|
||||
|
||||
let parse_crate_file
|
||||
(sess:Session.sess)
|
||||
(get_mod:get_mod_fn)
|
||||
(infer_lib_name:(Ast.ident -> filename))
|
||||
(crate_cache:(crate_id, Ast.mod_items) Hashtbl.t)
|
||||
: Ast.crate =
|
||||
let fname = Session.filename_of sess.Session.sess_in in
|
||||
let required = Hashtbl.create 4 in
|
||||
let required_syms = Hashtbl.create 4 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")
|
||||
| FreeBSD_x86_elf -> ("freebsd", "x86", "libc.so.7")
|
||||
| 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 bindings =
|
||||
ref (target_bindings
|
||||
@ build_bindings)
|
||||
in
|
||||
let get_cenv_tok ps ident =
|
||||
match ltab_search (!bindings) ident with
|
||||
None -> raise (err (Printf.sprintf "no binding for '%s' found"
|
||||
ident) ps)
|
||||
| Some (PVAL_bool b) -> LIT_BOOL b
|
||||
| Some (PVAL_str s) -> LIT_STR s
|
||||
| Some (PVAL_int n) -> LIT_INT n
|
||||
in
|
||||
let ps =
|
||||
make_parser crate_cache sess get_mod get_cenv_tok
|
||||
infer_lib_name required required_syms fname
|
||||
in
|
||||
let env = { env_bindings = 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) ->
|
||||
if Hashtbl.mem items name
|
||||
then raise
|
||||
(err ("duplicate mod declaration: " ^ name) ps)
|
||||
else Hashtbl.add 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))
|
||||
(crate_cache:(crate_id, Ast.mod_items) Hashtbl.t)
|
||||
: Ast.crate =
|
||||
let fname = Session.filename_of sess.Session.sess_in in
|
||||
let required = Hashtbl.create 0 in
|
||||
let required_syms = Hashtbl.create 0 in
|
||||
let get_cenv_tok ps ident =
|
||||
raise (err (Printf.sprintf "no binding for '%s' found"
|
||||
ident) ps)
|
||||
in
|
||||
let ps =
|
||||
make_parser crate_cache sess get_mod get_cenv_tok
|
||||
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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,229 +0,0 @@
|
|||
(* The 'fmt' extension is modeled on the posix printf system.
|
||||
*
|
||||
* A posix conversion ostensibly looks like this:
|
||||
*
|
||||
* %[parameter][flags][width][.precision][length]type
|
||||
*
|
||||
* Given the different numeric type bestiary we have, we omit the 'length'
|
||||
* parameter and support slightly different conversions for 'type':
|
||||
*
|
||||
* %[parameter][flags][width][.precision]type
|
||||
*
|
||||
* we also only support translating-to-rust a tiny subset of the possible
|
||||
* combinations at the moment.
|
||||
*)
|
||||
|
||||
exception Malformed of string
|
||||
;;
|
||||
|
||||
type case =
|
||||
CASE_upper
|
||||
| CASE_lower
|
||||
;;
|
||||
|
||||
type signedness =
|
||||
SIGNED
|
||||
| UNSIGNED
|
||||
;;
|
||||
|
||||
type ty =
|
||||
TY_bool
|
||||
| TY_str
|
||||
| TY_char
|
||||
| TY_int of signedness
|
||||
| TY_bits
|
||||
| TY_hex of case
|
||||
(* FIXME: Support more later. *)
|
||||
;;
|
||||
|
||||
type flag =
|
||||
FLAG_left_justify
|
||||
| FLAG_left_zero_pad
|
||||
| FLAG_left_space_pad
|
||||
| FLAG_plus_if_positive
|
||||
| FLAG_alternate
|
||||
;;
|
||||
|
||||
type count =
|
||||
COUNT_is of int
|
||||
| COUNT_is_param of int
|
||||
| COUNT_is_next_param
|
||||
| COUNT_implied
|
||||
|
||||
type conv =
|
||||
{ conv_parameter: int option;
|
||||
conv_flags: flag list;
|
||||
conv_width: count;
|
||||
conv_precision: count;
|
||||
conv_ty: ty }
|
||||
|
||||
type piece =
|
||||
PIECE_string of string
|
||||
| PIECE_conversion of conv
|
||||
|
||||
|
||||
let rec peek_num (s:string) (i:int) (lim:int)
|
||||
: (int * int) option =
|
||||
if i >= lim
|
||||
then None
|
||||
else
|
||||
let c = s.[i] in
|
||||
if '0' <= c && c <= '9'
|
||||
then
|
||||
let n = (Char.code c) - (Char.code '0') in
|
||||
match peek_num s (i+1) lim with
|
||||
None -> Some (n, i+1)
|
||||
| Some (m, i) -> Some (n * 10 + m, i)
|
||||
else None
|
||||
;;
|
||||
|
||||
let parse_parameter (s:string) (i:int) (lim:int)
|
||||
: (int option * int) =
|
||||
if i >= lim
|
||||
then (None, i)
|
||||
else
|
||||
match peek_num s i lim with
|
||||
None -> (None, i)
|
||||
| Some (n, j) ->
|
||||
if j < (String.length s) && s.[j] = '$'
|
||||
then (Some n, j+1)
|
||||
else (None, i)
|
||||
;;
|
||||
|
||||
let rec parse_flags (s:string) (i:int) (lim:int)
|
||||
: (flag list * int) =
|
||||
if i >= lim
|
||||
then ([], i)
|
||||
else
|
||||
let cont flag =
|
||||
let (rest, j) = parse_flags s (i+1) lim in
|
||||
(flag :: rest, j)
|
||||
in
|
||||
match s.[i] with
|
||||
'-' -> cont FLAG_left_justify
|
||||
| '0' -> cont FLAG_left_zero_pad
|
||||
| ' ' -> cont FLAG_left_space_pad
|
||||
| '+' -> cont FLAG_plus_if_positive
|
||||
| '#' -> cont FLAG_alternate
|
||||
| _ -> ([], i)
|
||||
;;
|
||||
|
||||
let parse_count (s:string) (i:int) (lim:int)
|
||||
: (count * int) =
|
||||
if i >= lim
|
||||
then (COUNT_implied, i)
|
||||
else
|
||||
if s.[i] = '*'
|
||||
then
|
||||
begin
|
||||
match parse_parameter s (i+1) lim with
|
||||
(None, j) -> (COUNT_is_next_param, j)
|
||||
| (Some n, j) -> (COUNT_is_param n, j)
|
||||
end
|
||||
else
|
||||
begin
|
||||
match peek_num s i lim with
|
||||
None -> (COUNT_implied, i)
|
||||
| Some (n, j) -> (COUNT_is n, j)
|
||||
end
|
||||
;;
|
||||
|
||||
let parse_precision (s:string) (i:int) (lim:int)
|
||||
: (count * int) =
|
||||
if i >= lim
|
||||
then (COUNT_implied, i)
|
||||
else
|
||||
if s.[i] = '.'
|
||||
then parse_count s (i+1) lim
|
||||
else (COUNT_implied, i)
|
||||
;;
|
||||
|
||||
let parse_type (s:string) (i:int) (lim:int)
|
||||
: (ty * int) =
|
||||
if i >= lim
|
||||
then raise (Malformed "missing type in conversion")
|
||||
else
|
||||
let t =
|
||||
match s.[i] with
|
||||
'b' -> TY_bool
|
||||
| 's' -> TY_str
|
||||
| 'c' -> TY_char
|
||||
| 'd' | 'i' -> TY_int SIGNED
|
||||
| 'u' -> TY_int UNSIGNED
|
||||
| 'x' -> TY_hex CASE_lower
|
||||
| 'X' -> TY_hex CASE_upper
|
||||
| 't' -> TY_bits
|
||||
| _ -> raise (Malformed "unknown type in conversion")
|
||||
in
|
||||
(t, i+1)
|
||||
;;
|
||||
|
||||
let parse_conversion (s:string) (i:int) (lim:int)
|
||||
: (piece * int) =
|
||||
let (parameter, i) = parse_parameter s i lim in
|
||||
let (flags, i) = parse_flags s i lim in
|
||||
let (width, i) = parse_count s i lim in
|
||||
let (precision, i) = parse_precision s i lim in
|
||||
let (ty, i) = parse_type s i lim in
|
||||
(PIECE_conversion { conv_parameter = parameter;
|
||||
conv_flags = flags;
|
||||
conv_width = width;
|
||||
conv_precision = precision;
|
||||
conv_ty = ty }, i)
|
||||
;;
|
||||
|
||||
let parse_fmt_string (s:string) : piece array =
|
||||
let pieces = Queue.create () in
|
||||
let i = ref 0 in
|
||||
let lim = String.length s in
|
||||
let buf = Buffer.create 10 in
|
||||
let flush_buf _ =
|
||||
if (Buffer.length buf) <> 0
|
||||
then
|
||||
let piece =
|
||||
PIECE_string (Buffer.contents buf)
|
||||
in
|
||||
Queue.add piece pieces;
|
||||
Buffer.clear buf;
|
||||
in
|
||||
while (!i) < lim
|
||||
do
|
||||
if s.[!i] = '%'
|
||||
then
|
||||
begin
|
||||
incr i;
|
||||
if (!i) >= lim
|
||||
then raise (Malformed "unterminated conversion at end of string");
|
||||
if s.[!i] = '%'
|
||||
then
|
||||
begin
|
||||
Buffer.add_char buf '%';
|
||||
incr i;
|
||||
end
|
||||
else
|
||||
begin
|
||||
flush_buf();
|
||||
let (piece, j) = parse_conversion s (!i) lim in
|
||||
Queue.add piece pieces;
|
||||
i := j
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
Buffer.add_char buf s.[!i];
|
||||
incr i;
|
||||
end
|
||||
done;
|
||||
flush_buf ();
|
||||
Common.queue_to_arr pieces
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,166 +0,0 @@
|
|||
open Common;;
|
||||
open Ast;;
|
||||
|
||||
let ident_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";;
|
||||
let digit_chars = "1234567890";;
|
||||
|
||||
type scope =
|
||||
SCOPE_crate of crate
|
||||
| SCOPE_mod_item of (ident * mod_item)
|
||||
| SCOPE_block of block
|
||||
| SCOPE_anon
|
||||
;;
|
||||
|
||||
type ctxt =
|
||||
{
|
||||
ctxt_scopes: scope Stack.t;
|
||||
ctxt_node_counter: int ref;
|
||||
ctxt_sess: Session.sess;
|
||||
}
|
||||
|
||||
let generate_ident _ : ident =
|
||||
let char n =
|
||||
if n = 0
|
||||
then '_'
|
||||
else ident_chars.[Random.int (String.length ident_chars)]
|
||||
in
|
||||
let i = 3 + (Random.int 10) in
|
||||
let s = String.create i in
|
||||
for j = 0 to (i-1)
|
||||
do
|
||||
s.[j] <- char j
|
||||
done;
|
||||
s
|
||||
;;
|
||||
|
||||
let wrap (n:'a) (cx:ctxt) : 'a identified =
|
||||
incr cx.ctxt_node_counter;
|
||||
{ node = n; id = Node (!(cx.ctxt_node_counter)) }
|
||||
;;
|
||||
|
||||
let generate_in (scope:scope) (fn:(ctxt -> 'a)) (cx:ctxt) : 'a =
|
||||
Stack.push scope cx.ctxt_scopes;
|
||||
let x = fn cx in
|
||||
ignore (Stack.pop cx.ctxt_scopes);
|
||||
x
|
||||
;;
|
||||
|
||||
let generate_some (fn:(ctxt -> 'a)) (cx:ctxt) : 'a array =
|
||||
let root_count = cx.ctxt_sess.Session.sess_fuzz_item_count in
|
||||
let depth = Stack.length cx.ctxt_scopes in
|
||||
if depth >= root_count
|
||||
then [| |]
|
||||
else
|
||||
Array.init (1 + (Random.int (root_count - depth)))
|
||||
(fun _ -> fn cx)
|
||||
;;
|
||||
|
||||
let rec generate_ty (cx:ctxt) : ty =
|
||||
let subty _ =
|
||||
generate_in SCOPE_anon
|
||||
generate_ty cx
|
||||
in
|
||||
match Random.int (if Random.bool() then 10 else 17) with
|
||||
0 -> TY_nil
|
||||
| 1 -> TY_bool
|
||||
|
||||
| 2 -> TY_mach TY_u8
|
||||
| 3 -> TY_mach TY_u32
|
||||
|
||||
| 4 -> TY_mach TY_i8
|
||||
| 5 -> TY_mach TY_i32
|
||||
|
||||
| 6 -> TY_int
|
||||
| 7 -> TY_uint
|
||||
| 8 -> TY_char
|
||||
| 9 -> TY_str
|
||||
|
||||
| 10 -> TY_tup (generate_in SCOPE_anon
|
||||
(generate_some
|
||||
generate_ty) cx)
|
||||
| 11 -> TY_vec (subty())
|
||||
| 12 ->
|
||||
let generate_elt cx =
|
||||
(generate_ident cx, generate_ty cx)
|
||||
in
|
||||
TY_rec (generate_in SCOPE_anon
|
||||
(generate_some generate_elt) cx)
|
||||
|
||||
| 13 -> TY_chan (subty())
|
||||
| 14 -> TY_port (subty())
|
||||
|
||||
| 15 -> TY_task
|
||||
|
||||
| _ -> TY_box (subty())
|
||||
;;
|
||||
|
||||
|
||||
let rec generate_mod_item (mis:mod_items) (cx:ctxt) : unit =
|
||||
let ident = generate_ident () in
|
||||
let decl i = wrap { decl_item = i;
|
||||
decl_params = [| |] } cx
|
||||
in
|
||||
let item =
|
||||
match Random.int 2 with
|
||||
0 ->
|
||||
let ty = generate_ty cx in
|
||||
let st = Ast.LAYER_value in
|
||||
decl (MOD_ITEM_type (st, ty))
|
||||
| _ ->
|
||||
let mis' = Hashtbl.create 0 in
|
||||
let view = { view_imports = Hashtbl.create 0;
|
||||
view_exports = Hashtbl.create 0; }
|
||||
in
|
||||
let item =
|
||||
decl (MOD_ITEM_mod (view, mis'))
|
||||
in
|
||||
let scope =
|
||||
SCOPE_mod_item (ident, item)
|
||||
in
|
||||
ignore
|
||||
(generate_in scope
|
||||
(generate_some (generate_mod_item mis'))
|
||||
cx);
|
||||
item
|
||||
in
|
||||
Hashtbl.add mis ident item
|
||||
;;
|
||||
|
||||
let fuzz (seed:int option) (sess:Session.sess) : unit =
|
||||
begin
|
||||
match seed with
|
||||
None -> Random.self_init ()
|
||||
| Some s -> Random.init s
|
||||
end;
|
||||
let filename =
|
||||
match sess.Session.sess_out with
|
||||
Some o -> o
|
||||
| None ->
|
||||
match seed with
|
||||
None -> "fuzz.rs"
|
||||
| Some seed -> "fuzz-" ^ (string_of_int seed) ^ ".rs"
|
||||
in
|
||||
let out = open_out_bin filename in
|
||||
let ff = Format.formatter_of_out_channel out in
|
||||
let cx = { ctxt_scopes = Stack.create ();
|
||||
ctxt_node_counter = ref 0;
|
||||
ctxt_sess = sess }
|
||||
in
|
||||
let mis = Hashtbl.create 0 in
|
||||
ignore (generate_some
|
||||
(generate_mod_item mis) cx);
|
||||
fmt_mod_items ff mis;
|
||||
Format.pp_print_flush ff ();
|
||||
close_out out;
|
||||
exit 0
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1334
src/boot/fe/item.ml
1334
src/boot/fe/item.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,478 +0,0 @@
|
|||
|
||||
|
||||
{
|
||||
|
||||
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 newline lexbuf =
|
||||
lexbuf.Lexing.lex_curr_p
|
||||
<- (bump_line lexbuf.Lexing.lex_curr_p)
|
||||
;;
|
||||
|
||||
let mach_suf_table = Hashtbl.create 10
|
||||
;;
|
||||
|
||||
let reserved_suf_table = Hashtbl.create 10
|
||||
;;
|
||||
|
||||
let _ =
|
||||
List.iter (fun (suf, ty) -> Common.htab_put mach_suf_table suf ty)
|
||||
[ ("u8", Common.TY_u8);
|
||||
("i8", Common.TY_i8);
|
||||
("u16", Common.TY_u16);
|
||||
("i16", Common.TY_i16);
|
||||
("u32", Common.TY_u32);
|
||||
("i32", Common.TY_i32);
|
||||
("u64", Common.TY_u64);
|
||||
("i64", Common.TY_i64);
|
||||
("f32", Common.TY_f32);
|
||||
("f64", Common.TY_f64); ]
|
||||
;;
|
||||
|
||||
let _ =
|
||||
List.iter (fun suf -> Common.htab_put reserved_suf_table suf ())
|
||||
[ "f16"; (* IEEE 754-2008 'binary16' interchange format. *)
|
||||
"f80"; (* IEEE 754-1985 'extended' *)
|
||||
"f128"; (* IEEE 754-2008 'binary128' *)
|
||||
"m32"; (* IEEE 754-2008 'decimal32' *)
|
||||
"m64"; (* IEEE 754-2008 'decimal64' *)
|
||||
"m128"; (* IEEE 754-2008 'decimal128' *)
|
||||
"m"; (* One of m32, m64, m128. *)
|
||||
]
|
||||
;;
|
||||
|
||||
let keyword_table = Hashtbl.create 100
|
||||
;;
|
||||
|
||||
let reserved_table = Hashtbl.create 10
|
||||
;;
|
||||
|
||||
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);
|
||||
("assert", ASSERT);
|
||||
("claim", CLAIM);
|
||||
("prove", PROVE);
|
||||
|
||||
("state", STATE);
|
||||
("gc", GC);
|
||||
|
||||
("unsafe", UNSAFE);
|
||||
|
||||
("native", NATIVE);
|
||||
("mutable", MUTABLE);
|
||||
("auto", AUTO);
|
||||
|
||||
("fn", FN);
|
||||
("iter", ITER);
|
||||
|
||||
("import", IMPORT);
|
||||
("export", EXPORT);
|
||||
|
||||
("let", LET);
|
||||
("const", CONST);
|
||||
|
||||
("log", LOG);
|
||||
("log_err", LOG_ERR);
|
||||
("break", BREAK);
|
||||
("cont", CONT);
|
||||
("spawn", SPAWN);
|
||||
("thread", THREAD);
|
||||
("yield", YIELD);
|
||||
("join", JOIN);
|
||||
|
||||
("bool", BOOL);
|
||||
|
||||
("int", INT);
|
||||
("uint", UINT);
|
||||
("float", FLOAT);
|
||||
|
||||
("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 _ =
|
||||
List.iter (fun kwd -> Common.htab_put reserved_table kwd ())
|
||||
[ "f16"; (* IEEE 754-2008 'binary16' interchange format. *)
|
||||
"f80"; (* IEEE 754-1985 'extended' *)
|
||||
"f128"; (* IEEE 754-2008 'binary128' *)
|
||||
"m32"; (* IEEE 754-2008 'decimal32' *)
|
||||
"m64"; (* IEEE 754-2008 'decimal64' *)
|
||||
"m128"; (* IEEE 754-2008 'decimal128' *)
|
||||
"dec"; (* One of m32, m64, m128. *)
|
||||
];
|
||||
;;
|
||||
|
||||
}
|
||||
|
||||
let hexdig = ['0'-'9' 'a'-'f' 'A'-'F']
|
||||
let decdig = ['0'-'9']
|
||||
let bin = '0' 'b' ['0' '1' '_']*
|
||||
let hex = '0' 'x' ['0'-'9' 'a'-'f' 'A'-'F' '_']*
|
||||
let dec = decdig ['0'-'9' '_']*
|
||||
let exp = ['e''E']['-''+']? dec
|
||||
let flo = (dec '.' dec (exp?)) | (dec exp)
|
||||
|
||||
let mach_float_suf = "f32"|"f64"
|
||||
let mach_int_suf = ['u''i']('8'|"16"|"32"|"64")
|
||||
let flo_suf = ['m''f']("16"|"32"|"64"|"80"|"128")
|
||||
|
||||
let ws = [ ' ' '\t' '\r' ]
|
||||
|
||||
let id = ['a'-'z' 'A'-'Z' '_']['a'-'z' 'A'-'Z' '0'-'9' '_']*
|
||||
|
||||
rule token = parse
|
||||
ws+ { token lexbuf }
|
||||
| '\n' { newline lexbuf;
|
||||
token lexbuf }
|
||||
| "//" [^'\n']* { token lexbuf }
|
||||
| "/*" { comment 1 lexbuf }
|
||||
| '+' { PLUS }
|
||||
| '-' { MINUS }
|
||||
| '*' { STAR }
|
||||
| '/' { SLASH }
|
||||
| '%' { PERCENT }
|
||||
| '=' { EQ }
|
||||
| '<' { LT }
|
||||
| "<=" { LE }
|
||||
| "==" { EQEQ }
|
||||
| "!=" { NE }
|
||||
| ">=" { GE }
|
||||
| '>' { GT }
|
||||
| '!' { NOT }
|
||||
| '&' { AND }
|
||||
| "&&" { ANDAND }
|
||||
| '|' { OR }
|
||||
| "||" { OROR }
|
||||
| "<<" { LSL }
|
||||
| ">>" { LSR }
|
||||
| ">>>" { ASR }
|
||||
| '~' { TILDE }
|
||||
| '{' { LBRACE }
|
||||
| '_' (decdig+ 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 }
|
||||
| '?' { QUES }
|
||||
| "<-" { LARROW }
|
||||
| "<|" { SEND }
|
||||
| "->" { RARROW }
|
||||
| '(' { LPAREN }
|
||||
| ')' { RPAREN }
|
||||
| '[' { LBRACKET }
|
||||
| ']' { RBRACKET }
|
||||
|
||||
| id as i
|
||||
{
|
||||
match Common.htab_search keyword_table i with
|
||||
Some tok -> tok
|
||||
| None ->
|
||||
if Hashtbl.mem reserved_table i
|
||||
then fail lexbuf "reserved keyword"
|
||||
else IDENT (i)
|
||||
}
|
||||
|
||||
| (bin|hex|dec) as n { LIT_INT (Int64.of_string n) }
|
||||
| ((bin|hex|dec) as n) 'u' { LIT_UINT (Int64.of_string n) }
|
||||
| ((bin|hex|dec) as n)
|
||||
(mach_int_suf as s)
|
||||
{
|
||||
match Common.htab_search mach_suf_table s with
|
||||
Some tm -> LIT_MACH_INT (tm, Int64.of_string n)
|
||||
| None ->
|
||||
if Hashtbl.mem reserved_suf_table s
|
||||
then fail lexbuf "reserved mach-int suffix"
|
||||
else fail lexbuf "bad mach-int suffix"
|
||||
}
|
||||
|
||||
| flo as n { LIT_FLOAT (float_of_string n) }
|
||||
| flo 'm' { fail lexbuf "reseved mach-float suffix" }
|
||||
| (flo as n) (flo_suf as s)
|
||||
{
|
||||
match Common.htab_search mach_suf_table s with
|
||||
Some tm -> LIT_MACH_FLOAT (tm, float_of_string n)
|
||||
| None ->
|
||||
if Hashtbl.mem reserved_suf_table s
|
||||
then fail lexbuf "reserved mach-float suffix"
|
||||
else fail lexbuf "bad mach-float suffix"
|
||||
}
|
||||
|
||||
| '\'' { char lexbuf }
|
||||
| '"' { let buf = Buffer.create 32 in
|
||||
str buf lexbuf }
|
||||
| _ as c { let s = Char.escaped c in
|
||||
fail lexbuf ("Bad character: " ^ s) }
|
||||
| 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 }
|
||||
|
||||
|
||||
| [^'\\' '{' '}'] as c { Buffer.add_char buf c;
|
||||
if c = '\n'
|
||||
then newline lexbuf;
|
||||
bracequote buf depth lexbuf }
|
||||
|
||||
|
||||
and comment depth = parse
|
||||
|
||||
'/' '*' { comment (depth+1) lexbuf }
|
||||
|
||||
| '*' '/' { if depth = 1
|
||||
then token lexbuf
|
||||
else comment (depth-1) lexbuf }
|
||||
|
||||
| '\n' { newline lexbuf;
|
||||
comment depth lexbuf }
|
||||
|
||||
| _ { comment depth lexbuf }
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,372 +0,0 @@
|
|||
|
||||
open Common;;
|
||||
open Token;;
|
||||
|
||||
(* Fundamental parser types and actions *)
|
||||
|
||||
type get_mod_fn = (Ast.meta_pat
|
||||
-> node_id
|
||||
-> (crate_id, Ast.mod_items) Hashtbl.t
|
||||
-> (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_crate_cache : (crate_id, Ast.mod_items) Hashtbl.t;
|
||||
pstate_get_mod : get_mod_fn;
|
||||
pstate_get_cenv_tok : pstate -> Ast.ident -> token;
|
||||
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
|
||||
(crate_cache:(crate_id, Ast.mod_items) Hashtbl.t)
|
||||
(sess:Session.sess)
|
||||
(get_mod:get_mod_fn)
|
||||
(get_cenv_tok:pstate -> Ast.ident -> token)
|
||||
(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_crate_cache = crate_cache;
|
||||
pstate_get_mod = get_mod;
|
||||
pstate_get_cenv_tok = get_cenv_tok;
|
||||
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 r = ps.pstate_sess.Session.sess_node_id_counter in
|
||||
let id = !r in
|
||||
r := Node ((int_of_node id)+1);
|
||||
id
|
||||
;;
|
||||
|
||||
let next_opaque_id (ps:pstate) : opaque_id =
|
||||
let r = ps.pstate_sess.Session.sess_opaque_id_counter in
|
||||
let id = !r in
|
||||
r := 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)
|
||||
| Ast.ATOM_pexp _ -> bug () "Parser.clone_atom on ATOM_pexp"
|
||||
;;
|
||||
|
||||
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_local;
|
||||
Ast.slot_ty = Some Ast.TY_nil }
|
||||
;;
|
||||
|
||||
let (slot_auto:Ast.slot) =
|
||||
{ Ast.slot_mode = Ast.MODE_local;
|
||||
Ast.slot_ty = None }
|
||||
;;
|
||||
|
||||
let build_tmp
|
||||
(ps:pstate)
|
||||
(slot:Ast.slot)
|
||||
(apos:pos)
|
||||
(bpos:pos)
|
||||
: (temp_id * Ast.lval * Ast.stmt) =
|
||||
let r = ps.pstate_sess.Session.sess_temp_id_counter in
|
||||
let id = !r in
|
||||
r := Temp ((int_of_temp id)+1);
|
||||
iflog ps
|
||||
(fun _ -> log ps "building temporary %d" (int_of_temp id));
|
||||
let decl = Ast.DECL_slot (Ast.KEY_temp id, (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 id)) in
|
||||
(id, tmp, declstmt)
|
||||
;;
|
||||
|
||||
(* Simple helpers *)
|
||||
|
||||
(* FIXME (issue #71): 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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1441
src/boot/fe/pexp.ml
1441
src/boot/fe/pexp.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,334 +0,0 @@
|
|||
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
|
||||
| QUES
|
||||
| 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
|
||||
| BREAK
|
||||
| CONT
|
||||
|
||||
(* Type and type-state keywords *)
|
||||
| TYPE
|
||||
| CHECK
|
||||
| ASSERT
|
||||
| CLAIM
|
||||
| PROVE
|
||||
|
||||
(* Layer keywords *)
|
||||
| STATE
|
||||
| GC
|
||||
|
||||
(* Unsafe-block keyword *)
|
||||
| UNSAFE
|
||||
|
||||
(* Type qualifiers *)
|
||||
| NATIVE
|
||||
| AUTO
|
||||
| MUTABLE
|
||||
|
||||
(* Name management *)
|
||||
| IMPORT
|
||||
| EXPORT
|
||||
|
||||
(* Value / stmt declarators *)
|
||||
| LET
|
||||
| CONST
|
||||
|
||||
(* Magic runtime services *)
|
||||
| LOG
|
||||
| LOG_ERR
|
||||
| SPAWN
|
||||
| BIND
|
||||
| THREAD
|
||||
| YIELD
|
||||
| JOIN
|
||||
|
||||
(* Literals *)
|
||||
| LIT_INT of int64
|
||||
| LIT_UINT of int64
|
||||
| LIT_FLOAT of float
|
||||
| LIT_MACH_INT of Common.ty_mach * int64
|
||||
| LIT_MACH_FLOAT of Common.ty_mach * float
|
||||
| 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
|
||||
| FLOAT
|
||||
| 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 -> ":"
|
||||
| QUES -> "?"
|
||||
| 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"
|
||||
| BREAK -> "break"
|
||||
| CONT -> "cont"
|
||||
|
||||
(* Type and type-state keywords *)
|
||||
| TYPE -> "type"
|
||||
| CHECK -> "check"
|
||||
| ASSERT -> "assert"
|
||||
| CLAIM -> "claim"
|
||||
| PROVE -> "prove"
|
||||
|
||||
(* Layer keywords *)
|
||||
| STATE -> "state"
|
||||
| GC -> "gc"
|
||||
|
||||
(* Unsafe-block keyword *)
|
||||
| UNSAFE -> "unsafe"
|
||||
|
||||
(* Type qualifiers *)
|
||||
| NATIVE -> "native"
|
||||
| AUTO -> "auto"
|
||||
| MUTABLE -> "mutable"
|
||||
|
||||
(* Name management *)
|
||||
| IMPORT -> "import"
|
||||
| EXPORT -> "export"
|
||||
|
||||
(* Value / stmt declarators. *)
|
||||
| LET -> "let"
|
||||
| CONST -> "const"
|
||||
|
||||
(* Magic runtime services *)
|
||||
| LOG -> "log"
|
||||
| LOG_ERR -> "log_err"
|
||||
| SPAWN -> "spawn"
|
||||
| BIND -> "bind"
|
||||
| THREAD -> "thread"
|
||||
| YIELD -> "yield"
|
||||
| JOIN -> "join"
|
||||
|
||||
(* Literals *)
|
||||
| LIT_INT i -> Int64.to_string i
|
||||
| LIT_UINT i -> (Int64.to_string i) ^ "u"
|
||||
| LIT_FLOAT s -> string_of_float s
|
||||
| LIT_MACH_INT (tm, i) ->
|
||||
(Int64.to_string i) ^ (Common.string_of_ty_mach tm)
|
||||
| LIT_MACH_FLOAT (tm, f) ->
|
||||
(string_of_float f) ^ (Common.string_of_ty_mach tm)
|
||||
| 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"
|
||||
| FLOAT -> "float"
|
||||
| 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 -> "iter"
|
||||
|
||||
(* 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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,156 +0,0 @@
|
|||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "alias"
|
||||
(should_log cx 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 =
|
||||
let defn_id = lval_base_defn_id cx lval in
|
||||
if (defn_id_is_slot cx defn_id)
|
||||
then alias_slot defn_id
|
||||
in
|
||||
|
||||
let alias_atom at =
|
||||
match at with
|
||||
Ast.ATOM_lval lv -> alias lv
|
||||
| _ -> () (* Aliasing a literal is harmless, if weird. *)
|
||||
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 check_no_alias_bindings
|
||||
(fn:Ast.lval)
|
||||
(args:(Ast.atom option) array)
|
||||
: unit =
|
||||
let fty = match lval_ty cx fn with
|
||||
Ast.TY_fn tfn -> tfn
|
||||
| _ -> err (Some (lval_base_id fn)) "binding non-fn"
|
||||
in
|
||||
let arg_slots = (fst fty).Ast.sig_input_slots in
|
||||
Array.iteri
|
||||
begin
|
||||
fun i arg ->
|
||||
match arg with
|
||||
None -> ()
|
||||
| Some _ ->
|
||||
match arg_slots.(i).Ast.slot_mode with
|
||||
Ast.MODE_local -> ()
|
||||
| Ast.MODE_alias ->
|
||||
err (Some (lval_base_id fn)) "binding alias slot"
|
||||
end
|
||||
args
|
||||
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_bind (_, fn, args) ->
|
||||
check_no_alias_bindings fn args
|
||||
|
||||
| Ast.STMT_send (_, src) -> alias src
|
||||
| Ast.STMT_recv (dst, _) -> alias dst
|
||||
| Ast.STMT_new_port (dst) -> alias dst
|
||||
| Ast.STMT_new_chan (dst, _) -> alias dst
|
||||
| Ast.STMT_new_vec (dst, _, _) -> alias dst
|
||||
| Ast.STMT_new_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_base_defn_id cx lv in
|
||||
if (not (Stack.is_empty curr_stmt)) && (defn_id_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 passes =
|
||||
[|
|
||||
(alias_analysis_visitor cx
|
||||
Walk.empty_visitor);
|
||||
|]
|
||||
in
|
||||
run_passes cx "alias" passes
|
||||
cx.ctxt_sess.Session.sess_log_alias log crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,134 +0,0 @@
|
|||
(*
|
||||
* A simple dead-code analysis that rejects code following unconditional
|
||||
* 'ret' or 'be'.
|
||||
*)
|
||||
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "dead"
|
||||
(should_log cx 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 exit_stmt_if_exit_body s body =
|
||||
if (Hashtbl.mem must_exit body.id) then
|
||||
Hashtbl.add must_exit s.id ()
|
||||
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 w
|
||||
| Ast.STMT_do_while w ->
|
||||
exit_stmt_if_exit_body s w.Ast.while_body
|
||||
|
||||
| Ast.STMT_for_each f ->
|
||||
exit_stmt_if_exit_body s f.Ast.for_each_body
|
||||
|
||||
| Ast.STMT_for f ->
|
||||
exit_stmt_if_exit_body s f.Ast.for_body
|
||||
|
||||
| Ast.STMT_if { Ast.if_then = b1;
|
||||
Ast.if_else = Some b2;
|
||||
Ast.if_test = _ } ->
|
||||
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;
|
||||
Ast.alt_tag_lval = _ } ->
|
||||
let arm_ids =
|
||||
Array.map (fun { node = (_, block); id = _ } -> 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;
|
||||
Ast.alt_type_lval = _ } ->
|
||||
let arm_ids = Array.map (fun { node = ((_, _), block); id = _ } ->
|
||||
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 passes =
|
||||
[|
|
||||
(dead_code_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|]
|
||||
in
|
||||
|
||||
run_passes cx "dead" passes
|
||||
cx.ctxt_sess.Session.sess_log_dead log crate;
|
||||
()
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
3213
src/boot/me/dwarf.ml
3213
src/boot/me/dwarf.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,108 +0,0 @@
|
|||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "layer"
|
||||
(should_log cx cx.ctxt_sess.Session.sess_log_layer)
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog cx thunk =
|
||||
if (should_log cx cx.ctxt_sess.Session.sess_log_layer)
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
|
||||
let state_layer_checking_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
(*
|
||||
* This visitor enforces the following rules:
|
||||
*
|
||||
* - A channel type carrying a state 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 cx t' ->
|
||||
err None "channel of state type: %a " Ast.sprintf_ty t'
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let check_write s dst =
|
||||
let is_init = Hashtbl.mem cx.ctxt_stmt_is_init s.id in
|
||||
let dst_ty = lval_ty cx dst in
|
||||
let is_mutable =
|
||||
match dst_ty with
|
||||
Ast.TY_mutable _ -> true
|
||||
| _ -> false
|
||||
in
|
||||
iflog cx
|
||||
(fun _ -> log cx "checking %swrite to %slval #%d = %a of type %a"
|
||||
(if is_init then "initializing " else "")
|
||||
(if is_mutable then "mutable " else "")
|
||||
(int_of_node (lval_base_id dst))
|
||||
Ast.sprintf_lval dst
|
||||
Ast.sprintf_ty dst_ty);
|
||||
if (is_mutable or is_init)
|
||||
then ()
|
||||
else err (Some s.id)
|
||||
"writing to immutable type %a in statement %a"
|
||||
Ast.sprintf_ty dst_ty Ast.sprintf_stmt s
|
||||
in
|
||||
(* FIXME (issue #75): enforce the no-write-alias-to-immutable-slot
|
||||
* rule.
|
||||
*)
|
||||
let visit_stmt_pre s =
|
||||
begin
|
||||
match s.node with
|
||||
Ast.STMT_copy (lv_dst, _)
|
||||
| Ast.STMT_call (lv_dst, _, _)
|
||||
| Ast.STMT_spawn (lv_dst, _, _, _, _)
|
||||
| Ast.STMT_recv (lv_dst, _)
|
||||
| Ast.STMT_bind (lv_dst, _, _)
|
||||
| Ast.STMT_new_rec (lv_dst, _, _)
|
||||
| Ast.STMT_new_tup (lv_dst, _)
|
||||
| Ast.STMT_new_vec (lv_dst, _, _)
|
||||
| Ast.STMT_new_str (lv_dst, _)
|
||||
| Ast.STMT_new_port lv_dst
|
||||
| Ast.STMT_new_chan (lv_dst, _)
|
||||
| Ast.STMT_new_box (lv_dst, _, _) ->
|
||||
check_write s lv_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 process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let passes =
|
||||
[|
|
||||
(state_layer_checking_visitor cx
|
||||
Walk.empty_visitor);
|
||||
|]
|
||||
in
|
||||
run_passes cx "layer" passes
|
||||
cx.ctxt_sess.Session.sess_log_layer log crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,480 +0,0 @@
|
|||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "layout"
|
||||
(should_log cx 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
|
||||
* |closure/obj ptr (impl. arg) | + abi_implicit_args_sz
|
||||
* |task ptr (implicit arg) |
|
||||
* |output ptr (implicit arg) |
|
||||
* +----------------------------+ <-- fp + abi_frame_base_sz
|
||||
* |return pc |
|
||||
* |old fp | <-- fp
|
||||
* +----------------------------+
|
||||
* |other callee-save registers |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - callee_saves
|
||||
* |crate ptr |
|
||||
* |crate-rel frame info disp |
|
||||
* +----------------------------+ <-- fp - (callee_saves
|
||||
* |spills determined in ra | + abi_frame_info_sz)
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - (callee_saves
|
||||
* |... | + abi_frame_info_sz
|
||||
* |frame-allocated stuff | + spillsz)
|
||||
* |determined in resolve |
|
||||
* |laid out in layout |
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - (callee_saves + framesz)
|
||||
* |call space | == sp + callsz
|
||||
* |... |
|
||||
* |... |
|
||||
* +----------------------------+ <-- fp - (callee_saves
|
||||
* + 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 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 (should_log cx 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 = get_slot cx id in
|
||||
let rt = slot_referent_type cx slot in
|
||||
let (elt_size, elt_align) = rty_layout rt in
|
||||
if vregs_ok
|
||||
&& (is_subword_size elt_size)
|
||||
&& (not (type_is_structured cx (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 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.box_rc_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 =
|
||||
(* NB: the "frame size" does not include the callee-saves. *)
|
||||
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 = local_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 (hdr, _, _) when Array.length hdr <> 0 ->
|
||||
enter_frame i.id;
|
||||
layout_header i.id
|
||||
(header_slot_ids hdr)
|
||||
|
||||
| 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_obj _ -> leave_frame ()
|
||||
| Ast.MOD_ITEM_tag (hdr, _, _) when Array.length hdr <> 0 ->
|
||||
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 callee_saves_sz = SIZE_fixed cx.ctxt_abi.Abi.abi_callee_saves_sz in
|
||||
let info_sz = SIZE_fixed cx.ctxt_abi.Abi.abi_frame_info_sz in
|
||||
let locals_off = add_sz spill_sz (add_sz info_sz callee_saves_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 =
|
||||
if lval_base_is_item cx callee
|
||||
then Array.length (lval_item cx callee).node.Ast.decl_params
|
||||
else 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 passes =
|
||||
[|
|
||||
(layout_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|];
|
||||
in
|
||||
run_passes cx "layout" passes
|
||||
cx.ctxt_sess.Session.sess_log_layout log crate
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,164 +0,0 @@
|
|||
(*
|
||||
* Computes iterator-loop nesting depths and max depth of each function.
|
||||
*)
|
||||
|
||||
open Semant;;
|
||||
open Common;;
|
||||
|
||||
let log cx = Session.log "loop"
|
||||
(should_log cx 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 ();
|
||||
let fcx = Stack.top fcxs in
|
||||
htab_put cx.ctxt_block_loop_depths b.id fcx.current_depth;
|
||||
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 passes =
|
||||
[|
|
||||
(loop_depth_visitor cx
|
||||
Walk.empty_visitor)
|
||||
|]
|
||||
in
|
||||
|
||||
run_passes cx "loop" passes
|
||||
cx.ctxt_sess.Session.sess_log_loop log crate
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,935 +0,0 @@
|
|||
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. )
|
||||
*
|
||||
*)
|
||||
|
||||
exception Resolution_failure of (Ast.name * Ast.name) list
|
||||
|
||||
let log cx = Session.log "resolve"
|
||||
(should_log cx cx.ctxt_sess.Session.sess_log_resolve)
|
||||
cx.ctxt_sess.Session.sess_log_out
|
||||
;;
|
||||
|
||||
let iflog cx thunk =
|
||||
if (should_log cx 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) =
|
||||
htab_put cx.ctxt_all_blocks b.id b.node;
|
||||
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;
|
||||
Ast.alt_tag_lval = _ } ->
|
||||
let rec resolve_pat block pat =
|
||||
match pat with
|
||||
Ast.PAT_slot ({ id = slot_id; node = _ }, 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); id = _ } ->
|
||||
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)
|
||||
(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 (path_to_name cx.ctxt_curr_path);
|
||||
log cx "collected item #%d: %s" (int_of_node i.id) n;
|
||||
begin
|
||||
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 (hdr, _, _) ->
|
||||
note_header i.id hdr
|
||||
| Ast.MOD_ITEM_type (_, Ast.TY_tag ttag) ->
|
||||
Hashtbl.replace cx.ctxt_user_tag_names ttag.Ast.tag_id
|
||||
(path_to_name cx.ctxt_curr_path)
|
||||
| _ -> ()
|
||||
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 (path_to_name cx.ctxt_curr_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 (path_to_name cx.ctxt_curr_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
|
||||
(path_to_name cx.ctxt_curr_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
|
||||
RES_failed name' -> raise (Resolution_failure [ name', name ])
|
||||
| RES_ok (_, id) ->
|
||||
match htab_search cx.ctxt_all_defns id with
|
||||
Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_type _;
|
||||
Ast.decl_params = _ })
|
||||
| Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_obj _;
|
||||
Ast.decl_params = _ })
|
||||
| Some (DEFN_ty_param _) -> id
|
||||
| _ ->
|
||||
err None "Found non-type binding for %a"
|
||||
Ast.sprintf_name name
|
||||
;;
|
||||
|
||||
type recur_info =
|
||||
{ recur_all_nodes: node_id list }
|
||||
;;
|
||||
|
||||
let empty_recur_info =
|
||||
{ recur_all_nodes = []; }
|
||||
;;
|
||||
|
||||
let push_node r n =
|
||||
{ recur_all_nodes = n :: r.recur_all_nodes }
|
||||
|
||||
|
||||
let report_resolution_failure type_names =
|
||||
let rec recur type_names str =
|
||||
let stringify_pair (part, whole) =
|
||||
if part = whole then
|
||||
Printf.sprintf "'%a'" Ast.sprintf_name part
|
||||
else
|
||||
Printf.sprintf "'%a' in name '%a'" Ast.sprintf_name part
|
||||
Ast.sprintf_name whole
|
||||
in
|
||||
match type_names with
|
||||
[] -> bug () "no name in resolution failure"
|
||||
| [ pair ] -> err None "unbound name %s%s" (stringify_pair pair) str
|
||||
| pair::pairs ->
|
||||
recur pairs
|
||||
(Printf.sprintf " while resolving %s" (stringify_pair pair))
|
||||
in
|
||||
recur type_names ""
|
||||
|
||||
let rec lookup_type_by_name
|
||||
?loc:loc
|
||||
(cx:ctxt)
|
||||
(scopes:scope list)
|
||||
(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 ?loc:loc cx [] scopes name with
|
||||
RES_failed name' -> raise (Resolution_failure [ name', name ])
|
||||
| RES_ok (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 loc "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 ?loc:loc cx scopes 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"
|
||||
(Fmt.fmt_to_str Ast.fmt_decl_params params);
|
||||
log cx "args: %s"
|
||||
(Fmt.fmt_to_str Ast.fmt_app_args args);
|
||||
end;
|
||||
let ty =
|
||||
rebuild_ty_under_params ?node_id:loc cx None 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
|
||||
?loc:loc
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list))
|
||||
(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 ?loc:loc cx scopes 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);
|
||||
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);
|
||||
try
|
||||
resolve_type ?loc:loc cx scopes recur t
|
||||
with Resolution_failure names ->
|
||||
raise (Resolution_failure ((name, name)::names))
|
||||
in
|
||||
let fold =
|
||||
{ base with
|
||||
ty_fold_named = ty_fold_named; }
|
||||
in
|
||||
let t' = fold_ty cx 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)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let tinfos = Hashtbl.create 0 in
|
||||
|
||||
let resolve_ty ?(loc=id_of_scope (List.hd (!scopes))) (t:Ast.ty) : Ast.ty =
|
||||
try
|
||||
resolve_type ~loc:loc cx (!scopes) empty_recur_info t
|
||||
with Resolution_failure pairs ->
|
||||
report_resolution_failure pairs
|
||||
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);
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
log cx "collected resolved slot #%d with type %s"
|
||||
(int_of_node slot.id)
|
||||
(match slot.node.Ast.slot_ty with
|
||||
None -> "??"
|
||||
| Some t -> (Fmt.fmt_to_str Ast.fmt_ty t)));
|
||||
inner.Walk.visit_slot_identified_pre slot
|
||||
in
|
||||
|
||||
let visit_mod_item_pre id params item =
|
||||
let resolve_and_store_type _ =
|
||||
let t = ty_of_mod_item item in
|
||||
let ty = resolve_ty ~loc:item.id t in
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
log cx "resolved item %s, type as %a" id Ast.sprintf_ty ty);
|
||||
htab_put cx.ctxt_all_item_types item.id ty;
|
||||
in
|
||||
begin
|
||||
try
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_type (_, ty) ->
|
||||
let ty = resolve_ty ~loc:item.id ty in
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
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;
|
||||
if Hashtbl.mem cx.ctxt_all_item_names item.id then
|
||||
Hashtbl.add cx.ctxt_user_type_names ty
|
||||
(Hashtbl.find cx.ctxt_all_item_names item.id)
|
||||
|
||||
(*
|
||||
* Don't resolve the "type" of a mod item; just resolve its
|
||||
* members.
|
||||
*)
|
||||
| Ast.MOD_ITEM_mod _ -> ()
|
||||
|
||||
| Ast.MOD_ITEM_tag (slots, oid, n) ->
|
||||
resolve_and_store_type ();
|
||||
let tinfo =
|
||||
htab_search_or_add
|
||||
tinfos oid
|
||||
(fun _ ->
|
||||
{ tag_idents = Hashtbl.create 0;
|
||||
tag_nums = Hashtbl.create 0; } )
|
||||
in
|
||||
let ttup =
|
||||
Array.map
|
||||
(fun (s,_) -> (slot_ty (resolve_slot_identified s).node))
|
||||
slots
|
||||
in
|
||||
if not (Hashtbl.mem tinfo.tag_idents id)
|
||||
then
|
||||
begin
|
||||
htab_put tinfo.tag_idents id (n, item.id, ttup);
|
||||
htab_put tinfo.tag_nums n (id, item.id, ttup);
|
||||
end
|
||||
|
||||
| _ -> resolve_and_store_type ()
|
||||
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_ty ~loc:fn.id (Ast.TY_fn (ty_fn_of_fn fn.node)) in
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
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 rebuilt_pexps = Hashtbl.create 0 in
|
||||
let get_rebuilt_pexp p =
|
||||
Hashtbl.find rebuilt_pexps p.id
|
||||
in
|
||||
|
||||
let visit_pexp_post p =
|
||||
inner.Walk.visit_pexp_post p;
|
||||
let rebuild_plval pl =
|
||||
match pl with
|
||||
Ast.PLVAL_base (Ast.BASE_app (id, tys)) ->
|
||||
Ast.PLVAL_base (Ast.BASE_app (id, Array.map resolve_ty tys))
|
||||
| Ast.PLVAL_base _ -> pl
|
||||
| Ast.PLVAL_ext_name (pexp, nc) ->
|
||||
let pexp = get_rebuilt_pexp pexp in
|
||||
let nc =
|
||||
match nc with
|
||||
Ast.COMP_ident _
|
||||
| Ast.COMP_idx _ -> nc
|
||||
| Ast.COMP_app (id, tys) ->
|
||||
Ast.COMP_app (id, Array.map resolve_ty tys)
|
||||
in
|
||||
Ast.PLVAL_ext_name (pexp, nc)
|
||||
|
||||
| Ast.PLVAL_ext_pexp (a, b) ->
|
||||
Ast.PLVAL_ext_pexp (get_rebuilt_pexp a,
|
||||
get_rebuilt_pexp b)
|
||||
| Ast.PLVAL_ext_deref p ->
|
||||
Ast.PLVAL_ext_deref (get_rebuilt_pexp p)
|
||||
in
|
||||
let p =
|
||||
match p.node with
|
||||
Ast.PEXP_lval pl ->
|
||||
let pl' = rebuild_plval pl in
|
||||
iflog cx (fun _ -> log cx "rebuilt plval %a as %a (#%d)"
|
||||
Ast.sprintf_plval pl Ast.sprintf_plval pl'
|
||||
(int_of_node p.id));
|
||||
{ p with node = Ast.PEXP_lval pl' }
|
||||
|
||||
| _ -> p
|
||||
in
|
||||
htab_put rebuilt_pexps p.id p
|
||||
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_deref
|
||||
| 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_atom (Ast.ATOM_pexp _) ->
|
||||
bug () "Resolve.rebuild_lval' on ATOM_pexp"
|
||||
|
||||
| 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
|
||||
|
||||
let visit_crate_post c =
|
||||
inner.Walk.visit_crate_post c;
|
||||
Hashtbl.iter (fun k v -> Hashtbl.add cx.ctxt_all_tag_info k v) tinfos
|
||||
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;
|
||||
Walk.visit_pexp_post = visit_pexp_post;
|
||||
Walk.visit_crate_post = visit_crate_post }
|
||||
;;
|
||||
|
||||
|
||||
let lval_base_resolving_visitor
|
||||
(cx:ctxt)
|
||||
(scopes:(scope list) ref)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let lookup_defn_by_ident id ident =
|
||||
iflog cx
|
||||
(fun _ -> log cx "looking up slot or item with ident '%s'" ident);
|
||||
match lookup cx (!scopes) (Ast.KEY_ident ident) with
|
||||
RES_failed _ -> err (Some id) "unresolved identifier '%s'" ident
|
||||
| RES_ok (_, id) ->
|
||||
((iflog cx (fun _ -> log cx "resolved to node id #%d"
|
||||
(int_of_node id))); id)
|
||||
in
|
||||
let lookup_slot_by_temp id temp =
|
||||
iflog cx (fun _ -> log cx "looking up temp slot #%d" (int_of_temp temp));
|
||||
let res = lookup cx (!scopes) (Ast.KEY_temp temp) in
|
||||
match res with
|
||||
RES_failed _ -> err
|
||||
(Some id) "unresolved temp node #%d" (int_of_temp temp)
|
||||
| RES_ok (_, id) ->
|
||||
(iflog cx
|
||||
(fun _ -> log cx "resolved to node id #%d" (int_of_node id));
|
||||
id)
|
||||
in
|
||||
let lookup_defn_by_name_base id nb =
|
||||
match nb with
|
||||
Ast.BASE_ident ident
|
||||
| Ast.BASE_app (ident, _) -> lookup_defn_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 defn_id = lookup_defn_by_name_base nb.id nb.node in
|
||||
iflog cx (fun _ -> log cx "resolved lval #%d to defn #%d"
|
||||
(int_of_node nb.id) (int_of_node defn_id));
|
||||
htab_put cx.ctxt_lval_base_id_to_defn_base_id nb.id defn_id
|
||||
in
|
||||
|
||||
(*
|
||||
* The point here is just to tickle the reference-a-name machinery in
|
||||
* lookup that makes sure that all and only those items referenced get
|
||||
* processed by later stages. An lval that happens to be an item will
|
||||
* mark the item in question here.
|
||||
*)
|
||||
let reference_any_name lv =
|
||||
let rec lval_is_name lv =
|
||||
match lv with
|
||||
Ast.LVAL_base {node = Ast.BASE_ident _; id = _}
|
||||
| Ast.LVAL_base {node = Ast.BASE_app _; id = _} -> true
|
||||
| Ast.LVAL_ext (lv', Ast.COMP_named (Ast.COMP_ident _))
|
||||
| Ast.LVAL_ext (lv', Ast.COMP_named (Ast.COMP_app _))
|
||||
-> lval_is_name lv'
|
||||
| _ -> false
|
||||
in
|
||||
if lval_is_name lv && lval_base_is_item cx lv
|
||||
then ignore (lookup_by_name cx [] (!scopes) (lval_to_name lv))
|
||||
in
|
||||
|
||||
lookup_lval lv;
|
||||
reference_any_name lv;
|
||||
inner.Walk.visit_lval_pre lv
|
||||
in
|
||||
|
||||
let visit_pexp_pre p =
|
||||
begin
|
||||
match p.node with
|
||||
Ast.PEXP_lval pl ->
|
||||
begin
|
||||
match pl with
|
||||
(Ast.PLVAL_base (Ast.BASE_ident ident))
|
||||
| (Ast.PLVAL_base (Ast.BASE_app (ident, _))) ->
|
||||
let id = lookup_defn_by_ident p.id ident in
|
||||
|
||||
iflog cx
|
||||
(fun _ ->
|
||||
log cx "resolved plval %a = #%d to defn #%d"
|
||||
Ast.sprintf_plval pl
|
||||
(int_of_node p.id) (int_of_node id));
|
||||
|
||||
(* Record the pexp -> defn mapping. *)
|
||||
htab_put cx.ctxt_lval_base_id_to_defn_base_id p.id id;
|
||||
|
||||
(* Tickle the referenced-ness table if it's an item. *)
|
||||
if defn_id_is_item cx id
|
||||
then ignore (lookup_by_name cx [] (!scopes)
|
||||
(plval_to_name pl))
|
||||
| _ -> ()
|
||||
end
|
||||
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_pexp_pre p
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_lval_pre = visit_lval_pre;
|
||||
Walk.visit_pexp_pre = visit_pexp_pre
|
||||
};
|
||||
;;
|
||||
|
||||
|
||||
let pattern_resolving_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let not_tag_ctor nm id : unit =
|
||||
err (Some id) "'%s' is not a tag constructor" (string_of_name nm)
|
||||
in
|
||||
|
||||
let resolve_pat_tag
|
||||
(name:Ast.name)
|
||||
(id:node_id)
|
||||
(pats:Ast.pat array)
|
||||
(tag_ctor_id:node_id)
|
||||
: unit =
|
||||
|
||||
(* NB this isn't really the proper tag type, since we aren't applying any
|
||||
* type parameters from the tag constructor in the pattern, but since we
|
||||
* are only looking at the fact that it's a tag-like type at all, and
|
||||
* asking for its arity, it doesn't matter that the possibly parametric
|
||||
* tag type has its parameters unbound here. *)
|
||||
let tag_ty =
|
||||
match Hashtbl.find cx.ctxt_all_item_types tag_ctor_id with
|
||||
Ast.TY_tag t -> Ast.TY_tag t
|
||||
| ft -> fn_output_ty ft
|
||||
in
|
||||
begin
|
||||
match tag_ty with
|
||||
Ast.TY_tag ttag ->
|
||||
let ident =
|
||||
match name with
|
||||
Ast.NAME_ext (_, Ast.COMP_ident id)
|
||||
| Ast.NAME_ext (_, Ast.COMP_app (id, _))
|
||||
| Ast.NAME_base (Ast.BASE_ident id)
|
||||
| Ast.NAME_base (Ast.BASE_app (id, _)) -> id
|
||||
| _ -> err (Some id) "pattern-name ends in non-ident"
|
||||
in
|
||||
let tinfo = Hashtbl.find cx.ctxt_all_tag_info ttag.Ast.tag_id in
|
||||
let (_, _, ttup) = Hashtbl.find tinfo.tag_idents ident in
|
||||
let arity = Array.length ttup in
|
||||
if (Array.length pats) != arity
|
||||
then
|
||||
err (Some id)
|
||||
"tag pattern '%s' with wrong number of components"
|
||||
(string_of_name name)
|
||||
else ()
|
||||
| _ -> not_tag_ctor name id
|
||||
end
|
||||
in
|
||||
|
||||
let resolve_arm { node = arm; id = id } =
|
||||
match fst arm with
|
||||
Ast.PAT_tag (lval, pats) ->
|
||||
let lval_nm = lval_to_name lval in
|
||||
let lval_id = lval_base_id lval in
|
||||
let tag_ctor_id = (lval_item ~node_id:id cx lval).id in
|
||||
if defn_id_is_item cx tag_ctor_id
|
||||
|
||||
(* FIXME (issue #76): we should actually check here that the
|
||||
* function is a tag value-ctor. For now this actually allows
|
||||
* any function returning a tag type to pass as a tag
|
||||
* pattern. *)
|
||||
then resolve_pat_tag lval_nm lval_id pats tag_ctor_id
|
||||
else not_tag_ctor lval_nm lval_id
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let visit_stmt_pre stmt =
|
||||
begin
|
||||
match stmt.node with
|
||||
Ast.STMT_alt_tag { Ast.alt_tag_lval = _;
|
||||
Ast.alt_tag_arms = arms } ->
|
||||
Array.iter resolve_arm arms
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre stmt
|
||||
in
|
||||
{ inner with Walk.visit_stmt_pre = visit_stmt_pre }
|
||||
;;
|
||||
|
||||
let export_referencing_visitor
|
||||
(cx:ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let visit_mod_item_pre id params item =
|
||||
begin
|
||||
match item.node.Ast.decl_item with
|
||||
Ast.MOD_ITEM_mod (view, items) ->
|
||||
let is_defining_mod =
|
||||
(* auto-ref the default-export cases only if
|
||||
* the containing mod is 'defining', meaning
|
||||
* not-native / not-use
|
||||
*)
|
||||
not (Hashtbl.mem cx.ctxt_required_items item.id)
|
||||
in
|
||||
let reference _ item =
|
||||
Hashtbl.replace cx.ctxt_node_referenced item.id ();
|
||||
in
|
||||
let reference_export e _ =
|
||||
match e with
|
||||
Ast.EXPORT_ident ident ->
|
||||
let item = Hashtbl.find items ident in
|
||||
reference ident item
|
||||
| Ast.EXPORT_all_decls ->
|
||||
if is_defining_mod
|
||||
then Hashtbl.iter reference items
|
||||
in
|
||||
Hashtbl.iter reference_export view.Ast.view_exports
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_mod_item_pre id params item
|
||||
in
|
||||
{ inner with Walk.visit_mod_item_pre = visit_mod_item_pre }
|
||||
|
||||
|
||||
;;
|
||||
|
||||
let process_crate
|
||||
(cx:ctxt)
|
||||
(crate:Ast.crate)
|
||||
: unit =
|
||||
let (scopes:(scope list) ref) = ref [] in
|
||||
|
||||
let passes_0 =
|
||||
[|
|
||||
(block_scope_forming_visitor cx Walk.empty_visitor);
|
||||
(stmt_collecting_visitor cx
|
||||
(all_item_collecting_visitor cx
|
||||
Walk.empty_visitor));
|
||||
|]
|
||||
in
|
||||
|
||||
let passes_1 =
|
||||
[|
|
||||
(scope_stack_managing_visitor scopes
|
||||
(type_resolving_visitor cx scopes
|
||||
(lval_base_resolving_visitor cx scopes
|
||||
Walk.empty_visitor)));
|
||||
|]
|
||||
in
|
||||
|
||||
let passes_2 =
|
||||
[|
|
||||
(scope_stack_managing_visitor scopes
|
||||
(pattern_resolving_visitor cx
|
||||
Walk.empty_visitor));
|
||||
export_referencing_visitor cx Walk.empty_visitor
|
||||
|]
|
||||
in
|
||||
let log_flag = cx.ctxt_sess.Session.sess_log_resolve in
|
||||
log cx "running primary resolve passes";
|
||||
run_passes cx "resolve collect" passes_0 log_flag log crate;
|
||||
log cx "running secondary resolve passes";
|
||||
run_passes cx "resolve bind" passes_1 log_flag log crate;
|
||||
log cx "running tertiary resolve passes";
|
||||
run_passes cx "resolve patterns" passes_2 log_flag log crate;
|
||||
|
||||
iflog cx
|
||||
begin
|
||||
fun _ ->
|
||||
Hashtbl.iter
|
||||
begin
|
||||
fun n _ ->
|
||||
if defn_id_is_item cx n
|
||||
then
|
||||
log cx "referenced: %a"
|
||||
Ast.sprintf_name
|
||||
(Hashtbl.find cx.ctxt_all_item_names n)
|
||||
end
|
||||
cx.ctxt_node_referenced;
|
||||
end;
|
||||
(* Post-resolve, we can establish a tag cache. *)
|
||||
cx.ctxt_tag_cache <- Some (Hashtbl.create 0);
|
||||
cx.ctxt_rebuild_cache <- Some (Hashtbl.create 0)
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,109 +0,0 @@
|
|||
open Common;;
|
||||
open Semant;;
|
||||
|
||||
let log cx =
|
||||
Session.log
|
||||
"simplify"
|
||||
(should_log cx cx.Semant.ctxt_sess.Session.sess_log_simplify)
|
||||
cx.Semant.ctxt_sess.Session.sess_log_out
|
||||
|
||||
let iflog cx thunk =
|
||||
if (should_log cx cx.Semant.ctxt_sess.Session.sess_log_simplify)
|
||||
then thunk ()
|
||||
else ()
|
||||
;;
|
||||
|
||||
|
||||
let plval_const_marking_visitor
|
||||
(cx:Semant.ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
let visit_pexp_pre pexp =
|
||||
begin
|
||||
match pexp.node with
|
||||
Ast.PEXP_lval pl ->
|
||||
begin
|
||||
let id = lval_base_id_to_defn_base_id cx pexp.id in
|
||||
let is_const =
|
||||
if defn_id_is_item cx id
|
||||
then match (get_item cx id).Ast.decl_item with
|
||||
Ast.MOD_ITEM_const _ -> true
|
||||
| _ -> false
|
||||
else false
|
||||
in
|
||||
iflog cx (fun _ -> log cx "plval %a refers to %s"
|
||||
Ast.sprintf_plval pl
|
||||
(if is_const then "const item" else "non-const"));
|
||||
htab_put cx.ctxt_plval_const pexp.id is_const
|
||||
end
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_pexp_pre pexp
|
||||
in
|
||||
|
||||
let visit_pexp_post p =
|
||||
inner.Walk.visit_pexp_post p;
|
||||
iflog cx (fun _ -> log cx "pexp %a is %s"
|
||||
Ast.sprintf_pexp p
|
||||
(if pexp_is_const cx p
|
||||
then "constant"
|
||||
else "non-constant"))
|
||||
in
|
||||
|
||||
{ inner with
|
||||
Walk.visit_pexp_pre = visit_pexp_pre;
|
||||
Walk.visit_pexp_post = visit_pexp_post;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
let pexp_simplifying_visitor
|
||||
(_:Semant.ctxt)
|
||||
(inner:Walk.visitor)
|
||||
: Walk.visitor =
|
||||
|
||||
let walk_atom at =
|
||||
match at with
|
||||
Ast.ATOM_pexp _ ->
|
||||
begin
|
||||
(* FIXME: move desugaring code from frontend to here. *)
|
||||
()
|
||||
end
|
||||
| _ -> ()
|
||||
in
|
||||
|
||||
let visit_stmt_pre s =
|
||||
begin
|
||||
match s.node with
|
||||
Ast.STMT_copy (_, Ast.EXPR_atom a) -> walk_atom a
|
||||
| _ -> ()
|
||||
end;
|
||||
inner.Walk.visit_stmt_pre s;
|
||||
in
|
||||
{ inner with
|
||||
Walk.visit_stmt_pre = visit_stmt_pre;
|
||||
}
|
||||
;;
|
||||
|
||||
|
||||
let process_crate (cx:Semant.ctxt) (crate:Ast.crate) : unit =
|
||||
|
||||
let passes =
|
||||
[|
|
||||
(plval_const_marking_visitor cx Walk.empty_visitor);
|
||||
(pexp_simplifying_visitor cx Walk.empty_visitor)
|
||||
|]
|
||||
in
|
||||
let log_flag = cx.Semant.ctxt_sess.Session.sess_log_simplify in
|
||||
Semant.run_passes cx "simplify" passes log_flag log crate
|
||||
;;
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
||||
|
6367
src/boot/me/trans.ml
6367
src/boot/me/trans.ml
File diff suppressed because it is too large
Load Diff
|
@ -1,251 +0,0 @@
|
|||
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 box 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 box 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 deref_ctrl =
|
||||
DEREF_one_box
|
||||
| DEREF_all_boxes
|
||||
| DEREF_none
|
||||
;;
|
||||
|
||||
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 rec ty_mem_ctrl (cx:ctxt) (ty:Ast.ty) : mem_ctrl =
|
||||
match ty with
|
||||
Ast.TY_port _
|
||||
| Ast.TY_chan _
|
||||
| Ast.TY_task
|
||||
| Ast.TY_str -> MEM_rc_opaque
|
||||
| Ast.TY_vec _ ->
|
||||
if type_has_state cx ty
|
||||
then MEM_gc
|
||||
else MEM_rc_opaque
|
||||
| Ast.TY_box t ->
|
||||
if type_has_state cx t
|
||||
then MEM_gc
|
||||
else
|
||||
if type_is_structured cx t
|
||||
then MEM_rc_struct
|
||||
else MEM_rc_opaque
|
||||
| Ast.TY_mutable t
|
||||
| Ast.TY_constrained (t, _) ->
|
||||
ty_mem_ctrl cx t
|
||||
| _ ->
|
||||
MEM_interior
|
||||
;;
|
||||
|
||||
let slot_mem_ctrl (cx:ctxt) (slot:Ast.slot) : mem_ctrl =
|
||||
match slot.Ast.slot_mode with
|
||||
Ast.MODE_alias -> MEM_interior
|
||||
| Ast.MODE_local ->
|
||||
ty_mem_ctrl cx (slot_ty slot)
|
||||
;;
|
||||
|
||||
|
||||
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 = get_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 = get_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_parts
|
||||
(get_element_ptr:'a -> int -> 'a)
|
||||
(dst_ptr:'a)
|
||||
(src_ptr:'a)
|
||||
(tys:Ast.ty_tup)
|
||||
(f:'a -> 'a -> Ast.ty -> unit)
|
||||
: unit =
|
||||
Array.iteri
|
||||
begin
|
||||
fun i ty ->
|
||||
f (get_element_ptr dst_ptr i)
|
||||
(get_element_ptr src_ptr i)
|
||||
ty
|
||||
end
|
||||
tys
|
||||
;;
|
||||
|
||||
let iter_rec_parts
|
||||
(get_element_ptr:'a -> int -> 'a)
|
||||
(dst_ptr:'a)
|
||||
(src_ptr:'a)
|
||||
(entries:Ast.ty_rec)
|
||||
(f:'a -> 'a -> Ast.ty -> unit)
|
||||
: unit =
|
||||
iter_tup_parts get_element_ptr dst_ptr src_ptr
|
||||
(Array.map snd entries) f
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
1453
src/boot/me/type.ml
1453
src/boot/me/type.ml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,729 +0,0 @@
|
|||
|
||||
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_pexp_pre: Ast.pexp -> unit;
|
||||
visit_pexp_post: Ast.pexp -> 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_plval_pre: Ast.plval -> unit;
|
||||
visit_plval_post: Ast.plval -> 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_pexp_pre = (fun _ -> ());
|
||||
visit_pexp_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_plval_pre = (fun _ -> ());
|
||||
visit_plval_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 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_const (ty, e) ->
|
||||
walk_ty v ty;
|
||||
walk_option (walk_expr v) e
|
||||
| Ast.MOD_ITEM_fn f -> walk_fn v f item.id
|
||||
| Ast.MOD_ITEM_tag (hdr, _, _) ->
|
||||
walk_header_slots v hdr
|
||||
| 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_ty v) ttup
|
||||
|
||||
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_ty v s
|
||||
| Ast.TY_rec trec -> Array.iter (fun (_, s) -> walk_ty v s) trec
|
||||
| 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_tag _ -> ()
|
||||
| Ast.TY_native _ -> ()
|
||||
| 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 -> ()
|
||||
| Ast.TY_box m -> walk_ty v m
|
||||
| Ast.TY_mutable m -> walk_ty v m
|
||||
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 lv = s.Ast.for_seq in
|
||||
walk_slot_identified v si;
|
||||
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 | Ast.STMT_log_err a ->
|
||||
walk_atom v a
|
||||
|
||||
| Ast.STMT_new_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_new_vec (lv, _, atoms) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (walk_atom v) atoms
|
||||
|
||||
| Ast.STMT_new_tup (lv, mut_atoms) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (fun (_, atom) -> walk_atom v atom) mut_atoms
|
||||
|
||||
| Ast.STMT_new_str (lv, _) ->
|
||||
walk_lval v lv
|
||||
|
||||
| Ast.STMT_new_port lv ->
|
||||
walk_lval v lv
|
||||
|
||||
| Ast.STMT_new_chan (chan,port) ->
|
||||
walk_option (walk_lval v) port;
|
||||
walk_lval v chan;
|
||||
|
||||
| Ast.STMT_new_box (dst, _, src) ->
|
||||
walk_lval v dst;
|
||||
walk_atom v src
|
||||
|
||||
| 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 (issue #86): 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_break | Ast.STMT_cont | 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); id=_ } =
|
||||
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 _ ->
|
||||
unimpl (Some s.id) "statement type in Walk.walk_stmt"
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_stmt_pre
|
||||
children
|
||||
v.visit_stmt_post
|
||||
s
|
||||
|
||||
and walk_unop
|
||||
(v:visitor)
|
||||
(unop:Ast.unop)
|
||||
: unit =
|
||||
match unop with
|
||||
Ast.UNOP_cast tyi ->
|
||||
walk_ty v tyi.node
|
||||
| _ -> ()
|
||||
|
||||
|
||||
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 (unop,a) ->
|
||||
walk_atom v a;
|
||||
walk_unop v unop
|
||||
| Ast.EXPR_atom a ->
|
||||
walk_atom v a
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_expr_pre
|
||||
children
|
||||
v.visit_expr_post
|
||||
e
|
||||
|
||||
and walk_pexp
|
||||
(v:visitor)
|
||||
(p:Ast.pexp)
|
||||
: unit =
|
||||
let children _ =
|
||||
match p.node with
|
||||
Ast.PEXP_call (pexp, pexps) ->
|
||||
walk_pexp v pexp;
|
||||
Array.iter (walk_pexp v) pexps
|
||||
|
||||
| Ast.PEXP_spawn (_, _, pexp)
|
||||
| Ast.PEXP_box (_, pexp) ->
|
||||
walk_pexp v pexp;
|
||||
|
||||
| Ast.PEXP_unop (unop, pexp) ->
|
||||
walk_pexp v pexp;
|
||||
walk_unop v unop
|
||||
|
||||
| Ast.PEXP_bind (pexp, pexp_opts) ->
|
||||
walk_pexp v pexp;
|
||||
Array.iter (walk_option (walk_pexp v)) pexp_opts
|
||||
|
||||
| Ast.PEXP_rec (elts, base) ->
|
||||
let walk_elt (_, _, pexp) = walk_pexp v pexp in
|
||||
Array.iter walk_elt elts;
|
||||
walk_option (walk_pexp v) base
|
||||
|
||||
| Ast.PEXP_tup elts ->
|
||||
let walk_elt (_, pexp) = walk_pexp v pexp in
|
||||
Array.iter walk_elt elts
|
||||
|
||||
| Ast.PEXP_vec (_, pexps)
|
||||
| Ast.PEXP_custom (_, pexps, _) ->
|
||||
Array.iter (walk_pexp v) pexps
|
||||
|
||||
| Ast.PEXP_chan po ->
|
||||
walk_option (walk_pexp v) po
|
||||
|
||||
| Ast.PEXP_binop (_, a, b)
|
||||
| Ast.PEXP_lazy_and (a, b)
|
||||
| Ast.PEXP_lazy_or (a, b) ->
|
||||
walk_pexp v a;
|
||||
walk_pexp v b
|
||||
|
||||
| Ast.PEXP_lval pl -> walk_plval v pl
|
||||
|
||||
| Ast.PEXP_lit lit -> walk_lit v lit
|
||||
|
||||
| Ast.PEXP_port
|
||||
| Ast.PEXP_str _ -> ()
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_pexp_pre
|
||||
children
|
||||
v.visit_pexp_post
|
||||
p
|
||||
|
||||
and walk_plval
|
||||
(v:visitor)
|
||||
(p:Ast.plval)
|
||||
: unit =
|
||||
let children _ =
|
||||
match p with
|
||||
| Ast.PLVAL_base (Ast.BASE_app (_, tys)) ->
|
||||
Array.iter (walk_ty v) tys
|
||||
| Ast.PLVAL_base _ -> ()
|
||||
| Ast.PLVAL_ext_name (pexp, _) ->
|
||||
walk_pexp v pexp
|
||||
| Ast.PLVAL_ext_pexp (a, b) ->
|
||||
walk_pexp v a;
|
||||
walk_pexp v b;
|
||||
| Ast.PLVAL_ext_deref pexp ->
|
||||
walk_pexp v pexp
|
||||
in
|
||||
walk_bracketed
|
||||
v.visit_plval_pre
|
||||
children
|
||||
v.visit_plval_post
|
||||
p
|
||||
|
||||
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
|
||||
| Ast.ATOM_pexp p -> walk_pexp v p
|
||||
|
||||
|
||||
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 walk p =
|
||||
match p with
|
||||
Ast.PAT_lit lit -> walk_lit v lit
|
||||
| Ast.PAT_tag (lv, pats) ->
|
||||
walk_lval v lv;
|
||||
Array.iter (walk_pat v) 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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,116 +0,0 @@
|
|||
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
|
||||
;;
|
||||
|
||||
(* dst = dst - src *)
|
||||
let difference (dst:t) (src:t) : bool =
|
||||
invert src;
|
||||
let b = intersect dst src in
|
||||
invert src;
|
||||
b
|
||||
;;
|
||||
|
||||
|
||||
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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,823 +0,0 @@
|
|||
(*
|
||||
* This module goes near the *bottom* of the dependency DAG, and holds basic
|
||||
* types shared across all phases of the compiler.
|
||||
*)
|
||||
|
||||
type ('a, 'b) either = Left of 'a | Right of 'b
|
||||
|
||||
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
|
||||
type crate_id = Crate 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
|
||||
let int_of_common (Crate i) = i
|
||||
|
||||
type 'a identified = { node: 'a; id: node_id }
|
||||
;;
|
||||
|
||||
let bug _ =
|
||||
let k s = failwith s
|
||||
in Printf.ksprintf k
|
||||
;;
|
||||
|
||||
(* TODO: On some joyous day, remove me. *)
|
||||
exception Not_implemented of ((node_id option) * string)
|
||||
;;
|
||||
|
||||
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
|
||||
;;
|
||||
|
||||
let unimpl (idopt:node_id option) =
|
||||
let k s =
|
||||
raise (Not_implemented (idopt, "unimplemented " ^ s))
|
||||
in
|
||||
Printf.ksprintf k
|
||||
;;
|
||||
|
||||
(* Some ubiquitous low-level types. *)
|
||||
|
||||
type target =
|
||||
Linux_x86_elf
|
||||
| Win32_x86_pe
|
||||
| MacOS_x86_macho
|
||||
| FreeBSD_x86_elf
|
||||
;;
|
||||
|
||||
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 string functions.
|
||||
*)
|
||||
|
||||
let split_string (c:char) (s:string) : string list =
|
||||
let ls = ref [] in
|
||||
let b = Buffer.create (String.length s) in
|
||||
let flush _ =
|
||||
if Buffer.length b <> 0
|
||||
then
|
||||
begin
|
||||
ls := (Buffer.contents b) :: (!ls);
|
||||
Buffer.clear b
|
||||
end
|
||||
in
|
||||
let f ch =
|
||||
if c = ch
|
||||
then flush()
|
||||
else Buffer.add_char b ch
|
||||
in
|
||||
String.iter f s;
|
||||
flush();
|
||||
List.rev (!ls)
|
||||
;;
|
||||
|
||||
(*
|
||||
* 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 sorted_htab_iter
|
||||
(f:'a -> 'b -> unit)
|
||||
(tab:('a, 'b) Hashtbl.t)
|
||||
: unit =
|
||||
Array.iter
|
||||
(fun k -> f k (Hashtbl.find tab k))
|
||||
(sorted_htab_keys tab)
|
||||
;;
|
||||
|
||||
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
|
||||
;;
|
||||
|
||||
(* This is completely ridiculous, but it turns out that ocaml hashtables are
|
||||
* order-of-element-addition sensitive when it comes to the built-in
|
||||
* polymorphic comparison operator. So you have to canonicalize them after
|
||||
* you've stopped adding things to them if you ever want to use them in a
|
||||
* term that requires structural comparison to work. Sigh.
|
||||
*)
|
||||
|
||||
let htab_canonicalize (htab:('a,'b) Hashtbl.t) : ('a,'b) Hashtbl.t =
|
||||
let n = Hashtbl.create (Hashtbl.length htab) in
|
||||
Array.iter
|
||||
(fun k -> Hashtbl.add n k (Hashtbl.find htab k))
|
||||
(sorted_htab_keys htab);
|
||||
n
|
||||
;;
|
||||
|
||||
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;
|
||||
htab_canonicalize (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)
|
||||
;;
|
||||
|
||||
let rec list_count elem lst =
|
||||
match lst with
|
||||
[] -> 0
|
||||
| h::t when h = elem -> 1 + (list_count elem t)
|
||||
| _::t -> list_count elem t
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Auxiliary pair functions.
|
||||
*)
|
||||
let pair_rev (x,y) = (y,x)
|
||||
|
||||
(*
|
||||
* Auxiliary option functions.
|
||||
*)
|
||||
|
||||
let bool_of_option x =
|
||||
match x with
|
||||
Some _ -> true
|
||||
| None -> false
|
||||
|
||||
let may f x =
|
||||
match x with
|
||||
Some x' -> f x'
|
||||
| None -> ()
|
||||
|
||||
let option_map f x =
|
||||
match x with
|
||||
Some x' -> Some (f x')
|
||||
| None -> None
|
||||
|
||||
let option_get x =
|
||||
match x with
|
||||
Some x -> x
|
||||
| None -> raise Not_found
|
||||
|
||||
(*
|
||||
* Auxiliary either functions.
|
||||
*)
|
||||
let either_has_left x =
|
||||
match x with
|
||||
Left _ -> true
|
||||
| Right _ -> false
|
||||
|
||||
let either_has_right x = not (either_has_left x)
|
||||
|
||||
let either_get_left x =
|
||||
match x with
|
||||
Left x -> x
|
||||
| Right _ -> raise Not_found
|
||||
|
||||
let either_get_right x =
|
||||
match x with
|
||||
Right x -> x
|
||||
| Left _ -> raise Not_found
|
||||
(*
|
||||
* 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_iter2 (f:'a -> 'b -> unit) (a:'a array) (b:'b array) : unit =
|
||||
assert ((Array.length a) = (Array.length b));
|
||||
Array.iteri (fun i a_elem -> f a_elem b.(i)) a
|
||||
;;
|
||||
|
||||
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 -> a
|
||||
| (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 $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,86 +0,0 @@
|
|||
(*
|
||||
* Common formatting helpers.
|
||||
*)
|
||||
|
||||
let fmt = Format.fprintf
|
||||
;;
|
||||
|
||||
let fmt_str ff = fmt ff "%s"
|
||||
;;
|
||||
|
||||
let fmt_obox ff = Format.pp_open_box ff 4;;
|
||||
let fmt_obox_n ff n = Format.pp_open_box ff n;;
|
||||
let fmt_cbox ff = Format.pp_close_box ff ();;
|
||||
let fmt_obr ff = fmt ff "{";;
|
||||
let fmt_cbr ff = fmt ff "@\n}";;
|
||||
let fmt_cbb ff = (fmt_cbox ff; fmt_cbr ff);;
|
||||
let fmt_break ff = Format.pp_print_space ff ();;
|
||||
|
||||
let fmt_bracketed
|
||||
(bra:string)
|
||||
(ket:string)
|
||||
(inner:Format.formatter -> 'a -> unit)
|
||||
(ff:Format.formatter)
|
||||
(a:'a)
|
||||
: unit =
|
||||
fmt_str ff bra;
|
||||
fmt_obox_n ff 0;
|
||||
inner ff a;
|
||||
fmt_cbox ff;
|
||||
fmt_str ff ket
|
||||
;;
|
||||
|
||||
let fmt_arr_sep
|
||||
(sep:string)
|
||||
(inner:Format.formatter -> 'a -> unit)
|
||||
(ff:Format.formatter)
|
||||
(az:'a array)
|
||||
: unit =
|
||||
Array.iteri
|
||||
begin
|
||||
fun i a ->
|
||||
if i <> 0
|
||||
then (fmt_str ff sep; fmt_break ff);
|
||||
inner ff a
|
||||
end
|
||||
az
|
||||
;;
|
||||
|
||||
let fmt_bracketed_arr_sep
|
||||
(bra:string)
|
||||
(ket:string)
|
||||
(sep:string)
|
||||
(inner:Format.formatter -> 'a -> unit)
|
||||
(ff:Format.formatter)
|
||||
(az:'a array)
|
||||
: unit =
|
||||
fmt_bracketed bra ket
|
||||
(fmt_arr_sep sep inner)
|
||||
ff az
|
||||
;;
|
||||
|
||||
let fmt_to_str (f:Format.formatter -> 'a -> unit) (v:'a) : string =
|
||||
let buf = Buffer.create 16 in
|
||||
let bf = Format.formatter_of_buffer buf in
|
||||
begin
|
||||
f bf v;
|
||||
Format.pp_print_flush bf ();
|
||||
Buffer.contents buf
|
||||
end
|
||||
;;
|
||||
|
||||
let sprintf_fmt
|
||||
(f:Format.formatter -> 'a -> unit)
|
||||
: (unit -> 'a -> string) =
|
||||
(fun _ -> fmt_to_str f)
|
||||
;;
|
||||
|
||||
|
||||
(*
|
||||
* Local Variables:
|
||||
* fill-column: 78;
|
||||
* indent-tabs-mode: nil
|
||||
* buffer-file-coding-system: utf-8-unix
|
||||
* compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
* End:
|
||||
*)
|
|
@ -1,16 +1,6 @@
|
|||
An informal guide to reading and working on the rustc compiler.
|
||||
==================================================================
|
||||
|
||||
First off, know that our current state of development is "bootstrapping";
|
||||
this means we've got two compilers on the go and one of them is being used
|
||||
to develop the other. Rustboot is written in ocaml and rustc in rust. The
|
||||
one you *probably* ought to be working on at present is rustc. Rustboot is
|
||||
more for historical comparison and bug-fixing whenever necessary to un-block
|
||||
development of rustc.
|
||||
|
||||
There's a document similar to this next door, then, in boot/README. The boot
|
||||
directory is where we do work on rustboot.
|
||||
|
||||
If you wish to expand on this document, or have one of the
|
||||
slightly-more-familiar authors add anything else to it, please get in touch or
|
||||
file a bug. Your concerns are probably the same as someone else's.
|
||||
|
@ -85,34 +75,3 @@ Control and information flow within the compiler:
|
|||
type-directed translation to LLVM-ese. When it's finished synthesizing LLVM
|
||||
values, rustc asks LLVM to write them out as a bitcode file, on which you
|
||||
can run the normal LLVM pipeline (opt, llc, as) to get an executable.
|
||||
|
||||
|
||||
Comparison with rustboot
|
||||
========================
|
||||
|
||||
Rustc is written in a more "functional" style than rustboot; each rustc pass
|
||||
tends to depend only on the AST it's given as input, which it does not mutate.
|
||||
Calculations flow from one phase to another by repeatedly rebuilding the AST
|
||||
with additional annotations.
|
||||
|
||||
Rustboot normalizes to a statement-centric AST. Rustc uses an
|
||||
expression-centric AST.
|
||||
|
||||
Rustboot generates 3-address IL into imperative buffers of coded IL quads.
|
||||
Rustc generates LLVM, an SSA-based expression IL.
|
||||
|
||||
Rustc, being attached to LLVM, generates much better code. Factor of 5
|
||||
smaller, usually. Sometimes much more.
|
||||
|
||||
Rustc preserves more of the parsed input structure. Rustboot "desugars" most
|
||||
of the input, rendering round-trip pretty-printing impossible. Error reporting
|
||||
is also better in rustc, as type names (as denoted by the user) are preserved
|
||||
throughout typechecking.
|
||||
|
||||
Rustc is not concerned with the PIC-ness of the resulting code, nor anything
|
||||
to do with encoding DWARF or x86 instructions. All this superfluous
|
||||
machine-level logic that seeped up to the translation layer in rustboot is
|
||||
pushed past LLVM into later stages of the toolchain in rustc.
|
||||
|
||||
Numerous "bad idea" idiosyncracies of the rustboot AST have been eliminated in
|
||||
rustc. In general the code is much more obvious, minimal and straightforward.
|
||||
|
|
|
@ -984,9 +984,7 @@ fn parse_bottom_expr(parser p) -> @ast::expr {
|
|||
* FIXME: This is a crude approximation of the syntax-extension system,
|
||||
* for purposes of prototyping and/or hard-wiring any extensions we
|
||||
* wish to use while bootstrapping. The eventual aim is to permit
|
||||
* loading rust crates to process extensions, but this will likely
|
||||
* require a rust-based frontend, or an ocaml-FFI-based connection to
|
||||
* rust crates. At the moment we have neither.
|
||||
* loading rust crates to process extensions.
|
||||
*/
|
||||
|
||||
fn expand_syntax_ext(parser p, ast::span sp,
|
||||
|
|
Loading…
Reference in New Issue