├── .gitignore
├── ChangeLog.md
├── LICENSE
├── LICENSE.language-rust
├── README.md
├── app
├── floorplan.hs
└── semantics.hs
├── bin
└── make-pkg
├── build
├── build-genrs
├── build-immix
├── clean-gen
├── docs
├── .gitignore
├── 404.html
├── Gemfile
├── _config.yml
├── _posts
│ └── 2020-01-26-docs-site-goes-live.markdown
├── about.markdown
├── floorplan-info.svg
├── index.markdown
└── unsupported-operations.markdown
├── examples
├── app.flp
├── arith.flp
├── arith_id.flp
├── arith_power.flp
├── bits.flp
├── bump.flp
├── dyn_choice.flp
├── dyn_seq.flp
├── dynamic_prim.flp
├── empty.flp
├── enum.flp
├── enum_bad0.flp
├── grafting_loop.flp
├── hotspot.flp
├── immix
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── build.rs
│ ├── c_src
│ │ ├── common.h
│ │ ├── freelist.c
│ │ ├── freelist.h
│ │ ├── gc.c
│ │ ├── gc.h
│ │ ├── gcbench.c
│ │ ├── immix.h
│ │ ├── immix_mutator.c
│ │ ├── immix_space.c
│ │ ├── main.c
│ │ ├── objectmodel.c
│ │ └── objectmodel.h
│ ├── dot
│ │ ├── .gitignore
│ │ └── call-graph.dot
│ ├── features
│ ├── features.all
│ ├── run-mt-trace.py.example
│ ├── run.py.example
│ ├── runme
│ ├── runone
│ ├── rust_c_interface
│ │ ├── immix_rust.h
│ │ └── test.c
│ ├── src
│ │ ├── exhaust.rs
│ │ ├── gcbench.rs
│ │ ├── heap
│ │ │ ├── .gitignore
│ │ │ ├── debug.rs
│ │ │ ├── freelist
│ │ │ │ └── mod.rs
│ │ │ ├── gc
│ │ │ │ ├── clib_x64.c
│ │ │ │ └── mod.rs
│ │ │ ├── immix
│ │ │ │ ├── immix_mutator.rs
│ │ │ │ ├── immix_space.rs
│ │ │ │ └── mod.rs
│ │ │ ├── layout.flp
│ │ │ └── mod.rs
│ │ ├── leakme.rs
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ ├── mark.rs
│ │ ├── mt_gcbench.rs
│ │ ├── mt_trace.rs
│ │ ├── obj_init.rs
│ │ ├── objectmodel
│ │ │ └── mod.rs
│ │ ├── testme.rs
│ │ └── trace.rs
│ └── test_mac.sh
├── layer.flp
├── map_bits.flp
├── named_ref.flp
├── nested.flp
├── nested_union.flp
├── parens.flp
├── seq.flp
├── union.flp
└── uniq_fail.flp
├── flp-compiler
├── .gitignore
├── Cargo.lock
├── Cargo.toml
└── src
│ └── lib.rs
├── flp-framework
├── .gitignore
├── Cargo.toml
└── src
│ ├── address.rs
│ └── lib.rs
├── genrs
├── .gitignore
├── Cargo.toml
├── build.rs
└── src
│ ├── layout.flp
│ └── main.rs
├── package.yaml
├── proofs
├── .gitignore
├── Binary.v
├── Calculus.v
├── Common.v
├── ListSet.v
├── Map.v
├── README.md
├── Semantics.v
└── Set.v
├── pygmentize
├── .gitignore
├── Makefile
├── README.md
└── floorplan.py
├── src
└── Language
│ ├── Floorplan.hs
│ ├── Floorplan
│ ├── C
│ │ └── Compiler.hs
│ ├── Core
│ │ ├── Compiler.hs
│ │ └── Syntax.hs
│ ├── Parser.y
│ ├── Preproc
│ │ ├── Passes.hs
│ │ └── Types.hs
│ ├── Rust.hs
│ ├── Rust
│ │ ├── Common.hs
│ │ ├── Compiler.hs
│ │ ├── Mapping.hs
│ │ └── Types.hs
│ ├── Semantics.hs
│ ├── Syntax.hs
│ └── Token.x
│ └── Rust
│ ├── Data
│ ├── Ident.hs
│ ├── InputStream.hs
│ └── Position.hs
│ ├── Parser.hs
│ ├── Parser
│ ├── Internal.y
│ ├── Lexer.x
│ ├── Literals.hs
│ ├── NonEmpty.hs
│ ├── ParseMonad.hs
│ └── Reversed.hs
│ ├── Pretty.hs
│ ├── Pretty
│ ├── Internal.hs
│ ├── Literals.hs
│ ├── Resolve.hs
│ └── Util.hs
│ ├── Quote.hs
│ ├── Syntax.hs
│ └── Syntax
│ ├── AST.hs
│ ├── Token.hs
│ └── Token.hs-boot
├── stack.yaml
└── test
└── parser
└── Main.hs
/.gitignore:
--------------------------------------------------------------------------------
1 | /*.c
2 | /dist
3 | Cargo.lock
4 | /*.lock
5 | .stack-work/
6 | flp.cabal
7 | *~
8 |
--------------------------------------------------------------------------------
/ChangeLog.md:
--------------------------------------------------------------------------------
1 | # Changelog for flp
2 |
3 | ## Unreleased changes
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The code in floorplan is released under the license below.
2 |
3 | The MIT License (MIT)
4 |
5 | Copyright (c) 2019,2020 Karl Cronburg. All rights reserved.
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of
8 | this software and associated documentation files (the "Software"), to deal in
9 | the Software without restriction, including without limitation the rights to
10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 | of the Software, and to permit persons to whom the Software is furnished to do
12 | so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/LICENSE.language-rust:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Alec Theriault
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Alec Theriault nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Floorplan compiler
2 |
3 | A language for specifying the layout of a one-dimensional address space, particularly
4 | for garbage collectors and manual memory managers written in Rust and C/C++.
5 |
6 | ## Building and running
7 |
8 | Floorplan is written in Haskell and must be [built with either
9 | stack](https://docs.haskellstack.org/en/stable/README/) or
10 | [cabal](haskell.org/cabal). If you just want to build and install Floorplan
11 | globally on your system you can do the following:
12 |
13 | ```bash
14 | $ cabal install flp
15 | ```
16 |
17 | This will pull the latest stable release of Floorplan from
18 | [Hackage](hackage.haskell.org/package/flp) and install the `flp` executable
19 | globally. If instead you wish to make modifications to the compiler or to
20 | reference it directly from another Haskell project, you can pull the
21 | master branch of this repo and build the project like follows:
22 |
23 | ```bash
24 | $ git clone --branch master https://github.com/RedlineResearch/floorplan.git
25 | $ cd floorplan && stack build
26 | ...
27 | Completed 2 action(s).
28 | ```
29 |
30 | At which point you can compile, for example, the file `examples/immix/layout.flp` with
31 | the `build-immix` script:
32 |
33 | ```bash
34 | $ ./build-immix
35 | ...
36 | Compiling immix_rust v0.0.1 (/home/karl/w/flp/examples/immix)
37 | Finished dev [unoptimized + debuginfo] target(s) in 4.56s
38 | ```
39 |
40 | This script ensures the Floorplan compiler is built, installs it for your current
41 | user, and then builds the Immix project which itself invokes the Floorplan compiler
42 | to build the file `examples/immix/src/heap/layout.flp`.
43 |
44 | In order to run the compiler against some other `.flp` file, the compiler can
45 | be run directly as follows:
46 |
47 | ```bash
48 | stack exec flp [path/to/layout.flp] [path/to/generated.rs]
49 | ```
50 |
51 | ### Rust
52 |
53 | Note that to build a Rust file generated in this manner,
54 | you must include the `flp-framework` to your Cargo dependencies, and `flp-compiler`
55 | to your cargo build-dependencies. The latter is simply a wrapper for calling out
56 | to the (already stack-installed) flp compiler, and the framework crate contains
57 | necessary macros and address types that generated Rust code uses.
58 |
59 | The skeleton of a Rust cargo project is given in the `genrs/` directory of this
60 | repo, which can be copied over and modified to support the needs of a memory
61 | manager other than immix-rust.
62 |
63 | ### C/C++
64 |
65 | C/C++ output is under active development. Regardless of which output type is chosen
66 | (`.c`, `.h`, or `.hpp`), the compiler will output the same C-like code. This is
67 | expected to work with a recent version of the Clang compiler, but will likely work
68 | just as fine under a modern verison of GCC.
69 |
70 | When reporting an issue with the C output mode of the Floorplan compiler, please
71 | check first or have a reasonable belief that the same issue happens with Clang.
72 | Modifications to the compiler which tailor the output to different C compilers are
73 | more than welcome, but we intend to primarily support Clang as a backend toolchain.
74 |
75 | ## Dependencies
76 |
77 | A customized version of the [language-rust](https://github.com/harpocrates/language-rust)
78 | package is included in the `deps/` directory of this repo, which adds support for
79 | directly splicing of host language expressions into quasiquoted Rust code. This is the
80 | mechanism by which Floorplan generates Rust code.
81 |
82 | ## Testing and contributing
83 |
84 | If you want to help maintain or contribute new code to this project, feel free to
85 | make a pull request or better yet start an issue in the the Issues tracker so that
86 | you can get our feedback along the way. A number of avenues for work on the compiler
87 | exist, including but not limited to:
88 |
89 | - More example Rust allocators implemented with a Floorplan layout.
90 | - Rust templates for allocating on alignment boundaries.
91 | - Extensively document the interfaces generated.
92 | - Better error messages.
93 | - Integrating the core semantics (`app/semantics.hs`) directly into the project
94 | `src/` hierarchy.
95 | - Calling out to a SMT library to verify alignment and size constraints.
96 | - Targeting both C and Rust.
97 | - Support for non-64-bit architectures.
98 | - Generating debugging assertions.
99 | - Dynamic tracking of type information for each piece of the heap.
100 | - Cleaning up the dependencies by integrating the Rust splicing support directly
101 | into [the upstream repository](https://github.com/harpocrates/language-rust),
102 | e.g. as a separate quasiquoter.
103 | - Generate cargo-based Rust documentation alongside generated functions, indicating
104 | why a certain function was generated and how it might be used.
105 | - Repairing the Coq proofs in the `proofs/*.v` files.
106 | - Better Rust integration and downstream crates.
107 |
108 |
--------------------------------------------------------------------------------
/app/floorplan.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
2 | module Main where
3 | import Prelude hiding (putStrLn)
4 | import qualified Prelude as P
5 | import System.Environment
6 | import System.IO hiding (putStr, putStrLn, hGetLine)
7 | import System.Exit (exitSuccess, exitFailure)
8 | import Control.Exception (assert)
9 | import Data.List (isSuffixOf)
10 | import Data.Either (partitionEithers)
11 | import Text.RE.TDFA.String (RE(..), compileRegex)
12 |
13 | import Language.Rust.Syntax (SourceFile(..))
14 | import Language.Rust.Data.Position (Span(..))
15 | import Language.Rust.Pretty (pretty')
16 |
17 | import Language.Floorplan
18 | import qualified Language.Floorplan.Rust.Compiler as RC
19 |
20 | --import qualified Language.Floorplan.Parser as P
21 | import qualified Language.Floorplan.Parser as P
22 | import qualified Language.Floorplan.Token as T
23 | import qualified Language.Floorplan.Syntax as S
24 | import qualified Language.Floorplan.Core.Compiler as CC
25 | import qualified Language.Floorplan.Core.Syntax as CS
26 |
27 | import qualified Language.Floorplan.C.Compiler as CComp
28 | import qualified Language.Floorplan.Preproc.Passes as Preproc
29 |
30 | usage = do
31 | pName <- getProgName
32 | P.putStrLn $ pName ++ " [file.flp] [path/to/dest.{rs,c}]"
33 | exitFailure
34 |
35 | data CompilerOutput = RustOutput | COutput | Unknown
36 |
37 | validateAnalysis [] = return ()
38 | validateAnalysis xs = P.putStrLn (show xs) >> exitFailure
39 |
40 | -- | Returns a tuple (for now just the [RE]) of the results of analyses with computed results.
41 | doAnalyses :: [Decl] -> IO ([RE], String, String)
42 | doAnalyses result = do
43 | (errors, regexes) <- partitionEithers <$> Preproc.regexAnalysis result
44 | validateAnalysis errors
45 | validateAnalysis $ Preproc.balancedScopeAnalysis result
46 | validateAnalysis $ Preproc.graftingAnalysis result
47 | let (err, header, footer) = Preproc.headFootAnalysis result
48 | validateAnalysis err
49 | return (regexes, header, footer)
50 |
51 | --print $ pretty' sf
52 |
53 | doResult :: CompilerOutput -> FilePath -> [Decl] -> IO ()
54 | doResult Unknown outputFile _ = P.putStrLn ("Error: File type unknown '" ++ outputFile ++ "'") >> usage
55 | doResult out_type outputFile result = do
56 | (filterOutRegexes, header, footer) <- doAnalyses result
57 | P.putStrLn ("No grafting errors. Proceeding to graft.")
58 | let layers :: [S.Demarc] = Preproc.removeNoGlobalPass result
59 | grafted :: [S.Demarc] = map (Preproc.grafting $ onlyLayers result) layers
60 | core_flp :: [CS.BaseExp] = map CC.compile grafted
61 | assert (CC.countGrafting grafted == 0) (return ())
62 | P.putStrLn ("Grafting completed.")
63 | case out_type of
64 | RustOutput -> do sf :: SourceFile Span <- return $ RC.genRust core_flp
65 | if length filterOutRegexes > 0
66 | then P.putStrLn "Warning: Rust output mode does not support filtering regular expressions. Ignoring."
67 | else return ()
68 | if length header > 0 || length footer > 0
69 | then P.putStrLn "Warning: Rust output mode does not support headers or footers. Ignoring."
70 | else return ()
71 | (RC.writeModule outputFile) sf
72 | COutput -> do sf <- return $ CComp.genC core_flp filterOutRegexes
73 | (CComp.writeCFile outputFile (header, footer)) sf
74 | exitSuccess
75 |
76 | oops s = P.putStr s >> exitFailure
77 |
78 | checkSuffix f
79 | | ".rs" `isSuffixOf` f = RustOutput
80 | | ".c" `isSuffixOf` f = COutput
81 | | ".h" `isSuffixOf` f = COutput
82 | | ".hpp" `isSuffixOf` f = COutput
83 | | otherwise = Unknown
84 |
85 | main = do
86 | args <- getArgs
87 | case args of
88 | (flpFile : outputFile : rst) ->
89 | do P.putStrLn $ "Loading FLP file from " ++ flpFile ++ "..."
90 | contents <- readFile flpFile
91 | P.putStrLn $ "Parsing top-level declarations..."
92 | --let toks = T.scanTokens contents
93 | --P.putStrLn $ "Tokens: " ++ show toks
94 | let result = P.parseTopLevelDecls contents
95 | let cnt = sum $ map countDeclNodes result
96 | P.putStrLn $ "Parsed contents: " ++ show result
97 | P.putStrLn ("Surface-syntax AST nodes: " ++ show cnt)
98 | doResult (checkSuffix outputFile) outputFile result
99 | _ -> usage
100 |
101 |
102 |
--------------------------------------------------------------------------------
/bin/make-pkg:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SCRIPTDIR="$(cd "`dirname $0`"; pwd)"
3 | GITDIR="$(dirname "$SCRIPTDIR")"
4 |
5 | # =============================================================================
6 | # MODIFY THESE PARAMETERS EACH TIME YOU PUBLISH A NEW VERSION:
7 | arch=x86_64-linux-tinfo6 #-nopie
8 | cVersion=2.4.0.1
9 | pVersion=0.1.0.0
10 | # =============================================================================
11 |
12 | gzDir=$GITDIR/.stack-work/dist/$arch/Cabal-$cVersion
13 | gzFile=flp-$pVersion.tar.gz
14 |
15 | echo "Changing to directory $GITDIR"
16 | cd "$GITDIR"
17 |
18 | # Generates .stack-work/dist/[arch]/Cabal-[version]/flp-[version].tar.gz :
19 | echo "Generating $gzFile"
20 | stack sdist --pvp-bounds both
21 |
22 | # Generates .stack-work/dist/[arch]/Cabal-[version]/doc/html/flp/* :
23 | echo "Generating haddock documentation"
24 | stack haddock --haddock-hyperlink-source
25 |
26 | # Copy source gzipped tar file to dist:
27 | mkdir -p dist
28 | cp $gzDir/$gzFile dist/
29 |
30 | # Copy over the haddock documentation:
31 | destDir=dist/flp-$pVersion/flp-$pVersion-docs
32 | mkdir -p $destDir
33 | cp -r $gzDir/doc/html/flp $destDir
34 |
35 | # Generate tar file to upload to hackage:
36 | echo "Making haddock tar file for *manual* upload to https://hackage.haskell.org/package/flp-$pVersion/maintain/docs"
37 | tarFile=flp-$pVersion-docs.tar
38 | tar --format=ustar -cvf dist/$tarFile $destDir | grep index.html
39 |
40 |
--------------------------------------------------------------------------------
/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | stack build --ghc-options="-ddump-splices -ddump-to-file"; ret=$?
3 | if ! [ $ret -eq 0 ]; then exit $ret; fi
4 | stack exec flp examples/hotspot.flp out.c && cat out.c
5 |
6 |
--------------------------------------------------------------------------------
/build-genrs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stack build flp --ghc-options="-ddump-splices"
4 | if [ $? -eq 0 ]; then
5 | # Add the path of the executable built by stack to PATH instead of installing it:
6 | export PATH="$(pwd)/$(dirname $(find .stack-work -path */flp/flp | head -n1)):$PATH"
7 | (cd genrs; cargo build)
8 | else
9 | echo "Failed to build FLP."
10 | fi
11 | #&& stack exec flp ./examples/immix/src/heap/layout.flp ./genrs/src/heap/flp/generated.rs \
12 |
13 |
--------------------------------------------------------------------------------
/build-immix:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | IMMIX="./examples/immix"
4 |
5 | #stack exec flp $IMMIX/layout.flp $IMMIX/src/heap/flp/generated.rs \
6 | stack build flp --ghc-options="-ddump-splices"
7 | if [ $? -eq 0 ]; then
8 | # Add the path of the executable built by stack to PATH instead of installing it:
9 | export PATH="$(pwd)/$(dirname $(find .stack-work -path */flp/flp | head -n1)):$PATH"
10 | (cd $IMMIX; cargo build --features "gcbench")
11 | else
12 | echo "Failed to build FLP."
13 | fi
14 |
--------------------------------------------------------------------------------
/clean-gen:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | (cd genrs; cargo clean; rm -f src/heap/flp/generated.rs)
4 |
5 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | _site
2 | .sass-cache
3 | .jekyll-cache
4 | .jekyll-metadata
5 | vendor
6 |
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /404.html
3 | layout: default
4 | ---
5 |
6 |
19 |
20 |
21 |
404
22 |
23 |
Page not found :(
24 |
The requested page could not be found.
25 |
26 |
--------------------------------------------------------------------------------
/docs/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | # Hello! This is where you manage which Jekyll version is used to run.
3 | # When you want to use a different version, change it below, save the
4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
5 | #
6 | # bundle exec jekyll serve
7 | #
8 | # This will help ensure the proper Jekyll version is running.
9 | # Happy Jekylling!
10 | #gem "jekyll", "~> 4.0.0"
11 | # This is the default theme for new Jekyll sites. You may change this to anything you like.
12 | gem "minima", "~> 2.5"
13 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and
14 | # uncomment the line below. To upgrade, run `bundle update github-pages`.
15 | gem "github-pages", group: :jekyll_plugins
16 |
17 | # If you have any plugins, put them here!
18 | group :jekyll_plugins do
19 | gem "jekyll-feed", "~> 0.12"
20 | end
21 |
22 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
23 | # and associated library.
24 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do
25 | gem "tzinfo", "~> 1.2"
26 | gem "tzinfo-data"
27 | end
28 |
29 | # Performance-booster for watching directories on Windows
30 | gem "wdm", "~> 0.1.1", :install_if => Gem.win_platform?
31 |
32 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | # Welcome to Jekyll!
2 | #
3 | # This config file is meant for settings that affect your whole blog, values
4 | # which you are expected to set up once and rarely edit after that. If you find
5 | # yourself editing this file very often, consider using Jekyll's data files
6 | # feature for the data you need to update frequently.
7 | #
8 | # For technical reasons, this file is *NOT* reloaded automatically when you use
9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process.
10 | #
11 | # If you need help with YAML syntax, here are some quick references for you:
12 | # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
13 | # https://learnxinyminutes.com/docs/yaml/
14 | #
15 | # Site settings
16 | # These are used to personalize your new site. If you look in the HTML files,
17 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
18 | # You can create any custom variable you would like, and they will be accessible
19 | # in the templates via {{ site.myvariable }}.
20 |
21 | title: Floorplan for Memory Layouts
22 | email: karl@cs.tufts.edu
23 | description: >- # this means to ignore newlines until "baseurl:"
24 | A language for specifying the layout of a one-dimensional address space,
25 | for garbage collectors and manual memory managers written in Rust.
26 | baseurl: "/" # the subpath of your site, e.g. /blog
27 | url: "redlineresearch.github.io" # the base hostname & protocol for your site, e.g. http://example.com
28 | #twitter_username: jekyllrb
29 | github_username: RedlineResearch
30 |
31 | # Build settings
32 | theme: minima
33 | #theme: jekyll-theme-slate
34 | plugins:
35 | - jekyll-feed
36 |
37 | # Exclude from processing.
38 | # The following items will not be processed, by default.
39 | # Any item listed under the `exclude:` key here will be automatically added to
40 | # the internal "default list".
41 | #
42 | # Excluded items can be processed by explicitly listing the directories or
43 | # their entries' file path in the `include:` list.
44 | #
45 | # exclude:
46 | # - .sass-cache/
47 | # - .jekyll-cache/
48 | # - gemfiles/
49 | # - Gemfile
50 | # - Gemfile.lock
51 | # - node_modules/
52 | # - vendor/bundle/
53 | # - vendor/cache/
54 | # - vendor/gems/
55 | # - vendor/ruby/
56 |
--------------------------------------------------------------------------------
/docs/_posts/2020-01-26-docs-site-goes-live.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Floorplan Compiler Documentation"
4 | date: 2020-01-26 15:50:00 -0500
5 | categories: floorplan update
6 | ---
7 |
8 | The most extensive documentation of the compiler can at present be
9 | found in the preprint of our accompanying conference paper [as available here][preprint-link].
10 | For prospective users of the compiler, we recommend reading Section 3 of the paper as that
11 | provides a practical view of what type of code the compiler generates for you.
12 |
13 | # Installation
14 |
15 | The Floorplan compiler itself (Haskell project) must be installed from source
16 | with the `stack` toolchain like so:
17 |
18 | ```bash
19 | git clone https://github.com/RedlineResearch/floorplan.git
20 | cd floorplan
21 | stack build
22 | stack install
23 | ```
24 |
25 | Once this is done, and the `flp` executable from doing so is on your `$PATH`, you
26 | can write Floorplan-enabled Rust crates by adding the following two crates to
27 | your `Cargo.toml`:
28 |
29 | - [flp-framework-0.1.0](https://crates.io/crates/flp-framework/0.1.0) added to `[dependencies]`
30 | - [flp-compiler-0.1.0](https://crates.io/crates/flp-compiler/0.1.0) added to `[build-dependencies]`
31 |
32 | # The "Hello World" of Floorplan
33 |
34 | The simplest memory layout you can define in Floorplan is an opaque chunk of memory,
35 | such as the ``Hello`` type as follows:
36 |
37 | {% highlight floorplan %}
38 | Hello -> 1 bytes
39 | {% endhighlight %}
40 |
41 | [preprint-link]: https://cronburg.com/papers/floorplan19.pdf
42 |
--------------------------------------------------------------------------------
/docs/about.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: About
4 | permalink: /about/
5 | ---
6 |
7 | Floorplan is a research project developed by the [RedlineResearch group][redline-research]
8 | at Tufts University, with ancillary technical support as well as encouragement from various
9 | members of the [Tufts Programming Languages][tupl-link] research group.
10 |
11 | 
12 |
13 | [redline-research]: http://www.cs.tufts.edu/research/redline/
14 | [tupl-link]: https://tupl.cs.tufts.edu/
15 |
16 |
--------------------------------------------------------------------------------
/docs/index.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | # Feel free to add content and custom Front Matter to this file.
3 | # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults
4 |
5 | layout: home
6 | ---
7 |
--------------------------------------------------------------------------------
/docs/unsupported-operations.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Unsupported Floorplan Operations
4 | permalink: /unsupported-operations/
5 | ---
6 |
7 | Many public functions and interfaces made available through the `flp-framework`
8 | crate is intrinsically unsafe, and unsupported for direct use in memory manager
9 | implementations. They are made available because:
10 |
11 | 1. Code generated by the `flp-compiler` crate needs common access to basic
12 | address computations.
13 | 2. To provide quick and easy prototyping of Floorplan extensions currently
14 | in development without requiring major modifications to the compiler.
15 | 3. To provide an escape hatch for performance sensitive memory management code
16 | which is not well supported by Floorplan.
17 |
18 |
--------------------------------------------------------------------------------
/examples/app.flp:
--------------------------------------------------------------------------------
1 | // Parser test: nested groups (union and seq) with
2 | // fixed-width fields.
3 |
4 | Block -> union {
5 | A -> seq { a : Cell<$ 0 $>, b : 2 words }
6 | | B -> seq { a : 0 words, c : 2 words }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/examples/arith.flp:
--------------------------------------------------------------------------------
1 | // Parser test
2 |
3 | Arith ||7 bytes|| -> (4 / 2 * 3 + 1) bytes
4 |
5 |
--------------------------------------------------------------------------------
/examples/arith_id.flp:
--------------------------------------------------------------------------------
1 | // Parser test: arithmetic expressions with identifiers
2 |
3 | Block -> union {
4 | A -> seq { a : 0 words, b : n words }
5 | | B -> seq { a : n words, c : k words }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/arith_power.flp:
--------------------------------------------------------------------------------
1 | // Parser test: exponentiation in arithmetic inside a field.
2 |
3 | Block -> union {
4 | A -> seq { a : 0 words, b : 2^2 words }
5 | | B -> seq { a : 0 words, c : 2^4 words }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/bits.flp:
--------------------------------------------------------------------------------
1 | // Parser test
2 |
3 | RefBits -> bits {
4 | SHORT_ENCODE : 1 bits,
5 | OBJ_START : 1 bits,
6 | REF : 6 bits,
7 | MORE : (2 + 2) bits + 4 bits
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/examples/bump.flp:
--------------------------------------------------------------------------------
1 |
2 | Bump ||2^16 bytes|| @(2^16 bytes)@ -> seq {
3 | union {
4 | Partial ||7 * 2^13 bytes|| @(2^13 bytes)@ -> seq {
5 | cells : # Cell,
6 | cursor : # words,
7 | limit : 0 words
8 | }
9 | | # Allocd -> 1 words
10 | }
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/examples/dyn_choice.flp:
--------------------------------------------------------------------------------
1 | // Parser test: dynamic union of two fixed-width fields.
2 |
3 | Union ||2 bytes|| @(2 bytes)@ -> # union {
4 | a : 1 bytes
5 | | b : 1 bytes
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/dyn_seq.flp:
--------------------------------------------------------------------------------
1 | // Parser test: dynamic sequence with fixed-width fields.
2 |
3 | Seq ||2 bytes|| @(2 bytes)@ -> # seq {
4 | a : 1 bytes,
5 | b : 1 bytes
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/dynamic_prim.flp:
--------------------------------------------------------------------------------
1 | // Parser test: dynamic primitive in arithmetic
2 |
3 | Seq -> seq {
4 | a : # bytes,
5 | b : 1 bytes
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/empty.flp:
--------------------------------------------------------------------------------
1 | // Mostly empty file
2 |
3 | // Ok.
4 |
5 | Foo -> Bar
6 |
7 |
--------------------------------------------------------------------------------
/examples/enum.flp:
--------------------------------------------------------------------------------
1 | // Parser test: basic enum
2 |
3 | LineMark -> enum {
4 | Free | Live | FreshAlloc
5 | | ConservLive | PrevLive
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/enum_bad0.flp:
--------------------------------------------------------------------------------
1 | // Parser test: basic enum failure due to lower-case ID (prevLive)
2 |
3 | LineMark -> enum {
4 | Free | Live | FreshAlloc
5 | | ConservLive | prevLive
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/grafting_loop.flp:
--------------------------------------------------------------------------------
1 | // Grafting error test.
2 |
3 | A -> B
4 | B -> C
5 | C -> A
6 |
--------------------------------------------------------------------------------
/examples/immix/.gitignore:
--------------------------------------------------------------------------------
1 | /out*.txt
2 | /std*.txt
3 | *.log
4 | ^.*\.s$
5 | *.out
6 | *.dylib
7 | *.so
8 | Cargo.lock
9 | target/*
10 | .project
11 | .setting
12 |
--------------------------------------------------------------------------------
/examples/immix/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 |
3 | name = "immix_rust"
4 | version = "0.0.1"
5 |
6 | # Original author shown here:
7 | authors = [ "Yi Lin " ]
8 | build = "build.rs"
9 |
10 | [build-dependencies]
11 | cc = "1.0"
12 | flp-compiler = { path = "../../flp-compiler" }
13 |
14 | [lib]
15 | name = "immix_rust"
16 | path = "src/lib.rs"
17 | crate-type = ["dylib"]
18 |
19 | [[bin]]
20 | name = "immix-rust"
21 | path = "src/main.rs"
22 |
23 | [features]
24 | exhaust = []
25 | initobj = []
26 | mt-gcbench = ["mt-trace"]
27 | gcbench = ["mt-trace"]
28 | mark = []
29 | trace = []
30 | linked-list = []
31 | leakme = []
32 | testme = []
33 |
34 | mt-trace = []
35 |
36 | [dependencies]
37 | flp-framework = { path = "../../flp-framework" }
38 | time = "0.2.16"
39 | memmap = "0.7.0"
40 | libc = "0.2.71"
41 | lazy_static = "1.4.0"
42 | log = { version = "0.4.8", features = [] }
43 | aligned_alloc = "0.1.3"
44 | crossbeam = "0.7.3"
45 | simple_logger = "1.6.0"
46 | backtrace = "0.3.48"
47 | rand = "0.7.3"
48 |
49 |
--------------------------------------------------------------------------------
/examples/immix/README.md:
--------------------------------------------------------------------------------
1 | immix-rust
2 | ==========
3 |
4 | An Immix implementation written in Rust.
5 |
6 | Features
7 | --------
8 |
9 | * Immix GC
10 | * thread local allocation
11 | * parallel GC
12 | * non generational, non moving, no defragment
13 | * Free list allocation (as large object allocator)
14 | * linked list + malloc
15 | * no proper reclamation yet
16 | * will reimplement this part in near future
17 | * Target platform
18 | * x86_64 (root tracing including stack scanning is target dependent)
19 |
20 | Interface
21 | --------------
22 | `src/lib.rs` defines interface to use the GC. The interface is also
23 | exposed as a C header `rust_c_interface/immix_rust.h`.
24 |
25 | ### Initialisation and Destroy
26 |
27 | * Rust: `pub extern fn gc_init(immix_size: usize, lo_size: usize, n_gcthreads: usize)`
28 | * C: `extern void gc_init(uint64_t immix_size, uint64_t lo_size, uint64_t n_gcthreads)`
29 |
30 | initialises the GC. First two parameters give sizes of immix space and
31 | large object space (in bytes), the 3rd parameter defines the number of
32 | GC threads (only used if multi-thread tracing is turned on via
33 | `--features mt-trace`)
34 |
35 | * Rust: `pub extern fn new_mutator() -> Box`
36 | * C: `extern struct Mutator* new_mutator()`
37 |
38 | creates a mutator. The user is responsible for storing the mutator at thread
39 | local storage.
40 |
41 | * Rust: `pub extern fn drop_mutator(mutator: Box)`
42 | * C: `extern void drop_mutator(struct Mutator* mutator)`
43 |
44 | destroys the mutator. After the call, `mutator` is no longer valid. This should
45 | only be useful when used in C (since Rust will identify the liveness of the mutator,
46 | and drop it when necessary).
47 |
48 | * Rust: `extern "C" {pub fn set_low_water_mark();}`
49 | * C: `extern void set_low_water_mark()`
50 |
51 | stores current stack pointer as a limit, so that conservative stack scanning
52 | will not traverse stack beyond this limit. Call this function after getting
53 | a new mutator.
54 |
55 | ### Allocation
56 |
57 | * Rust: `pub extern fn alloc(mutator: &mut Box, size: usize, align: usize) -> ObjectReference`
58 | * C: `inline uint64_t alloc(struct Mutator** mutator, uint64_t size, uint64_t align)`
59 |
60 | allocates an object in Immix space (thread local allocation).
61 | The object size should be smaller than one Immix line (256 bytes).
62 | Use large object allocation if the object size is larger than 256 bytes.
63 |
64 | * Rust: `pub extern fn alloc_large(mutator: &mut Box, size: usize) -> ObjectReference`
65 | * C: `extern uint64_t alloc_large(struct Mutator** mutator, uint64_t size)`
66 |
67 | allocates an object in large object space (global synchronisation involved).
68 | May later move the object size check into `alloc()` and deprecate this function.
69 |
70 | * Rust: `pub extern fn yieldpoint(mutator: &mut Box)`
71 | * C: `inline void yieldpoint(bool* take_yield, struct Mutator** m)`
72 |
73 | checks if current mutator should yield. GC won't be able to stop a mutator
74 | unless this function is put into code.
75 |
76 | Note: `alloc` and `yieldpoint` are fast paths. They are provided in Rust,
77 | and Rust compiler is able to inline them into Rust code. And they are
78 | expressed in C code in the header file, so that C compiler is able to inline them.
79 |
80 | Usage
81 | -----
82 | Install Rust and Cargo from https://www.rust-lang.org/.
83 | Run `cargo build --release` under the repo directory to build the GC
84 | (add `--features mt-trace` to turn on parallel GC), which
85 | will generate a dynamic linked library under `target/release/`.
86 | `rust_c_interface/test.c` gives an example on how to use the GC from
87 | C code. Compile and link `test.c` with the library to test (it is an
88 | infinite allocation loop).
89 |
90 | Near Future Plan
91 | ------
92 |
93 | * implementing a more efficient free list allocator
94 | * removing `alloc_large()`, allocation in large object space will be triggered
95 | in `alloc()` if the object size exceeds a threshold.
96 |
--------------------------------------------------------------------------------
/examples/immix/build.rs:
--------------------------------------------------------------------------------
1 | extern crate cc;
2 | extern crate flp_compiler as flpc;
3 |
4 | fn main() {
5 | //cc::compile_library("libgc_clib_x64.a", &["src/heap/gc/clib_x64.c"]);
6 | cc::Build::new()
7 | .file("src/heap/gc/clib_x64.c")
8 | .compile("libgc_clib_x64.a");
9 |
10 | flpc::Build::new()
11 | .src("src/heap/layout.flp")
12 | .dest("src/heap/layout.rs")
13 | .compile();
14 | //
15 | // if cfg!(target_os = "linux") {
16 | // cc::Config::new()
17 | // .flag("-lpfm")
18 | // .flag("-O3")
19 | // .file("src/common/perf.c")
20 | // .compile("libgc_perf.a");
21 | // }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/immix/c_src/common.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #ifndef COMMON_H
4 | #define COMMON_H
5 | typedef uintptr_t Address;
6 | typedef uintptr_t ObjectReference;
7 |
8 | #define LOG_POINTER_SIZE 3
9 | #define POINTER_SIZE (1 << LOG_POINTER_SIZE)
10 |
11 | #define likely(x) __builtin_expect((x),1)
12 | #define unlikely(x) __builtin_expect((x),0)
13 |
14 | #define Address_store(addr, Ty, value) *((Ty*)addr) = value
15 |
16 | #define lower_bits(value, len) (value & ((1 << len) - 1))
17 | #define test_nth_bit(value, index) ((value & (1 << index)) != 0)
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/examples/immix/c_src/freelist.c:
--------------------------------------------------------------------------------
1 | #include "freelist.h"
2 | #include "immix.h"
3 | #include
4 | #include
5 | #include
6 |
7 | int LO_SPACE_SIZE = DEFAULT_HEAP_SIZE * LO_SPACE_RATIO;
8 |
9 | FreeListSpace* lo_space;
10 |
11 | /*
12 | * freelist space (now using global malloc)
13 | */
14 | FreeListSpace* FreeListSpace_new(int space_size) {
15 | FreeListSpace* ret = (FreeListSpace*) malloc(sizeof(FreeListSpace));
16 |
17 | ret->head = NULL;
18 | ret->last = NULL;
19 | ret->total_size = space_size;
20 | ret->used_size = 0;
21 | pthread_mutex_init(&(ret->lock), NULL);
22 |
23 | return ret;
24 | }
25 |
26 | Address FreeListSpace_alloc(FreeListSpace* flSpace, int size, int align) {
27 | pthread_mutex_lock( &(flSpace->lock));
28 |
29 | if (flSpace->used_size + size > flSpace->total_size) {
30 | pthread_mutex_unlock( &(flSpace->lock));
31 | return 0;
32 | }
33 |
34 | // actually allocation
35 | Address addr;
36 | int ret = posix_memalign((void*)&addr, align, size);
37 | if (ret != 0) {
38 | printf("trying to alloc from freelist space: size=%d, align=%d\n", size, align);
39 | printf("failed posix_memalign alloc");
40 | exit(1);
41 | }
42 |
43 | // metadata
44 | FreeListNode* node = (FreeListNode*) malloc(sizeof(FreeListNode));
45 | node->next = NULL;
46 | node->addr = addr;
47 | node->size = size;
48 |
49 | // update freelist space
50 | if (flSpace->last == NULL) {
51 | // first node
52 | flSpace->head = node;
53 | flSpace->last = node;
54 | } else {
55 | flSpace->last->next = node;
56 | }
57 |
58 | flSpace->used_size = flSpace->used_size + size;
59 |
60 | pthread_mutex_unlock( &(flSpace->lock));
61 |
62 | return addr;
63 | }
64 |
65 | Address alloc_large(int size, int align, ImmixMutatorLocal* mutator, FreeListSpace* space) {
66 | while (true) {
67 | yieldpoint(mutator);
68 |
69 | Address ret = FreeListSpace_alloc(space, size, align);
70 |
71 | if (ret != 0) {
72 | return ret;
73 | } else {
74 | printf("Space exhausted for large alloc\n");
75 | printf("gc should happen here\n");
76 | exit(1);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/examples/immix/c_src/freelist.h:
--------------------------------------------------------------------------------
1 | #include "immix.h"
2 |
3 | #ifndef FREELIST_H
4 | #define FREELIST_H
5 |
6 | #define LO_SPACE_RATIO (1 - IMMIX_SPACE_RATIO)
7 | extern int LO_SPACE_SIZE;
8 |
9 | /*
10 | * FreeList Node, Space
11 | */
12 | struct FreeListNode;
13 | typedef struct FreeListNode {
14 | struct FreeListNode* next;
15 | Address addr;
16 | int64_t size;
17 | } FreeListNode;
18 |
19 | typedef struct FreeListSpace {
20 | FreeListNode* head;
21 | FreeListNode* last;
22 |
23 | pthread_mutex_t lock;
24 |
25 | int used_size;
26 | int total_size;
27 | } FreeListSpace;
28 |
29 | extern FreeListSpace* lo_space;
30 |
31 | Address alloc_large(int size, int align, ImmixMutatorLocal* mutator, FreeListSpace* space);
32 |
33 | FreeListSpace* FreeListSpace_new(int space_size);
34 | Address FreeListSpace_alloc(FreeListSpace* flSpace, int size, int align);
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/examples/immix/c_src/gc.c:
--------------------------------------------------------------------------------
1 | #include "gc.h"
2 | #include "immix.h"
3 | #include "objectmodel.h"
4 |
5 | #include
6 | #include
7 |
8 | AddressVector* AddressVector_new(int init_capacity) {
9 | Address* data = (Address*) malloc(init_capacity * sizeof(Address));
10 |
11 | AddressVector* ret = (AddressVector*) malloc(sizeof(AddressVector));
12 | ret->current_capacity = init_capacity;
13 | ret->length = 0;
14 | ret->data = data;
15 |
16 | return ret;
17 | }
18 |
19 | void AddressVector_grow(AddressVector* list) {
20 | int old_capacity = list->current_capacity;
21 | int new_capacity = old_capacity * 2;
22 |
23 | void* new_data = malloc(new_capacity * sizeof(Address));
24 | memcpy(new_data, (void*)list->data, old_capacity * sizeof(Address));
25 |
26 | free((void*)list->data);
27 |
28 | list->data = (Address*) new_data;
29 | list->current_capacity = new_capacity;
30 | }
31 |
32 | void start_trace(AddressVector* roots) {
33 | uint8_t mark_state = MARK_STATE;
34 |
35 | LineMark* line_mark_table = immix_space->line_mark_table;
36 | int line_mark_table_len = immix_space->line_mark_table_len;
37 | Address space_start = immix_space->start;
38 | Address space_end = immix_space->end;
39 |
40 | while (!AddressVector_is_empty(roots)) {
41 | trace_object(AddressVector_pop(roots), roots, mark_state, line_mark_table, line_mark_table_len, space_start, space_end);
42 | }
43 | }
44 |
45 | void trace_object_no_inline(
46 | ObjectReference obj,
47 | AddressVector* work_queue,
48 | uint8_t mark_state,
49 | LineMark* line_mark_table, int line_mark_table_len,
50 | Address space_start, Address space_end
51 | ) {
52 | mark_as_traced(immix_space->trace_map, obj, space_start, mark_state);
53 |
54 | if (obj >= space_start && obj < space_end) {
55 | mark_line_live(line_mark_table, line_mark_table_len, space_start, obj);
56 | } else {
57 | // freelist mark
58 | }
59 |
60 | Address base = obj;
61 |
62 | while (true) {
63 | uint8_t value = get_ref_byte(obj);
64 | uint8_t ref_bits = lower_bits(value, REF_BITS_LEN);
65 | bool short_encode = test_nth_bit(value, SHORT_ENCODE_BIT);
66 |
67 | switch (ref_bits) {
68 | case 0b00000001:
69 | process_edge(base, work_queue, mark_state); break;
70 | case 0b00000011:
71 | process_edge(base, work_queue, mark_state);
72 | process_edge(base + 8, work_queue, mark_state); break;
73 | case 0b00001111:
74 | process_edge(base, work_queue, mark_state);
75 | process_edge(base + 8, work_queue, mark_state);
76 | process_edge(base + 16, work_queue, mark_state);
77 | process_edge(base + 24, work_queue, mark_state); break;
78 | default:
79 | printf("unexpected ref_bits patterns"); exit(1);
80 | }
81 |
82 | if (short_encode) {
83 | return;
84 | } else {
85 | printf("should not use long encode"); exit(1);
86 | base = base + REF_BITS_LEN * 8;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/examples/immix/c_src/gc.h:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 | #include "immix.h"
3 | #include "objectmodel.h"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #ifndef GC_H
10 | #define GC_H
11 |
12 | /*
13 | * A linked list that would be useful when scanning object
14 | */
15 | typedef struct AddressVector {
16 | int current_capacity;
17 |
18 | int length;
19 | Address* data;
20 | } AddressVector;
21 |
22 | AddressVector* AddressVector_new(int init_capacity);
23 | void AddressVector_grow(AddressVector* list);
24 |
25 | inline void AddressVector_push(AddressVector* list, Address value) __attribute__((always_inline));
26 | inline void AddressVector_push(AddressVector* list, Address value) {
27 | if (unlikely(list->length == list->current_capacity)) {
28 | // grow
29 | AddressVector_grow(list);
30 | }
31 |
32 | list->data[list->length] = value;
33 | list->length ++;
34 | }
35 |
36 | inline Address AddressVector_pop(AddressVector* list) __attribute__((always_inline));
37 | inline Address AddressVector_pop(AddressVector* list) {
38 | Address ret = list->data[list->length - 1];
39 | list->length--;
40 | return ret;
41 | }
42 |
43 | inline bool AddressVector_is_empty(AddressVector* list) __attribute__((always_inline));
44 | inline bool AddressVector_is_empty(AddressVector* list) {
45 | return list->length == 0;
46 | }
47 |
48 | void start_trace(AddressVector* roots);
49 |
50 | inline void process_edge(Address addr, AddressVector* work_queue, uint8_t mark_state) __attribute__((always_inline));
51 | inline void process_edge(Address addr, AddressVector* work_queue, uint8_t mark_state) {
52 | ObjectReference obj = *((ObjectReference*) addr);
53 |
54 | if (obj != 0 && !is_traced(obj, mark_state)) {
55 | AddressVector_push(work_queue, obj);
56 | }
57 | }
58 |
59 | void trace_object_no_inline(
60 | ObjectReference obj,
61 | AddressVector* work_queue,
62 | uint8_t mark_state,
63 | LineMark* line_mark_table, int line_mark_table_len,
64 | Address space_start, Address space_end
65 | );
66 |
67 | inline void trace_object(
68 | ObjectReference obj,
69 | AddressVector* work_queue,
70 | uint8_t mark_state,
71 | LineMark* line_mark_table, int line_mark_table_len,
72 | Address space_start, Address space_end
73 | ) __attribute__((always_inline));
74 | inline void trace_object(
75 | ObjectReference obj,
76 | AddressVector* work_queue,
77 | uint8_t mark_state,
78 | LineMark* line_mark_table, int line_mark_table_len,
79 | Address space_start, Address space_end
80 | ) {
81 | mark_as_traced(immix_space->trace_map, obj, space_start, mark_state);
82 |
83 | if (obj >= space_start && obj < space_end) {
84 | mark_line_live(line_mark_table, line_mark_table_len, space_start, obj);
85 | } else {
86 | // freelist mark
87 | }
88 |
89 | Address base = obj;
90 |
91 | while (true) {
92 | uint8_t value = get_ref_byte(obj);
93 | uint8_t ref_bits = lower_bits(value, REF_BITS_LEN);
94 | bool short_encode = test_nth_bit(value, SHORT_ENCODE_BIT);
95 |
96 | switch (ref_bits) {
97 | case 0b00000001:
98 | process_edge(base, work_queue, mark_state); break;
99 | case 0b00000011:
100 | process_edge(base, work_queue, mark_state);
101 | process_edge(base + 8, work_queue, mark_state); break;
102 | case 0b00001111:
103 | process_edge(base, work_queue, mark_state);
104 | process_edge(base + 8, work_queue, mark_state);
105 | process_edge(base + 16, work_queue, mark_state);
106 | process_edge(base + 24, work_queue, mark_state); break;
107 | default:
108 | printf("unexpected ref_bits patterns"); exit(1);
109 | }
110 |
111 | if (short_encode) {
112 | return;
113 | } else {
114 | printf("should not use long encode"); exit(1);
115 | base = base + REF_BITS_LEN * 8;
116 | }
117 | }
118 | }
119 |
120 | #endif
121 |
--------------------------------------------------------------------------------
/examples/immix/c_src/immix.h:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 | #include
3 | #include
4 | #include
5 |
6 | #ifndef IMMIX_H
7 | #define IMMIX_H
8 |
9 | #define DEFAULT_HEAP_SIZE (500 << 20)
10 |
11 | typedef uint8_t LineMark;
12 | typedef uint8_t BlockMark;
13 |
14 | /*
15 | * ImmixBlock
16 | */
17 | typedef struct ImmixBlock {
18 | struct ImmixBlock* next;
19 | struct ImmixBlock* prev;
20 |
21 | int id;
22 | BlockMark state;
23 | Address start;
24 |
25 | LineMark* line_mark_ptr;
26 | int line_mark_len;
27 | } ImmixBlock;
28 |
29 | /*
30 | * ImmixSpace
31 | */
32 | typedef struct ImmixSpace {
33 | Address start;
34 | Address end;
35 |
36 | uint8_t* alloc_map;
37 | uint8_t* trace_map;
38 | int map_len;
39 |
40 | LineMark* line_mark_table;
41 | int line_mark_table_len;
42 |
43 | int total_blocks;
44 |
45 | ImmixBlock* usable_blocks;
46 | ImmixBlock* used_blocks;
47 |
48 | pthread_mutex_t lock;
49 | } ImmixSpace;
50 |
51 | #define IMMIX_SPACE_RATIO 0.8
52 |
53 | extern int IMMIX_SPACE_SIZE;
54 | extern ImmixSpace* immix_space;
55 |
56 | /*
57 | * Immix Mutator
58 | */
59 | typedef struct ImmixMutatorLocal {
60 | uint64_t id;
61 |
62 | uint8_t* alloc_map;
63 | Address space_start;
64 |
65 | Address cursor;
66 | Address limit;
67 | uint64_t line;
68 |
69 | ImmixSpace* space;
70 |
71 | ImmixBlock* block;
72 |
73 | bool take_yield;
74 | } ImmixMutatorLocal;
75 |
76 | #define MAX_MUTATORS 1024
77 |
78 | extern pthread_mutex_t id_lock;
79 | extern int N_MUTTAORS;
80 | extern ImmixMutatorLocal* MUTATORS[MAX_MUTATORS];
81 |
82 | void gc_init();
83 |
84 | /*
85 | * Immix constants
86 | */
87 | #define IMMIX_LOG_BYTES_IN_BLOCK 16
88 | #define IMMIX_BYTES_IN_BLOCK (1 << IMMIX_LOG_BYTES_IN_BLOCK)
89 |
90 | #define IMMIX_LOG_BYTES_IN_LINE 8
91 | #define IMMIX_BYTES_IN_LINE (1 << IMMIX_LOG_BYTES_IN_LINE)
92 |
93 | #define IMMIX_LINES_IN_BLOCK (1 << (IMMIX_LOG_BYTES_IN_BLOCK - IMMIX_LOG_BYTES_IN_LINE))
94 |
95 | #define IMMIX_LINE_MARK_FREE 0
96 | #define IMMIX_LINE_MARK_LIVE 1
97 | #define IMMIX_LINE_MARK_FRESH_ALLOC 2
98 | #define IMMIX_LINE_MARK_CONSERV_LIVE 3
99 | #define IMMIX_LINE_MARK_PREV_LIVE 4
100 |
101 | #define IMMIX_BLOCK_MARK_USABLE 0
102 | #define IMMIX_BLOCK_MARK_FULL 1
103 |
104 | #define ALIGNMENT_VALUE 1
105 |
106 | inline Address align_up(Address region, int align) __attribute__((always_inline));
107 | inline Address align_up(Address region, int align) {
108 | return (region + align - 1) & ~ (align - 1);
109 | }
110 |
111 | inline void fill_alignment_gap(Address start, Address end) __attribute__((always_inline));
112 | inline void fill_alignment_gap(Address start, Address end) {
113 | if (end > start)
114 | memset((void*) start, ALIGNMENT_VALUE, end - start);
115 | }
116 |
117 | ImmixSpace* ImmixSpace_new(int space_size);
118 | ImmixMutatorLocal* ImmixMutatorLocal_new(ImmixSpace* space);
119 |
120 | Address ImmixMutatorLocal_try_alloc_from_local(ImmixMutatorLocal* mutator, int size, int align) __attribute__ ((noinline));
121 |
122 | inline void init_object(ImmixMutatorLocal* mutator, Address addr, uint8_t encode) __attribute__((always_inline));
123 | inline void init_object(ImmixMutatorLocal* mutator, Address addr, uint8_t encode){
124 | uint8_t* map = mutator->alloc_map;
125 | map[(addr - mutator->space_start) >> LOG_POINTER_SIZE] = encode;
126 | }
127 | void init_object_no_inline(ImmixMutatorLocal* mutator, Address addr, uint8_t encode);
128 |
129 | inline Address ImmixMutatorLocal_alloc(ImmixMutatorLocal* mutator, int size, int align) __attribute__((always_inline));
130 | inline Address ImmixMutatorLocal_alloc(ImmixMutatorLocal* mutator, int size, int align) {
131 | Address start = align_up(mutator->cursor, align);
132 | Address end = start + size;
133 |
134 | if (end > mutator->limit) {
135 | return ImmixMutatorLocal_try_alloc_from_local(mutator, size, align);
136 | } else {
137 | // fill_alignment_gap(mutator->cursor, start);
138 | mutator->cursor = end;
139 |
140 | return start;
141 | }
142 | }
143 |
144 | void ImmixSpace_print(ImmixSpace* space);
145 |
146 | int ImmixBlock_get_next_avaiable_line(ImmixBlock* block, int line);
147 | int ImmixBlock_get_next_unavailable_line(ImmixBlock* block, int line);
148 | ImmixBlock* ImmixSpace_get_next_usable_block(ImmixSpace* space);
149 |
150 | void yieldpoint_slowpath(ImmixMutatorLocal* mutator) __attribute__((__noinline__));
151 |
152 | inline void yieldpoint(ImmixMutatorLocal* mutator) __attribute__((always_inline));
153 | inline void yieldpoint(ImmixMutatorLocal* mutator) {
154 | if (mutator->take_yield)
155 | yieldpoint_slowpath(mutator);
156 | }
157 |
158 | inline void mark_line_live(LineMark* line_mark_table, int line_mark_table_len, Address start, Address addr) __attribute__((always_inline));
159 | inline void mark_line_live(LineMark* line_mark_table, int line_mark_table_len, Address start, Address addr) {
160 | uintptr_t mark_table_index = (addr - start) >> IMMIX_LOG_BYTES_IN_LINE;
161 | line_mark_table[mark_table_index] = IMMIX_LINE_MARK_LIVE;
162 | if (mark_table_index < line_mark_table_len - 1)
163 | line_mark_table[mark_table_index + 1] = IMMIX_LINE_MARK_CONSERV_LIVE;
164 | }
165 |
166 | #endif
167 |
--------------------------------------------------------------------------------
/examples/immix/c_src/immix_mutator.c:
--------------------------------------------------------------------------------
1 | #include "immix.h"
2 | #include "freelist.h"
3 | #include "common.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | pthread_mutex_t id_lock;
12 | int N_MUTATORS;
13 | ImmixMutatorLocal* MUTATORS[MAX_MUTATORS];
14 |
15 | void gc_init() {
16 | immix_space = ImmixSpace_new(IMMIX_SPACE_SIZE);
17 | lo_space = FreeListSpace_new(LO_SPACE_SIZE);
18 |
19 | pthread_mutex_init(&id_lock, NULL);
20 | N_MUTATORS = 0;
21 | }
22 |
23 | void yieldpoint_slowpath(ImmixMutatorLocal* mutator) {
24 | printf("yieldpoint slowpath");
25 | }
26 |
27 | void ImmixMutatorLocal_reset(ImmixMutatorLocal* mutator) {
28 | mutator->cursor = 0;
29 | mutator->limit = 0;
30 | mutator->line = IMMIX_LINES_IN_BLOCK;
31 |
32 | mutator->block = NULL;
33 | }
34 |
35 | ImmixMutatorLocal* ImmixMutatorLocal_new(ImmixSpace* space) {
36 | ImmixMutatorLocal* ret = (ImmixMutatorLocal*) malloc(sizeof(ImmixMutatorLocal));
37 |
38 | pthread_mutex_lock(&id_lock);
39 |
40 | // set id
41 | ret->id = N_MUTATORS;
42 | // global array
43 | MUTATORS[N_MUTATORS] = ret;
44 | // increase id
45 | N_MUTATORS ++;
46 |
47 | // set other fields
48 | ret->space = space;
49 | ImmixMutatorLocal_reset(ret);
50 |
51 | // alloc map
52 | ret->alloc_map = space->alloc_map;
53 | ret->space_start = space->start;
54 |
55 | pthread_mutex_unlock(&id_lock);
56 | return ret;
57 | }
58 |
59 | Address ImmixMutatorLocal_alloc_from_global(ImmixMutatorLocal* mutator, int size, int align) {
60 | // we dont need to return block (as in Rust)
61 |
62 | while (true) {
63 | yieldpoint(mutator);
64 |
65 | ImmixBlock* new_block = ImmixSpace_get_next_usable_block(mutator->space);
66 |
67 | if (new_block != NULL) {
68 | mutator->block = new_block;
69 | mutator->cursor = new_block->start;
70 | mutator->limit = new_block->start;
71 | mutator->line = 0;
72 |
73 | return ImmixMutatorLocal_alloc(mutator, size, align);
74 | } else {
75 | continue;
76 | }
77 | }
78 | }
79 |
80 | Address ImmixMutatorLocal_try_alloc_from_local(ImmixMutatorLocal* mutator, int size, int align) {
81 | if (mutator->line < IMMIX_LINES_IN_BLOCK) {
82 | int next_available_line = ImmixBlock_get_next_avaiable_line(mutator->block, mutator->line);
83 |
84 | if (next_available_line != -1) {
85 | int end_line = ImmixBlock_get_next_unavailable_line(mutator->block, next_available_line);
86 |
87 | mutator->cursor = mutator->block->start + (next_available_line << IMMIX_LOG_BYTES_IN_LINE);
88 | mutator->limit = mutator->block->start + (end_line << IMMIX_LOG_BYTES_IN_LINE);
89 | mutator->line = end_line;
90 |
91 | memset((void*)mutator->cursor, 0, mutator->limit - mutator->cursor);
92 |
93 | int line = next_available_line;
94 | for (; line < end_line; line++) {
95 | mutator->block->line_mark_ptr[line] = IMMIX_LINE_MARK_FRESH_ALLOC;
96 | }
97 |
98 | return ImmixMutatorLocal_alloc(mutator, size, align);
99 | } else {
100 | return ImmixMutatorLocal_alloc_from_global(mutator, size, align);
101 | }
102 | } else {
103 | return ImmixMutatorLocal_alloc_from_global(mutator, size, align);
104 | }
105 | }
106 |
107 | void init_object_no_inline(ImmixMutatorLocal* mutator, Address addr, uint8_t encode) {
108 | mutator->alloc_map[(addr - mutator->space_start) >> LOG_POINTER_SIZE] = encode;
109 | }
110 |
--------------------------------------------------------------------------------
/examples/immix/c_src/immix_space.c:
--------------------------------------------------------------------------------
1 | #include "immix.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | ImmixSpace* immix_space;
9 |
10 | int IMMIX_SPACE_SIZE = DEFAULT_HEAP_SIZE * IMMIX_SPACE_RATIO;
11 |
12 | void init_blocks(ImmixSpace* space) {
13 | int id = 0;
14 | Address cur = space->start;
15 | LineMark* curLineMark = space->line_mark_table;
16 | ImmixBlock* last = NULL;
17 |
18 | int totalBlocks = 0;
19 |
20 | for (; cur < space->end; cur += IMMIX_BYTES_IN_BLOCK, curLineMark = &(curLineMark[IMMIX_LINES_IN_BLOCK]), id += 1) {
21 | ImmixBlock* b = (ImmixBlock*) malloc(sizeof(ImmixBlock));
22 | totalBlocks ++;
23 |
24 | if (last == NULL) {
25 | // first one
26 | space->usable_blocks = b;
27 | b->prev = NULL;
28 | } else {
29 | last->next = b;
30 | b->prev = last;
31 | }
32 |
33 | b->id = id;
34 | b->state = IMMIX_BLOCK_MARK_USABLE;
35 | b->start = cur;
36 | b->line_mark_ptr = curLineMark;
37 | b->line_mark_len = IMMIX_LINES_IN_BLOCK;
38 |
39 | last = b;
40 | }
41 |
42 | space->total_blocks = id;
43 | }
44 |
45 | ImmixSpace* ImmixSpace_new(int space_size) {
46 | // mmap
47 | void* mmap_ret = mmap(0, space_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
48 | if (mmap_ret == MAP_FAILED) {
49 | printf("failed to call mmap\n");
50 | exit(1);
51 | }
52 | Address mmap_start = (Address) mmap_ret;
53 |
54 | // init immix space
55 | ImmixSpace* ret = (ImmixSpace*) malloc(sizeof(ImmixSpace));
56 |
57 | ret->start = align_up(mmap_start, IMMIX_BYTES_IN_BLOCK);
58 | ret->end = ret->start + space_size;
59 |
60 | // init line mark table
61 | ret->line_mark_table_len = space_size / IMMIX_BYTES_IN_LINE;
62 | ret->line_mark_table = (LineMark*) malloc(sizeof(LineMark) * ret->line_mark_table_len);
63 | LineMark* cursor = ret->line_mark_table;
64 | for (int i = 0; i < ret->line_mark_table_len; i++) {
65 | *cursor = IMMIX_LINE_MARK_FREE;
66 | cursor += sizeof(LineMark);
67 | }
68 |
69 | // init alloc map and trace map
70 | ret->map_len = space_size >> LOG_POINTER_SIZE;
71 | int size = sizeof(uint8_t) * ret->map_len;
72 | ret->alloc_map = (uint8_t*) malloc(size);
73 | memset((void*)ret->alloc_map, 0, size);
74 | ret->trace_map = (uint8_t*) malloc(size);
75 | memset((void*)ret->trace_map, 0, size);
76 |
77 | printf("malloc alloc_map of size %d bytes\n", size);
78 |
79 | pthread_mutex_init(&(ret->lock), NULL);
80 |
81 | init_blocks(ret);
82 |
83 | return ret;
84 | }
85 |
86 | int ImmixBlock_get_next_avaiable_line(ImmixBlock* block, int line) {
87 | int i = line;
88 | while (i < block->line_mark_len) {
89 | if (block->line_mark_ptr[i] == IMMIX_LINE_MARK_FREE) {
90 | return i;
91 | } else {
92 | i++;
93 | }
94 | }
95 | return -1;
96 | }
97 |
98 | int ImmixBlock_get_next_unavailable_line(ImmixBlock* block, int line) {
99 | int i = line;
100 | while (i < block->line_mark_len) {
101 | if (block->line_mark_ptr[i] == IMMIX_LINE_MARK_FREE) {
102 | i++;
103 | } else {
104 | return i;
105 | }
106 | }
107 | return i;
108 | }
109 |
110 | ImmixBlock* ImmixSpace_get_next_usable_block(ImmixSpace* space) {
111 | // lock
112 | pthread_mutex_lock(&(space->lock));
113 |
114 | // get a new block
115 | ImmixBlock* new_block = space->usable_blocks;
116 |
117 | if (new_block != NULL) {
118 | // success
119 |
120 | // remove from usable_blocks
121 | space->usable_blocks = new_block->next;
122 | if (new_block->next != NULL)
123 | space->usable_blocks->prev = NULL;
124 |
125 | // add to used block
126 | new_block->prev = NULL;
127 | new_block->next = space->used_blocks;
128 | if (space->used_blocks != NULL)
129 | space->used_blocks->prev = new_block;
130 | space->used_blocks = new_block;
131 |
132 | // unlock
133 | pthread_mutex_unlock(&(space->lock));
134 |
135 | return new_block;
136 | } else {
137 | pthread_mutex_unlock(&(space->lock));
138 |
139 | printf("Space exhausted\n");
140 | printf("gc should happen here\n");
141 | exit(1);
142 |
143 | return NULL;
144 | }
145 | }
146 |
147 | void ImmixSpace_print(ImmixSpace* space) {
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/examples/immix/c_src/objectmodel.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "objectmodel.h"
5 |
6 | uint8_t MARK_STATE = 1;
7 |
8 | //Header Header_new(uint8_t gcbits, uint8_t ref_bits, uint16_t size, uint32_t ty_id) {
9 | // Header header;
10 | // header.gcbits = gcbits;
11 | // header.ref_bits = ref_bits;
12 | // header.size = size;
13 | // header.ty_id = ty_id;
14 | // return header;
15 | //}
16 |
17 | const char *byte_to_binary(int x) {
18 | static char b[9];
19 | b[0] = '\0';
20 |
21 | int z;
22 | for (z = 128; z > 0; z >>= 1)
23 | {
24 | strcat(b, ((x & z) == z) ? "1" : "0");
25 | }
26 |
27 | return b;
28 | }
29 |
30 | //void Header_print(Header* header) {
31 | // uint64_t header_u64 = Header_as_u64(*header);
32 | // printf("0x%" PRIx64 "(0b%s)\n", header_u64, byte_to_binary(header_u64));
33 | // printf("gcbits: 0b%s\n", byte_to_binary((int) header->gcbits));
34 | // printf("refbits: 0b%s\n", byte_to_binary((int) header->ref_bits));
35 | //}
36 |
37 | //void __attribute__ ((noinline)) mark_as_traced(ObjectReference obj) {
38 | // set_gcbits_as(obj, MARK_BIT_INDEX, MARK_STATE);
39 | //}
40 |
--------------------------------------------------------------------------------
/examples/immix/c_src/objectmodel.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "common.h"
5 | #include "immix.h"
6 |
7 | #ifndef OBJECTMODEL_H
8 | #define OBJECTMODEL_H
9 |
10 | #define HEADER_SIZE 0
11 |
12 | //#define SCALAR_INLINE 0
13 | //#define SCALAR_ID 1
14 | //#define ARRAY_INLINE 2
15 | //#define ARRAY_ID 3
16 | //
17 | //#define MARK_BIT_INDEX 0
18 | extern uint8_t MARK_STATE;
19 |
20 | //typedef struct Header {
21 | // uint8_t gcbits;
22 | // uint8_t ref_bits;
23 | // uint16_t size;
24 | // uint32_t ty_id;
25 | //} Header;
26 | //
27 | //typedef struct Type {
28 | // int size;
29 | //
30 | // int* ref_offsets;
31 | // int ref_offsets_len;
32 | //} Type;
33 | //
34 | //extern Type* types;
35 | //
36 | //Header Header_new(uint8_t gcbits, uint8_t ref_bits, uint16_t size, uint32_t ty_id);
37 | //void Header_print(Header* header);
38 | //
39 | //inline uint64_t Header_as_u64(Header hdr) __attribute__((always_inline));
40 | //inline uint64_t Header_as_u64(Header hdr) {
41 | // return *((uint64_t*)&hdr);
42 | //}
43 |
44 | //inline void* as_object(Address addr) __attribute__((always_inline));
45 | //inline void* as_object(Address addr) {
46 | // return (void*) (addr + HEADER_SIZE);
47 | //}
48 | //
49 | //inline void set_gcbits_as(ObjectReference obj, int index, uint8_t value) __attribute__((always_inline));
50 | //inline void set_gcbits_as(ObjectReference obj, int index, uint8_t value) {
51 | // Header* hdr = ((Header*)obj);
52 | // hdr->gcbits = hdr->gcbits ^ (( -value ^ hdr->gcbits) & (1 << index));
53 | //}
54 | //
55 | //inline bool test_gcbits(ObjectReference obj, int index, uint8_t value) __attribute__((always_inline));
56 | //inline bool test_gcbits(ObjectReference obj, int index, uint8_t value) {
57 | // return (((Header*)obj)->gcbits & value) == value;
58 | //}
59 |
60 | #define REF_BITS_LEN 6
61 | #define OBJ_START_BIT 6
62 | #define SHORT_ENCODE_BIT 7
63 |
64 | inline bool is_traced(ObjectReference obj, uint8_t mark_state) __attribute__((always_inline));
65 | inline bool is_traced(ObjectReference obj, uint8_t mark_state) {
66 | return immix_space->trace_map[(obj - immix_space->start) >> LOG_POINTER_SIZE] == mark_state;
67 | }
68 |
69 | inline void mark_as_traced(uint8_t* trace_map, ObjectReference obj, Address space_start, uint8_t mark_state) __attribute__((always_inline));
70 | inline void mark_as_traced(uint8_t* trace_map, ObjectReference obj, Address space_start, uint8_t mark_state) {
71 | trace_map[(obj - space_start) >> LOG_POINTER_SIZE] = mark_state;
72 | }
73 | //void __attribute__ ((noinline)) mark_as_traced(ObjectReference obj);
74 |
75 | inline uint8_t get_ref_byte(ObjectReference obj) __attribute__((always_inline));
76 | inline uint8_t get_ref_byte(ObjectReference obj) {
77 | return immix_space->alloc_map[(obj - immix_space->start) >> LOG_POINTER_SIZE];
78 | }
79 |
80 | #endif
81 |
--------------------------------------------------------------------------------
/examples/immix/dot/.gitignore:
--------------------------------------------------------------------------------
1 | /*.png
2 |
--------------------------------------------------------------------------------
/examples/immix/features:
--------------------------------------------------------------------------------
1 | exhaust gcbench mark
2 |
--------------------------------------------------------------------------------
/examples/immix/features.all:
--------------------------------------------------------------------------------
1 | exhaust initobj gcbench mt-gcbench mark trace mt-trace
2 |
--------------------------------------------------------------------------------
/examples/immix/run-mt-trace.py.example:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import subprocess
4 |
5 | def error(msg):
6 | print 'Error: ' + msg
7 | sys.exit
8 |
9 | def monitor_invoke(command):
10 | print command
11 | run = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
12 | while True:
13 | out = run.stdout.read(1)
14 | if out == '' and run.poll() != None:
15 | break;
16 | if out != '':
17 | sys.stdout.write(out)
18 | sys.stdout.flush()
19 | return True
20 |
21 | REMOTE_USER = 'yilin'
22 | RSYNC_DIR = '/home/{}/rust_gc/immix_rust_new/'.format(REMOTE_USER)
23 | MACHINE = 'rat.moma'
24 |
25 | #./run-mt-trace.py 1 8
26 |
27 | if len(sys.argv) != 5:
28 | error("usage: ./run-mt-trace.py 2000M 20 1 8")
29 |
30 | heap = sys.argv[1]
31 | invocations = int(sys.argv[2])
32 | gc_threads_min = int(sys.argv[3])
33 | gc_threads_max = int(sys.argv[4])
34 |
35 | print '###heap = ' + heap
36 | print '###invocations = ' + str(invocations)
37 | print '###gc_threads_min = ' + str(gc_threads_min)
38 | print '###gc_threads_max = ' + str(gc_threads_max)
39 |
40 | print 'rsync...'
41 | remote_path = "{}@{}:{}".format(REMOTE_USER, MACHINE, RSYNC_DIR)
42 | ret = subprocess.call(["rsync", "-rav", "-I", "--delete", "--exclude=target", "--exclude=.hg", ".", remote_path])
43 | if ret != 0:
44 | error('failed to rsync')
45 |
46 | build = 'ssh -t {}@{} "cd {}; rm -r target; cargo build --release --features \"mt-trace\""'.format(REMOTE_USER, MACHINE, RSYNC_DIR)
47 | succ = monitor_invoke(build)
48 | if not succ:
49 | error('failed to build mt-trace')
50 | print
51 |
52 | for n_gc_threads in range(gc_threads_min, gc_threads_max + 1):
53 | print 'Running with %d gc threads' % (n_gc_threads)
54 | print
55 |
56 | for invocation in range(0, invocations):
57 | print 'Invocation %d' % (invocation)
58 | build = 'ssh -t {}@{} "cd {}; HEAP_SIZE={} N_GCTHREADS={} ./target/release/immix-rust"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, heap, n_gc_threads)
59 | succ = monitor_invoke(build)
60 | if not succ:
61 | error('failed to execute rust code')
62 | print
63 | print 'Invocation Finish'
64 |
65 | print 'Finish for current thread number'
66 | print
67 |
--------------------------------------------------------------------------------
/examples/immix/run.py.example:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import subprocess
4 |
5 | def error(msg):
6 | print 'Error: ' + msg
7 | sys.exit
8 |
9 | def monitor_invoke(command):
10 | print command
11 | run = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
12 | while True:
13 | out = run.stdout.read(1)
14 | if out == '' and run.poll() != None:
15 | break;
16 | if out != '':
17 | sys.stdout.write(out)
18 | sys.stdout.flush()
19 | return True
20 |
21 | REMOTE_USER = 'yilin'
22 | RSYNC_DIR = '/home/{}/rust_gc/immix_rust_new/'.format(REMOTE_USER)
23 | MACHINE = 'rat.moma'
24 |
25 | C_FLAGS = '-O3 -std=gnu99'
26 | RUSTC_FLAGS = '--'
27 |
28 | if len(sys.argv) < 4:
29 | error("e.g. ./run.py exhaust 500M 5");
30 |
31 | feature = sys.argv[1]
32 | all_features = ["exhaust", "initobj", "gcbench", "mark", "trace", "mt-gcbench"]
33 | if feature not in all_features :
34 | error("unsupported feature")
35 |
36 | heap = sys.argv[2]
37 | invocations = sys.argv[3]
38 |
39 | bm_args = ''
40 | if len(sys.argv) > 4 and sys.argv[4] == '--':
41 | bm_args = ' '.join(sys.argv[5:])
42 |
43 | print 'benchmark specific args: ' + bm_args
44 |
45 | print '###feature = ' + feature
46 | print '###heap = ' + heap
47 | print '###invocations = ' + invocations
48 |
49 | print 'rsync...'
50 | remote_path = "{}@{}:{}".format(REMOTE_USER, MACHINE, RSYNC_DIR)
51 | ret = subprocess.call(["rsync", "-rav", "-I", "--delete", "--exclude=target", "--exclude=.hg", ".", remote_path])
52 | if ret != 0:
53 | error('failed to rsync')
54 |
55 | for invocation in range(0, int(invocations)):
56 | print 'Invocation %d' % (invocation)
57 | print
58 |
59 | print '===Rust Version==='
60 | build = ''
61 | if feature == 'gcbench' or feature == 'mt-gcbench':
62 | build = 'ssh -t {}@{} "cd {}; rm -r target; cargo rustc --release --features \"{}\" {}; HEAP_SIZE={} N_GCTHREADS=7 ./target/release/immix-rust 7"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, feature, RUSTC_FLAGS, heap, bm_args)
63 | else:
64 | build = 'ssh -t {}@{} "cd {}; rm -r target; cargo rustc --release --features \"{}\" {}; HEAP_SIZE={} N_GCTHREADS=0 ./target/release/immix-rust"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, feature, RUSTC_FLAGS, heap, bm_args)
65 | succ = monitor_invoke(build)
66 | if not succ:
67 | error('failed to execute rust code')
68 | print
69 |
70 | print '===C Version==='
71 | build = 'ssh -t {}@{} "cd {}c_src; clang-3.7 {} *.c ../src/common/perf.c -lpfm -o bench -D{}; HEAP_SIZE={} ./bench {}"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, C_FLAGS, feature, heap, bm_args)
72 | succ = monitor_invoke(build)
73 | if not succ:
74 | error('failed to execute c code')
75 | print
76 |
77 | print '===Boehm GC==='
78 | if feature == "exhaust":
79 | build = 'ssh -t {}@{} "cd {}../bdwgc/gctests/; clang-3.7 -I ../include exhaust.c ../*.o {} -DGC -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK -o exhaust -l pthread; GC_INITIAL_HEAP_SIZE={} GC_MAXIMUM_HEAP_SIZE={} ./exhaust"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, C_FLAGS, heap, heap)
80 | elif feature == "gcbench":
81 | build = 'ssh -t {}@{} "cd {}../bdwgc/gctests/; clang-3.7 -I ../include gcbench.c ../*.o {} -DGC -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK -o gcbench -l pthread; GC_INITIAL_HEAP_SIZE={} GC_MAXIMUM_HEAP_SIZE={} ./gcbench"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, C_FLAGS, heap, heap)
82 | elif feature == "mt-gcbench":
83 | build = 'ssh -t {}@{} "cd {}../bdwgc/gctests/; clang-3.7 -I ../include mt-gcbench.c ../*.o {} -DGC -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK -DNTHREADS=7 -o mt-gcbench -l pthread; GC_INITIAL_HEAP_SIZE={} GC_MAXIMUM_HEAP_SIZE={} ./mt-gcbench"'.format(REMOTE_USER, MACHINE, RSYNC_DIR, C_FLAGS, heap, heap, bm_args)
84 | else:
85 | build = ''
86 | print '"{}" not applicable to bdwgc'.format(feature);
87 | if build != '':
88 | succ = monitor_invoke(build)
89 | if not succ:
90 | error('failed to execute boehm gc code')
91 | print
92 |
93 | print 'Invocation Finish'
94 |
--------------------------------------------------------------------------------
/examples/immix/runme:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for f in `cat features`; do
4 | echo $f
5 | cargo run --release --features "$f"
6 | done
7 |
8 |
--------------------------------------------------------------------------------
/examples/immix/runone:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Add the path of the executable built by stack to PATH instead of installing it:
4 | export PATH="$(pwd)/$(dirname $(find ../../.stack-work -path */flp/flp)):$PATH"
5 | cargo run --release --features "$@"
6 |
7 |
--------------------------------------------------------------------------------
/examples/immix/rust_c_interface/immix_rust.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | // the layout of this struct needs to be the same as src/heap/immix/immix_mutator.rs
6 | struct Mutator {
7 | uint64_t id;
8 | void* alloc_map;
9 | uint64_t space_start;
10 | uint64_t cursor;
11 | uint64_t limit;
12 | uint64_t line;
13 |
14 | bool* yield;
15 | // we do not care about the rest
16 | };
17 |
18 | extern void gc_init(uint64_t immix_size, uint64_t lo_size, uint64_t n_gcthreads);
19 | extern void set_low_water_mark();
20 | extern struct Mutator* new_mutator();
21 | extern void drop_mutator(struct Mutator* mutator);
22 | extern void yieldpoint_slow(struct Mutator** mutator);
23 | extern uint64_t alloc_slow(struct Mutator** mutator, uint64_t size, uint64_t align);
24 | extern uint64_t alloc_large(struct Mutator** mutator, uint64_t size);
25 |
26 | inline void yieldpoint(bool* take_yield, struct Mutator** m) __attribute__((always_inline));
27 | inline void yieldpoint(bool* take_yield, struct Mutator** m) {
28 | if (*take_yield) {
29 | yieldpoint_slow(m);
30 | }
31 | }
32 |
33 | inline uint64_t align_up(uint64_t addr, uint64_t align) __attribute__((always_inline));
34 | inline uint64_t align_up(uint64_t addr, uint64_t align) {
35 | return (addr + align - 1) & ~(align - 1);
36 | }
37 |
38 | inline uint64_t alloc(struct Mutator** mutator, uint64_t size, uint64_t align) __attribute__((always_inline));
39 | inline uint64_t alloc(struct Mutator** mutator, uint64_t size, uint64_t align) {
40 | struct Mutator* self = *mutator;
41 | uint64_t start = align_up(self->cursor, align);
42 | uint64_t end = start + size;
43 |
44 | if (end > self->limit)
45 | return alloc_slow(mutator, size, align);
46 | else {
47 | self->cursor = end;
48 | return start;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/examples/immix/rust_c_interface/test.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "immix_rust.h"
5 |
6 | #define N_ALLOCATION_THREADS 5
7 |
8 | void* new_allocation_thread(void*);
9 | void allocation_once(struct Mutator* m);
10 | void allocation_loop(struct Mutator* m);
11 |
12 | int main() {
13 | // init gc with size of immix space and large object space, and number of gc threads
14 | gc_init(1 << 20, 1 << 20, 8);
15 |
16 | pthread_t threads[N_ALLOCATION_THREADS];
17 | int i;
18 |
19 | for (i = 0; i < N_ALLOCATION_THREADS; i++) {
20 | pthread_create(&threads[i], NULL, new_allocation_thread, NULL);
21 | }
22 |
23 | for (i = 0; i < N_ALLOCATION_THREADS; i++) {
24 | pthread_join(threads[i], NULL);
25 | }
26 | }
27 |
28 | void* new_allocation_thread(void* arg) {
29 | // creating a new mutator
30 | struct Mutator* m = new_mutator();
31 |
32 | // set current stack pointer as a limit for conservative stack scanning
33 | // (stack scanning won't traverse beyond the limit)
34 | set_low_water_mark();
35 |
36 | // main allocation loop
37 | allocation_loop(m);
38 |
39 | // if the allocation finishes, we destroy the mutator
40 | drop_mutator(m);
41 |
42 | return NULL;
43 | }
44 |
45 | void print_mutator(struct Mutator* m) {
46 | printf("mutator:\n");
47 | printf("id =%lld\n", m->id);
48 | printf("space =0x%llx\n", m->space_start);
49 | printf("cursor=0x%llx\n", m->cursor);
50 | printf("limit =0x%llx\n", m->limit);
51 | printf("yield?=%d\n", *(m->yield));
52 | }
53 |
54 | void allocation_once(struct Mutator* m) {
55 | print_mutator(m);
56 | uint64_t addr = alloc(&m, 32, 8);
57 | printf("RETURN1 = %llx\n", addr);
58 | print_mutator(m);
59 | addr = alloc(&m, 32, 8);
60 | printf("RETURN2 = %llx\n", addr);
61 | print_mutator(m);
62 | }
63 |
64 | void allocation_loop(struct Mutator* m) {
65 | bool* yield = m->yield;
66 | struct Mutator** m_ref = &m;
67 |
68 | while(1) {
69 | // yieldpoint
70 | yieldpoint(yield, m_ref);
71 |
72 | uint64_t addr = alloc(&m, 32, 8);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/examples/immix/src/exhaust.rs:
--------------------------------------------------------------------------------
1 | extern crate libc;
2 | extern crate time;
3 |
4 | use heap;
5 | use heap::immix::ImmixMutatorLocal;
6 | use heap::immix::ImmixSpace;
7 | use heap::freelist::FreeListSpace;
8 |
9 | use std::sync::RwLock;
10 |
11 | pub const OBJECT_SIZE : usize = 24;
12 | pub const OBJECT_ALIGN: usize = 8;
13 |
14 | pub const ALLOCATION_TIMES : usize = 50000000;
15 |
16 | #[allow(unused_variables)]
17 | pub fn exhaust_alloc() {
18 | use std::sync::{Arc};
19 | use std::sync::atomic::Ordering;
20 |
21 | let shared_space : Arc = {
22 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
23 |
24 | Arc::new(space)
25 | };
26 | let lo_space : Arc> = {
27 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
28 | Arc::new(RwLock::new(space))
29 | };
30 | heap::gc::init(shared_space.clone(), lo_space.clone());
31 |
32 | let mut mutator = ImmixMutatorLocal::new(shared_space.clone());
33 |
34 | println!("Trying to allocate {} objects of (size {}, align {}). ", ALLOCATION_TIMES, OBJECT_SIZE, OBJECT_ALIGN);
35 | const ACTUAL_OBJECT_SIZE : usize = OBJECT_SIZE;
36 | println!("Considering header size of {}, an object should be {}. ", 0, ACTUAL_OBJECT_SIZE);
37 | println!("This would take {} bytes of {} bytes heap", ALLOCATION_TIMES * ACTUAL_OBJECT_SIZE, heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
38 |
39 | alloc_loop(&mut mutator);
40 | }
41 |
42 | #[inline(never)]
43 | fn alloc_loop(mutator: &mut ImmixMutatorLocal) {
44 | let t_start = time::Instant::now();
45 |
46 | for _ in 0..ALLOCATION_TIMES {
47 | // mutator.yieldpoint();
48 |
49 | let res = mutator.alloc(OBJECT_SIZE, OBJECT_ALIGN);
50 | mutator.init_object(res, 0b000011_11);
51 | }
52 |
53 | let t_end = time::Instant::now();
54 |
55 | println!("time used: {} msec", (t_end - t_start).whole_milliseconds());
56 | }
57 |
--------------------------------------------------------------------------------
/examples/immix/src/gcbench.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_upper_case_globals)]
2 | #![allow(non_camel_case_types)]
3 | #![allow(non_snake_case)]
4 |
5 | #![allow(unused_variables)]
6 |
7 | use heap;
8 | use heap::layout::*;
9 | use heap::immix::ImmixMutatorLocal;
10 | use heap::immix::ImmixSpace;
11 | use heap::freelist;
12 | use heap::freelist::FreeListSpace;
13 | use std::mem::size_of as size_of;
14 |
15 | extern crate time;
16 |
17 | const kStretchTreeDepth : i32 = 18;
18 | const kLongLivedTreeDepth : i32 = 16;
19 | const kArraySize : i32 = 5000;
20 | const kMinTreeDepth : i32 = 4;
21 | const kMaxTreeDepth : i32 = 16;
22 |
23 | struct Node {
24 | left : *mut Node,
25 | right : *mut Node,
26 | i : i32,
27 | j : i32
28 | }
29 |
30 | struct Array {
31 | value : [f64; kArraySize as usize]
32 | }
33 |
34 | fn init_Node(me: *mut Node, l: *mut Node, r: *mut Node) {
35 | unsafe {
36 | (*me).left = l;
37 | (*me).right = r;
38 | }
39 | }
40 |
41 | fn TreeSize(i: i32) -> i32{
42 | (1 << (i + 1)) - 1
43 | }
44 |
45 | fn NumIters(i: i32) -> i32 {
46 | 2 * TreeSize(kStretchTreeDepth) / TreeSize(i)
47 | }
48 |
49 | fn Populate(iDepth: i32, thisNode: *mut Node, mutator: &mut ImmixMutatorLocal) {
50 | if iDepth <= 0 {
51 | return;
52 | } else {
53 | unsafe {
54 | (*thisNode).left = alloc(mutator);
55 | (*thisNode).right = alloc(mutator);
56 | Populate(iDepth - 1, (*thisNode).left, mutator);
57 | Populate(iDepth - 1, (*thisNode).right, mutator);
58 | }
59 | }
60 | }
61 |
62 | fn MakeTree(iDepth: i32, mutator: &mut ImmixMutatorLocal) -> *mut Node {
63 | if iDepth <= 0 {
64 | alloc(mutator)
65 | } else {
66 | let left = MakeTree(iDepth - 1, mutator);
67 | let right = MakeTree(iDepth - 1, mutator);
68 | let result = alloc(mutator);
69 | init_Node(result, left, right);
70 |
71 | result
72 | }
73 | }
74 |
75 | fn PrintDiagnostics() {
76 |
77 | }
78 |
79 | fn TimeConstruction(depth: i32, mutator: &mut ImmixMutatorLocal) {
80 | let iNumIters = NumIters(depth);
81 | println!("creating {} trees of depth {}", iNumIters, depth);
82 |
83 | let tStart = time::Instant::now();
84 | for _ in 0..iNumIters {
85 | let tempTree = alloc(mutator);
86 | Populate(depth, tempTree, mutator);
87 |
88 | // destroy tempTree
89 | }
90 | let tFinish = time::Instant::now();
91 | println!("\tTop down construction took {} msec", (tFinish - tStart).whole_milliseconds());
92 |
93 | let tStart = time::Instant::now();
94 | for _ in 0..iNumIters {
95 | let tempTree = MakeTree(depth, mutator);
96 | }
97 | let tFinish = time::Instant::now();
98 | println!("\tBottom up construction took {} msec", (tFinish - tStart).whole_milliseconds());
99 | }
100 |
101 | #[inline(always)]
102 | fn alloc(mutator: &mut ImmixMutatorLocal) -> *mut Node {
103 | let addr = mutator.alloc(size_of::(), 8);
104 | mutator.init_object(addr, 0b000011_11);
105 | // objectmodel::init_header(addr, HEADER_INIT_U64);
106 | addr.to_ptr_mut::()
107 | }
108 |
109 | pub fn start() {
110 | use std::sync::{Arc, RwLock};
111 | use std::sync::atomic::Ordering;
112 |
113 | unsafe {heap::gc::set_low_water_mark();}
114 |
115 | let immix_space : Arc = {
116 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
117 | Arc::new(space)
118 | };
119 | let lo_space : Arc> = {
120 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
121 | Arc::new(RwLock::new(space))
122 | };
123 | heap::gc::init(immix_space.clone(), lo_space.clone());
124 | let mut mutator = ImmixMutatorLocal::new(immix_space.clone());
125 |
126 | println!("Garbage Collector Test");
127 | println!(" Live storage will peak at {} bytes.\n",
128 | 2 * (size_of::() as i32) * TreeSize(kLongLivedTreeDepth) +
129 | (size_of::() as i32));
130 |
131 | println!(" Stretching memory with a binary tree or depth {}", kStretchTreeDepth);
132 | PrintDiagnostics();
133 |
134 | let tStart = time::Instant::now();
135 | // Stretch the memory space quickly
136 | let tempTree = MakeTree(kStretchTreeDepth, &mut mutator);
137 | // destroy tree
138 |
139 | // Create a long lived object
140 | println!(" Creating a long-lived binary tree of depth {}", kLongLivedTreeDepth);
141 | let longLivedTree = alloc(&mut mutator);
142 | Populate(kLongLivedTreeDepth, longLivedTree, &mut mutator);
143 |
144 | println!(" Creating a long-lived array of {} doubles", kArraySize);
145 | freelist::alloc_large(size_of::(), 8, &mut mutator, lo_space.clone());
146 |
147 | PrintDiagnostics();
148 |
149 | let mut d = kMinTreeDepth;
150 | while d <= kMaxTreeDepth {
151 | TimeConstruction(d, &mut mutator);
152 | d += 2;
153 | }
154 |
155 | if longLivedTree.is_null() {
156 | println!("Failed(long lived tree wrong)");
157 | }
158 |
159 | // if array.array[1000] != 1.0f64 / (1000 as f64) {
160 | // println!("Failed(array element wrong)");
161 | // }
162 |
163 | let tFinish = time::Instant::now();
164 | let tElapsed = (tFinish - tStart).whole_milliseconds();
165 |
166 | PrintDiagnostics();
167 | println!("Completed in {} msec", tElapsed);
168 | println!("Finished with {} collections", heap::gc::GC_COUNT.load(Ordering::SeqCst));
169 | }
170 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/.gitignore:
--------------------------------------------------------------------------------
1 | /layout.rs
2 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/debug.rs:
--------------------------------------------------------------------------------
1 | extern crate backtrace;
2 |
3 | macro_rules! backtraceHere {
4 | ( $val:expr ) => {
5 | { use std::fmt::Write;
6 | let bt = &backtrace::Backtrace::new();
7 | let frames_count = bt.frames().len();
8 | let frames = &bt.frames();
9 | for i in 2..frames_count {
10 | let symb = &frames[i].symbols()[0];
11 | if let Some(name) = symb.name() {
12 | if let Some(ln) = symb.lineno() {
13 | let mut output = String::new();
14 | let _ = write!(&mut output, "{}", name);
15 | if output.starts_with("std::rt") { break; }
16 | eprintln!(" {}:{}] = 0x{:X}", name, ln, $val);
17 | }
18 | }
19 | }
20 | }
21 | };
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/freelist/mod.rs:
--------------------------------------------------------------------------------
1 | use heap::layout::*;
2 | use heap::immix;
3 |
4 | extern crate aligned_alloc;
5 |
6 | use std::collections::LinkedList;
7 | use std::sync::Arc;
8 | use std::sync::RwLock;
9 |
10 | pub struct FreeListSpace {
11 | current_nodes : LinkedList>,
12 |
13 | node_id: usize,
14 |
15 | size : usize,
16 | used_bytes : usize
17 | }
18 |
19 | impl FreeListSpace {
20 | pub fn new(size: usize) -> FreeListSpace {
21 | FreeListSpace {
22 | current_nodes: LinkedList::new(),
23 | node_id: 0,
24 | size: size,
25 | used_bytes: 0
26 | }
27 | }
28 |
29 | pub fn mark(&mut self, _obj: CellAddr) {
30 |
31 | }
32 |
33 | pub fn alloc(&mut self, size: usize, align: usize) -> Option {
34 | if self.used_bytes + size > self.size {
35 | None
36 | } else {
37 | let ret = self::aligned_alloc::aligned_alloc(BYTES_IN_WORD + size, align);
38 |
39 | let addr = CellAddr::from_ptr::<()>(ret);
40 |
41 | self.current_nodes.push_front(Box::new(FreeListNode{id: self.node_id, start: addr, size: size, mark: NodeMark::FreshAlloc}));
42 | self.node_id += 1;
43 | self.used_bytes += size;
44 |
45 | addr.cell_size().store_word(size);
46 | Some(addr.object())
47 | }
48 | }
49 |
50 | pub fn sweep(&mut self) {
51 | let (new_nodes, new_used_bytes) = {
52 | let mut ret = LinkedList::new();
53 | let nodes = &mut self.current_nodes;
54 | let mut used_bytes = 0;
55 |
56 | while !nodes.is_empty() {
57 | let mut node = nodes.pop_front().unwrap();
58 | match node.mark {
59 | NodeMark::Live => {
60 | //let refBitsA : RefBitsAddr =
61 | // Word2RefBits::map_getAddr(
62 | // self.start.get_first_word(),
63 | // self.alloc_map.toStart,
64 | // WordAddr::from_usize(line_addr.as_usize() + j));
65 | //check_expect_pc!(refBitsA.as_usize(),
66 |
67 | node.set_mark(NodeMark::PrevLive);
68 | used_bytes += node.size;
69 | ret.push_back(node);
70 | },
71 | NodeMark::PrevLive | NodeMark::FreshAlloc => {
72 | let ptr = node.start.to_ptr::<()>() as *mut ();
73 | // free the memory
74 | unsafe {self::aligned_alloc::aligned_free(ptr);}
75 | // do not add this node into new linked list
76 | }
77 | }
78 | }
79 |
80 | (ret, used_bytes)
81 | };
82 |
83 | self.current_nodes = new_nodes;
84 | self.used_bytes = new_used_bytes;
85 | }
86 |
87 | pub fn current_nodes(&self) -> &LinkedList> {
88 | &self.current_nodes
89 | }
90 | pub fn current_nodes_mut(&mut self) -> &mut LinkedList> {
91 | &mut self.current_nodes
92 | }
93 | }
94 |
95 | pub struct FreeListNode {
96 | id: usize,
97 | start : CellAddr,
98 | size : usize,
99 | mark : NodeMark
100 | }
101 |
102 | impl FreeListNode {
103 | pub fn set_mark(&mut self, mark: NodeMark) {
104 | self.mark = mark;
105 | }
106 | }
107 |
108 | #[derive(PartialEq, Eq, Copy, Clone, Debug)]
109 | pub enum NodeMark {
110 | FreshAlloc,
111 | PrevLive,
112 | Live,
113 | }
114 | unsafe impl Sync for NodeMark {}
115 |
116 | #[inline(never)]
117 | pub fn alloc_large(size: usize, align: usize, mutator: &mut immix::ImmixMutatorLocal, space: Arc>) -> ObjectAddr {
118 | loop {
119 | mutator.yieldpoint();
120 |
121 | let ret_addr = {
122 | let mut lo_space_lock = space.write().unwrap();
123 | lo_space_lock.alloc(size, align)
124 | };
125 |
126 | match ret_addr {
127 | Some(addr) => {
128 | return addr;
129 | },
130 | None => {
131 | use heap::gc;
132 | gc::trigger_gc();
133 | }
134 | }
135 | }
136 | }
137 |
138 | use std::fmt;
139 |
140 | impl fmt::Display for FreeListSpace {
141 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 | write!(f, "FreeListSpace\n").unwrap();
143 | write!(f, "{} used, {} total\n", self.used_bytes, self.size).unwrap();
144 | write!(f, "nodes:\n").unwrap();
145 |
146 | for node in self.current_nodes() {
147 | write!(f, " {}\n", node).unwrap();
148 | }
149 |
150 | write!(f, "done\n")
151 | }
152 | }
153 |
154 | impl fmt::Display for FreeListNode {
155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 | write!(f, "FreeListNode#{}(start={:#X}, size={}, state={:?})", self.id, self.start, self.size, self.mark)
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/gc/clib_x64.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | void* malloc_zero(size_t size) {
6 | void* ret = malloc(size);
7 | memset(ret, 0, size);
8 | return ret;
9 | }
10 |
11 |
12 |
13 | uintptr_t immmix_get_stack_ptr();
14 | uintptr_t immmix_get_stack_ptr() {
15 | uintptr_t rsp;
16 | // get current rsp, rbp (this C func frame)
17 | __asm__(
18 | "mov %%rsp, %0 \n"
19 | : "=rm" (rsp)
20 | );
21 |
22 | return rsp;
23 | }
24 |
25 | int get_registers_count() {
26 | return 16;
27 | }
28 |
29 | uintptr_t* get_registers () {
30 | uintptr_t rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15;
31 |
32 | __asm__(
33 | "mov %%rax, %0 \n"
34 | "mov %%rbx, %1 \n"
35 | "mov %%rcx, %2 \n"
36 | "mov %%rdx, %3 \n"
37 | "mov %%rbp, %4 \n"
38 | "mov %%rsp, %5 \n"
39 | "mov %%rsi, %5 \n"
40 | "mov %%rdi, %6 \n"
41 | "mov %%r8, %7 \n"
42 | "mov %%r9, %8 \n"
43 | "mov %%r10, %10\n"
44 | "mov %%r11, %11\n"
45 | "mov %%r12, %12\n"
46 | "mov %%r13, %13\n"
47 | "mov %%r14, %14\n"
48 | "mov %%r15, %15\n"
49 | : "=m" (rax),
50 | "=m" (rbx),
51 | "=m" (rcx),
52 | "=m" (rdx),
53 | "=m" (rbp),
54 | "=m" (rsp),
55 | "=m" (rsi),
56 | "=m" (rdi),
57 | "=m" (r8),
58 | "=m" (r9),
59 | "=m" (r10),
60 | "=m" (r11),
61 | "=m" (r12),
62 | "=m" (r13),
63 | "=m" (r14),
64 | "=m" (r15)
65 | :
66 | :
67 | );
68 |
69 | uintptr_t* ret = (uintptr_t*) malloc(sizeof(uintptr_t) * 16);
70 | ret[0] = rax;
71 | ret[1] = rbx;
72 | ret[2] = rcx;
73 | ret[3] = rdx;
74 | ret[4] = rbp;
75 | ret[5] = rsp;
76 | ret[6] = rsi;
77 | ret[7] = rdi;
78 | ret[8] = r8;
79 | ret[9] = r9;
80 | ret[10] = r10;
81 | ret[11] = r11;
82 | ret[12] = r12;
83 | ret[13] = r13;
84 | ret[14] = r14;
85 | ret[15] = r15;
86 | return ret;
87 | }
88 |
89 | __thread uintptr_t low_water_mark;
90 |
91 | void set_low_water_mark () {
92 | uintptr_t rsp;
93 | // get current rsp, rbp (this C func frame)
94 | __asm__(
95 | "mov %%rsp, %0 \n"
96 | : "=rm" (rsp)
97 | );
98 |
99 | low_water_mark = rsp;
100 | }
101 |
102 | uintptr_t get_low_water_mark() {
103 | return low_water_mark;
104 | }
105 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/immix/mod.rs:
--------------------------------------------------------------------------------
1 | mod immix_space;
2 | mod immix_mutator;
3 |
4 | pub use self::immix_space::ImmixSpace;
5 | pub use self::immix_mutator::ImmixMutatorLocal;
6 | pub use self::immix_mutator::ImmixMutatorGlobal;
7 | pub use self::immix_space::LineMarkTable as ImmixLineMarkTable;
8 | pub use self::immix_mutator::MUTATORS;
9 | pub use self::immix_mutator::N_MUTATORS;
10 |
11 | pub use heap::layout::*;
12 |
13 | use std::sync::Arc;
14 | use std::sync::RwLock;
15 |
16 | lazy_static!{
17 | pub static ref SHARED_SPACE : Option>> = None;
18 | }
19 |
20 | pub const BYTES_IN_LINE : usize = 1 << LOG_BYTES_IN_LINE;
21 | pub const BYTES_IN_BLOCK : usize = 1 << LOG_BYTES_IN_BLOCK;
22 | pub const LINES_IN_BLOCK : usize = 1 << (LOG_BYTES_IN_BLOCK - LOG_BYTES_IN_LINE);
23 |
24 | #[derive(PartialEq, Eq, Debug, Copy, Clone)]
25 | pub enum LineMark {
26 | Free = 4, // Marked upon initialization and during sweep upon finding non-Live / non-ConservLive line.
27 | Live = 3, // Marked as such upon allocation
28 | FreshAlloc = 2, // try_alloc_from_local() found an "available" line and allocated it
29 | ConservLive = 1 // Next line of an allocation marked as such
30 | }
31 |
32 | impl From for LineMark {
33 | fn from(val : u8) -> LineMark {
34 | if val >= 5 || val == 0 {
35 | eprintln!("Invalid LineMark::from({})", val);
36 | LineMark::ConservLive
37 | }
38 | // Duplicate ConservLive below is on purpose:
39 | else { [LineMark::ConservLive, LineMark::ConservLive, LineMark::FreshAlloc, LineMark::Live, LineMark::Free][val as usize] }
40 | }
41 | }
42 |
43 | #[derive(PartialEq, Eq, Debug, Copy, Clone)]
44 | pub enum BlockMark {
45 | Usable,
46 | Full
47 | }
48 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/layout.flp:
--------------------------------------------------------------------------------
1 | Region -> seq {
2 | Space @(2^19 bytes)@ -> union {
3 | num_blocks union {
4 | FreeBlock @(2^16 bytes)@ -> seq {
5 | 2^16 bytes }
6 | | Block ||2^16 bytes|| @(2^16 bytes)@
7 | contains(Line) -> seq {
8 | cells : # union {
9 | FreeCell @(1 words)@ -> # words
10 | | Cell },
11 | remainder : # words,
12 | limit : 0 words } }
13 | | wrds words
14 | | lines Line @|2^8 bytes|@
15 | contains(Cell) -> # bytes },
16 | lms : lines LineMark,
17 | refs : wrds RefBits,
18 | mks : wrds MarkBits }
19 | Cell @(1 words)@ contains(Word) -> union {
20 | seq { cell_size : 1 words,
21 | Object -> seq {
22 | ptr_0 : Object ptr, ptr_1 : Object ptr,
23 | ptr_2 : Object ptr, ptr_3 : Object ptr,
24 | payload : # words } }
25 | | # words }
26 | RefBits ||1 bytes|| -> bits {
27 | SHORT_ENCODE : 1 bits,
28 | OBJ_START : 1 bits,
29 | REF : 6 bits }
30 | LineMark -> enum { Free | Live | FreshAlloc
31 | | ConservLive | PrevLive }
32 | MarkBits ||1 bytes|| -> bits { MARK : 8 bits }
33 | Stk -> seq { stack : # seq { Object ptr },
34 | lowWater : 0 words }
35 | Registers -> seq { regs : # seq { Object ptr },
36 | regsEnd : 0 words }
37 | AppObject -> union {
38 | LLNode -> 3 words
39 | | QNode -> 2 words
40 | | Dequeue -> 2 words
41 | | # words }
42 | Garbage -> # words
43 |
--------------------------------------------------------------------------------
/examples/immix/src/heap/mod.rs:
--------------------------------------------------------------------------------
1 | use std::sync::atomic::AtomicUsize;
2 | #[macro_use]
3 | pub mod debug;
4 |
5 | #[macro_use]
6 | pub mod layout;
7 | use self::layout::*;
8 |
9 | pub mod immix;
10 | pub mod freelist;
11 | pub mod gc;
12 |
13 | pub const ALIGNMENT_VALUE : u8 = 1;
14 |
15 | pub const IMMIX_SPACE_RATIO : f64 = 1.0 - LO_SPACE_RATIO;
16 | pub const LO_SPACE_RATIO : f64 = 0.2;
17 | pub const DEFAULT_HEAP_SIZE : usize = 500 << 20;
18 |
19 | lazy_static! {
20 | pub static ref IMMIX_SPACE_SIZE : AtomicUsize = AtomicUsize::new( (DEFAULT_HEAP_SIZE as f64 * IMMIX_SPACE_RATIO) as usize );
21 | pub static ref LO_SPACE_SIZE : AtomicUsize = AtomicUsize::new( (DEFAULT_HEAP_SIZE as f64 * LO_SPACE_RATIO) as usize );
22 | }
23 |
24 | #[inline(always)]
25 | pub fn fill_alignment_gap(start : RemainderAddr, end : LimitAddr) -> () {
26 | Block::memset_remainder_until_limit(ALIGNMENT_VALUE, start, end);
27 | }
28 |
--------------------------------------------------------------------------------
/examples/immix/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(const_fn)]
2 | #![allow(dead_code)]
3 | use std::sync::atomic::Ordering;
4 |
5 | #[macro_use]
6 | extern crate lazy_static;
7 | #[macro_use]
8 | extern crate log;
9 | extern crate simple_logger;
10 | extern crate libc;
11 |
12 | pub mod objectmodel;
13 |
14 | #[macro_use]
15 | pub mod heap;
16 |
17 | mod leakme;
18 |
19 | pub use heap::immix::ImmixMutatorLocal as Mutator;
20 | use std::sync::Arc;
21 | use std::sync::RwLock;
22 | use heap::immix::ImmixSpace;
23 | use heap::immix::ImmixMutatorLocal;
24 | use heap::freelist;
25 | use heap::freelist::FreeListSpace;
26 | use std::boxed::Box;
27 | use heap::layout::*;
28 |
29 | #[repr(C)]
30 | pub struct GC {
31 | immix_space: Arc,
32 | lo_space : Arc>
33 | }
34 |
35 | lazy_static! {
36 | pub static ref MY_GC : RwLock> = RwLock::new(None);
37 | }
38 |
39 | pub extern fn gc_init(immix_size: usize, lo_size: usize, n_gcthreads: usize) {
40 | // set this line to turn on certain level of debugging info
41 | // simple_logger::init_with_level(log::LogLevel::Trace).ok();
42 |
43 | // init space size
44 | heap::IMMIX_SPACE_SIZE.store(immix_size, Ordering::SeqCst);
45 | heap::LO_SPACE_SIZE.store(lo_size, Ordering::SeqCst);
46 |
47 | let (immix_space, lo_space) = {
48 | let immix_space = Arc::new(ImmixSpace::new(immix_size));
49 | let lo_space = Arc::new(RwLock::new(FreeListSpace::new(lo_size)));
50 |
51 | heap::gc::init(immix_space.clone(), lo_space.clone());
52 |
53 | (immix_space, lo_space)
54 | };
55 |
56 | *MY_GC.write().unwrap() = Some(GC {immix_space: immix_space, lo_space: lo_space});
57 | println!("heap is {} bytes (immix: {} bytes, lo: {} bytes) . ", immix_size + lo_size, immix_size, lo_size);
58 |
59 | // gc threads
60 | heap::gc::GC_THREADS.store(n_gcthreads, Ordering::SeqCst);
61 | println!("{} gc threads", n_gcthreads);
62 |
63 | // init object model
64 | objectmodel::init();
65 | }
66 |
67 | pub extern fn new_mutator() -> Box {
68 | Box::new(ImmixMutatorLocal::new(MY_GC.read().unwrap().as_ref().unwrap().immix_space.clone()))
69 | }
70 |
71 | #[allow(unused_variables)]
72 | pub extern fn drop_mutator(mutator: Box) {
73 | // rust will reclaim the boxed mutator
74 | }
75 |
76 | #[cfg(target_arch = "x86_64")]
77 | extern "C" {
78 | pub fn set_low_water_mark();
79 | }
80 |
81 | #[inline(always)]
82 | pub extern fn yieldpoint(mutator: &mut Box) {
83 | mutator.yieldpoint();
84 | }
85 |
86 | #[inline(never)]
87 | pub extern fn yieldpoint_slow(mutator: &mut Box) {
88 | mutator.yieldpoint_slow()
89 | }
90 |
91 | #[inline(always)]
92 | pub extern fn alloc(mutator: &mut Box, size: usize, align: usize) -> ObjectAddr {
93 | mutator.alloc(size, align)
94 | }
95 |
96 | pub extern fn alloc_slow(mutator: &mut Box, size: usize, align: usize) -> ObjectAddr {
97 | mutator.try_alloc_from_local(size, align)
98 | }
99 |
100 | pub extern fn alloc_large(mutator: &mut Box, size: usize) -> ObjectAddr {
101 | freelist::alloc_large(size, 8, mutator, MY_GC.read().unwrap().as_ref().unwrap().lo_space.clone())
102 | }
103 |
--------------------------------------------------------------------------------
/examples/immix/src/main.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 | #![allow(dead_code)]
3 | use std::env;
4 | use std::sync::atomic::Ordering;
5 |
6 | #[macro_use]
7 | extern crate lazy_static;
8 | #[macro_use]
9 | extern crate log;
10 |
11 | mod objectmodel;
12 |
13 | #[macro_use]
14 | mod heap;
15 |
16 | mod exhaust;
17 | mod mark;
18 | mod trace;
19 | mod mt_trace;
20 | mod gcbench;
21 | mod mt_gcbench;
22 | mod obj_init;
23 | pub mod leakme;
24 | pub mod testme;
25 |
26 | fn init() {
27 | objectmodel::init();
28 | }
29 |
30 | fn main() {
31 | init();
32 |
33 | match env::var("HEAP_SIZE") {
34 | Ok(val) => {
35 | if val.ends_with("M") {
36 | let (num, _) = val.split_at(val.len() - 1);
37 | let heap_size = num.parse::().unwrap() << 20;
38 |
39 | let immix_space_size : usize = (heap_size as f64 * heap::IMMIX_SPACE_RATIO) as usize;
40 | heap::IMMIX_SPACE_SIZE.store(immix_space_size, Ordering::SeqCst);
41 |
42 | let lo_space_size : usize = (heap_size as f64 * heap::LO_SPACE_RATIO) as usize;
43 | heap::LO_SPACE_SIZE.store(lo_space_size, Ordering::SeqCst);
44 |
45 | println!("heap is {} bytes (immix: {} bytes, lo: {} bytes) . ", heap_size, immix_space_size, lo_space_size);
46 | } else {
47 | println!("unknow heap size variable: {}, ignore", val);
48 | println!("using default heap size: {} bytes. ", heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
49 | }
50 | },
51 | Err(_) => {
52 | println!("using default heap size: {} bytes. ", heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
53 | }
54 | }
55 |
56 | match env::var("N_GCTHREADS") {
57 | Ok(val) => {
58 | heap::gc::GC_THREADS.store(val.parse::().unwrap(), Ordering::SeqCst);
59 | },
60 | Err(_) => {
61 | heap::gc::GC_THREADS.store(8, Ordering::SeqCst);
62 | }
63 | }
64 |
65 | if cfg!(feature = "exhaust") {
66 | println!("exhaust"); exhaust::exhaust_alloc();
67 | } else if cfg!(feature = "initobj") {
68 | println!("initobj"); obj_init::alloc_init();
69 | } else if cfg!(feature = "gcbench") {
70 | println!("gcbench"); gcbench::start();
71 | } else if cfg!(feature = "mt-gcbench") {
72 | println!("mt-gcbench"); mt_gcbench::start();
73 | } else if cfg!(feature = "mark") {
74 | println!("mark"); mark::alloc_mark();
75 | } else if cfg!(feature = "trace") {
76 | println!("trace"); trace::alloc_trace();
77 | } else if cfg!(feature = "mt-trace") {
78 | println!("mt-trace"); mt_trace::alloc_trace();
79 | } else if cfg!(feature = "leakme") {
80 | println!("leakme"); leakme::start();
81 | } else if cfg!(feature = "testme") {
82 | println!("testme"); testme::start();
83 | } else {
84 | println!("unknown features: build with 'cargo build --release --features \"exhaust\"");
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/examples/immix/src/mark.rs:
--------------------------------------------------------------------------------
1 | extern crate time;
2 |
3 | use heap;
4 | use heap::layout::*;
5 | use heap::immix::ImmixMutatorLocal;
6 | use heap::immix::ImmixSpace;
7 | use heap::freelist::FreeListSpace;
8 |
9 | use std::sync::RwLock;
10 | use std::sync::{Arc};
11 | use std::sync::atomic::Ordering;
12 |
13 | use exhaust::OBJECT_SIZE;
14 | use exhaust::OBJECT_ALIGN;
15 | use exhaust::ALLOCATION_TIMES;
16 |
17 | const MARK_TIMES : usize = ALLOCATION_TIMES;
18 |
19 | #[allow(unused_variables)]
20 | pub fn alloc_mark() {
21 | let shared_space : Arc = {
22 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
23 |
24 | Arc::new(space)
25 | };
26 | let lo_space : Arc> = {
27 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
28 | Arc::new(RwLock::new(space))
29 | };
30 | heap::gc::init(shared_space.clone(), lo_space.clone());
31 |
32 | let mut mutator = ImmixMutatorLocal::new(shared_space.clone());
33 |
34 | println!("Trying to allocate 1 object of (size {}, align {}). ", OBJECT_SIZE, OBJECT_ALIGN);
35 | const ACTUAL_OBJECT_SIZE : usize = OBJECT_SIZE;
36 | println!("Considering header size of {}, an object should be {}. ", 0, ACTUAL_OBJECT_SIZE);
37 |
38 | println!("Trying to allocate {} objects, which will take roughly {} bytes", MARK_TIMES, MARK_TIMES * ACTUAL_OBJECT_SIZE);
39 | let mut objs : Vec = vec![];
40 | for _ in 0..MARK_TIMES {
41 | let res = mutator.alloc(ACTUAL_OBJECT_SIZE, OBJECT_ALIGN);
42 | mutator.init_object(res, 0b000011_11);
43 |
44 | objs.push(res);
45 | }
46 |
47 | mark_loop(objs, &shared_space);
48 | }
49 |
50 | #[inline(never)]
51 | fn mark_loop(objs: Vec, shared_space: &Arc) {
52 | use objectmodel;
53 |
54 | println!("Start marking");
55 | let t_start = time::Instant::now();
56 |
57 | let mark_state : MarkBits = MarkBits::set_MARK_from_u8(objectmodel::MARK_STATE.load(Ordering::SeqCst) as u8);
58 |
59 | let line_mark_table = shared_space.line_mark_table();
60 | let (space_start, space_end) = (shared_space.start(), shared_space.end());
61 |
62 | let trace_map = shared_space.trace_map.toStart;
63 |
64 | for i in 0..objs.len() {
65 | let obj_addr = unsafe {*objs.get_unchecked(i)};
66 |
67 | // mark the object as traced
68 | objectmodel::mark_as_traced(trace_map, space_start, obj_addr, mark_state);
69 |
70 | // mark meta-data
71 | if space_start.contains_object(obj_addr, space_end) {
72 | line_mark_table.mark_line_live2(space_start.get_first_line(), obj_addr);
73 | }
74 | }
75 |
76 | let t_end = time::Instant::now();
77 |
78 | println!("time used: {} msec", (t_end - t_start).whole_milliseconds());
79 | }
80 |
--------------------------------------------------------------------------------
/examples/immix/src/mt_trace.rs:
--------------------------------------------------------------------------------
1 | extern crate time;
2 |
3 | use heap;
4 | use heap::layout::*;
5 | use heap::immix::ImmixMutatorLocal;
6 | use heap::immix::ImmixSpace;
7 | use heap::freelist::FreeListSpace;
8 |
9 | pub const K : usize = 4;
10 | pub const TREE_DEPTH : usize = 10; // 10
11 | pub const TREE_COUNT : usize = 50; // 50
12 |
13 | pub const OBJECT_SIZE : usize = K * 8;
14 | pub const ACTUAL_OBJECT_SIZE : usize = K * 8;
15 | pub const OBJECT_ALIGN : usize = 8;
16 |
17 | #[inline(always)]
18 | fn alloc_k_ary_tree(mutator: &mut ImmixMutatorLocal) -> ObjectAddr {
19 | let addr = mutator.alloc(ACTUAL_OBJECT_SIZE, 8);
20 | mutator.init_object(addr, 0b001111_11);
21 | addr
22 | }
23 |
24 | fn make_tree(depth: usize, mutator: &mut ImmixMutatorLocal) -> ObjectAddr {
25 | if depth <= 0 {
26 | alloc_k_ary_tree(mutator)
27 | } else {
28 | let mut children = vec![];
29 | for _ in 0..K {
30 | children.push(make_tree(depth - 1, mutator));
31 | }
32 |
33 | let result = alloc_k_ary_tree(mutator);
34 | // println!("parent node: {:X}", result);
35 |
36 | let cursor = result;
37 | cursor.ptr_0().store_ObjectAddr(children.pop().unwrap());
38 | cursor.ptr_1().store_ObjectAddr(children.pop().unwrap());
39 | cursor.ptr_2().store_ObjectAddr(children.pop().unwrap());
40 | cursor.ptr_3().store_ObjectAddr(children.pop().unwrap());
41 |
42 | result
43 | }
44 | }
45 |
46 | #[allow(unused_variables)]
47 | pub fn alloc_trace() {
48 | use std::sync::{Arc, RwLock};
49 | use std::sync::atomic::Ordering;
50 |
51 | let shared_space : Arc = {
52 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
53 |
54 | Arc::new(space)
55 | };
56 | let lo_space : Arc> = {
57 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
58 | Arc::new(RwLock::new(space))
59 | };
60 | heap::gc::init(shared_space.clone(), lo_space.clone());
61 | let mut mutator = ImmixMutatorLocal::new(shared_space.clone());
62 |
63 | println!("Trying to allocate 1 object of (size {}, align {}). ", K * 8, 8);
64 | println!("Considering header size of {}, an object should be {}. ", 0, ACTUAL_OBJECT_SIZE);
65 |
66 | println!("Trying to allocate {} trees of depth {}, which is {} objects ({} bytes)",
67 | TREE_COUNT, TREE_DEPTH, TREE_COUNT * K.pow(TREE_DEPTH as u32), TREE_COUNT * K.pow(TREE_DEPTH as u32) * ACTUAL_OBJECT_SIZE);
68 |
69 | let mut roots : Vec = vec![];
70 |
71 | for _ in 0..TREE_COUNT {
72 | roots.push(make_tree(TREE_DEPTH, &mut mutator));
73 | }
74 |
75 | println!("Start tracing");
76 |
77 | let t_start = time::Instant::now();
78 | heap::gc::start_trace(&mut roots, shared_space, lo_space);
79 | let t_end = time::Instant::now();
80 |
81 | println!("time used: {} msec", (t_end - t_start).whole_milliseconds());
82 | }
83 |
--------------------------------------------------------------------------------
/examples/immix/src/obj_init.rs:
--------------------------------------------------------------------------------
1 | extern crate time;
2 |
3 | use heap;
4 | use heap::immix::ImmixMutatorLocal;
5 | use heap::immix::ImmixSpace;
6 | use heap::freelist::FreeListSpace;
7 | use heap::layout::*;
8 |
9 | use std::sync::RwLock;
10 | use std::sync::{Arc};
11 | use std::sync::atomic::Ordering;
12 |
13 | use exhaust::OBJECT_SIZE;
14 | use exhaust::OBJECT_ALIGN;
15 | use exhaust::ALLOCATION_TIMES;
16 |
17 | const INIT_TIMES : usize = ALLOCATION_TIMES;
18 |
19 | #[allow(unused_variables)]
20 | pub fn alloc_init() {
21 | let shared_space : Arc = {
22 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
23 |
24 | Arc::new(space)
25 | };
26 | let lo_space : Arc> = {
27 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
28 | Arc::new(RwLock::new(space))
29 | };
30 | heap::gc::init(shared_space.clone(), lo_space.clone());
31 |
32 | let mut mutator = ImmixMutatorLocal::new(shared_space.clone());
33 |
34 | println!("Trying to allocate 1 object of (size {}, align {}). ", OBJECT_SIZE, OBJECT_ALIGN);
35 | const ACTUAL_OBJECT_SIZE : usize = OBJECT_SIZE;
36 | println!("Considering header size of {}, an object should be {}. ", 0, ACTUAL_OBJECT_SIZE);
37 |
38 | println!("Trying to allocate {} objects, which will take roughly {} bytes", INIT_TIMES, INIT_TIMES * ACTUAL_OBJECT_SIZE);
39 | let mut objs = vec![];
40 | for _ in 0..INIT_TIMES {
41 | let res = mutator.alloc(ACTUAL_OBJECT_SIZE, OBJECT_ALIGN);
42 |
43 | objs.push(res);
44 | }
45 |
46 | init_loop(objs, &mut mutator);
47 | }
48 |
49 | #[inline(never)]
50 | fn init_loop(objs: Vec, mutator: &mut ImmixMutatorLocal) {
51 | println!("Start init objects");
52 | let t_start = time::Instant::now();
53 |
54 | for obj in objs {
55 | mutator.init_object_no_inline(obj, 0b000011_11);
56 | }
57 |
58 | let t_end = time::Instant::now();
59 |
60 | println!("time used: {} msec", (t_end - t_start).whole_milliseconds());
61 | }
62 |
--------------------------------------------------------------------------------
/examples/immix/src/objectmodel/mod.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 | use std::sync::atomic;
3 | use std::sync::atomic::Ordering;
4 |
5 | pub static MARK_STATE : atomic::AtomicUsize = atomic::AtomicUsize::new(0);
6 |
7 | use heap::layout::*;
8 |
9 | pub fn init() {
10 | MARK_STATE.store(1, atomic::Ordering::SeqCst);
11 | }
12 |
13 | pub fn get_curr_MARK_STATE() -> MarkBits {
14 | MarkBits::set_MARK_from_u8(MARK_STATE.load(Ordering::SeqCst) as u8)
15 | }
16 |
17 | pub fn get_curr_UNMARKED_STATE() -> MarkBits {
18 | let mark_state = MARK_STATE.load(Ordering::SeqCst) as u8;
19 | if mark_state == 0 {
20 | MarkBits::set_MARK_from_u8(0b00000001 as u8)
21 | } else {
22 | MarkBits::set_MARK_from_u8(0b00000000 as u8)
23 | }
24 | }
25 |
26 | pub fn flip_mark_state() {
27 | let mark_state = MARK_STATE.load(atomic::Ordering::SeqCst);
28 | if mark_state == 0 {
29 | MARK_STATE.store(1, atomic::Ordering::SeqCst);
30 | } else {
31 | MARK_STATE.store(0, atomic::Ordering::SeqCst);
32 | }
33 | }
34 |
35 | #[inline(always)]
36 | pub fn mark_as_traced(trace_map: MarkBitsAddr, space_start: SpaceAddr, obj: ObjectAddr, mark_state: MarkBits) {
37 | let cell = CellAddr::from_object(obj);
38 | Word2MarkBits::map_set(space_start.get_first_word(), trace_map, cell.get_first_word(), mark_state)
39 | }
40 |
41 | #[inline(always)]
42 | pub fn is_traced(trace_map: MarkBitsAddr, space_start: SpaceAddr, obj: ObjectAddr, mark_state: MarkBits) -> bool {
43 | let cell = CellAddr::from_object(obj);
44 | let state = Word2MarkBits::map_get(space_start.get_first_word(), trace_map, cell.get_first_word());
45 | //println!("state = {}, (state == mark_state) = {}", state.get_MARK_bits(), state == mark_state);
46 | state == mark_state
47 | }
48 |
49 | #[inline(always)]
50 | pub fn get_ref_byte(alloc_map: RefBitsAddr, space_start: SpaceAddr, obj: ObjectAddr) -> RefBits {
51 | let cell = CellAddr::from_object(obj);
52 | Word2RefBits::map_get(space_start.get_first_word(), alloc_map, cell.get_first_word())
53 | }
54 |
55 | #[inline(always)]
56 | pub fn set_ref_byte(alloc_map: RefBitsAddr, space_start: SpaceAddr, obj: ObjectAddr, value: RefBits) {
57 | let cell_addr = CellAddr::from_object(obj);
58 | Word2RefBits::map_set(space_start.get_first_word(), alloc_map, cell_addr.get_first_word(), value)
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/examples/immix/src/testme.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_upper_case_globals)]
2 | #![allow(non_camel_case_types)]
3 | #![allow(non_snake_case)]
4 | #![allow(unused_variables)]
5 | #![allow(unused_assignments)]
6 |
7 | use heap;
8 | use heap::layout::*;
9 | use heap::immix::ImmixMutatorLocal;
10 | use heap::immix::ImmixSpace;
11 | use heap::freelist::FreeListSpace;
12 | use std::mem::size_of as size_of;
13 | use std::boxed::Box;
14 |
15 | pub struct Node {
16 | fwd : *mut Node,
17 | bwd : *mut Node,
18 | val : usize
19 | }
20 |
21 | static mut mutator: *mut ImmixMutatorLocal = std::ptr::null::() as *mut ImmixMutatorLocal;
22 | fn alloc(numP: usize, typ: u8) -> *mut Type {
23 | let sz = size_of::();
24 | let obj_addr : ObjectAddr = unsafe { (*mutator).alloc(sz, 8) };
25 | unsafe { (*mutator).init_object(obj_addr, match numP {
26 | 0 => 0b000000_11,
27 | 1 => 0b000001_11,
28 | 2 => 0b000011_11,
29 | 3 => 0b000111_11,
30 | 4 => 0b001111_11,
31 | _ => 0b000000_11
32 | }); }
33 | let ret = obj_addr.to_ptr_mut::();
34 | //for i in 0..sz {
35 | // check_expect_pc!(obj_addr.as_usize() + i, vec![__FLP_IDX_CELL]);
36 | // unsafe { layout::pc::set_bits(0, (obj_addr.as_usize() + i) as *const libc::c_void, typ); }
37 | //}
38 | ret
39 | }
40 |
41 | fn alloc_node() -> *mut Node {
42 | let ret = alloc::(2, __FLP_IDX_LLNODE);
43 | eprintln!("Allocated {} bytes at 0x{:X}", size_of::(), ret as usize);
44 | ret
45 | }
46 |
47 | pub const NUM_BLOCKS : usize = 1; // 1 Block (65536 bytes) fits 2048 Nodes (3 words + 1 cell_size word per Node)
48 | pub const LO_SIZE : usize = 1 << 20; // Also 1 megabyte (large-object space)
49 |
50 | pub fn fwd(n: *mut Node, val: *mut Node) { unsafe {
51 | (*n).fwd = val;
52 | }}
53 | pub fn bwd(n: *mut Node, val: *mut Node) { unsafe {
54 | (*n).bwd = val;
55 | }}
56 | pub fn get_fwd(n: *mut Node) -> *mut Node { unsafe {
57 | (*n).fwd
58 | }}
59 | pub fn get_bwd(n: *mut Node) -> *mut Node { unsafe {
60 | (*n).bwd
61 | }}
62 |
63 | pub fn trash_it(n: *mut Node) {
64 | //for i in 0 .. size_of::() {
65 | // unsafe { layout::pc::set_bits(0, ((n as usize) + i) as *const libc::c_void, __FLP_IDX_GARBAGE); }
66 | //}
67 | }
68 |
69 | pub fn start() {
70 | use std::sync::{Arc, RwLock};
71 | unsafe {heap::gc::set_low_water_mark();}
72 | let immix_space : Arc = {
73 | let space : ImmixSpace = ImmixSpace::new(NUM_BLOCKS * (1 << LOG_BYTES_IN_BLOCK));
74 | Arc::new(space)
75 | };
76 | let lo_space : Arc> = {
77 | let space : FreeListSpace = FreeListSpace::new(LO_SIZE);
78 | Arc::new(RwLock::new(space))
79 | };
80 | heap::gc::init(immix_space.clone(), lo_space.clone());
81 | unsafe { mutator = Box::into_raw(Box::new(ImmixMutatorLocal::new(immix_space.clone()))); }
82 |
83 | {
84 | let mut tmp = 0x0 as *mut Node;
85 | let orig = alloc_node();
86 | fwd(orig, orig);
87 | bwd(orig, orig);
88 | let mut curr = orig;
89 | for i in 0 .. (2048 - 1) {
90 | tmp = alloc_node();
91 | fwd(curr, tmp);
92 | bwd(tmp, curr);
93 | curr = tmp;
94 | }
95 | fwd(curr, curr);
96 | curr = get_fwd(orig);
97 | loop {
98 | tmp = get_fwd(curr);
99 | bwd(curr, 0xdeadbeef as *mut Node);
100 | fwd(curr, 0xdeadbeef as *mut Node);
101 | trash_it(curr);
102 | if tmp == curr { break; }
103 | curr = tmp;
104 | }
105 | fwd(orig, orig);
106 | bwd(orig, orig);
107 | curr = orig;
108 | tmp = orig;
109 | // Everything except the original node is marked as garbage now, and no longer accessible
110 | // via the Rust stack (i.e. orig and curr both point to orig, which is /not/ marked as
111 | // garbage)
112 | tmp = alloc_node();
113 |
114 | // Now trash that node, and move on to next allocation cycle:
115 | fwd(tmp, 0xdeaddead as *mut Node);
116 | bwd(tmp, 0xdeaddead as *mut Node);
117 | trash_it(tmp);
118 | for i in 0 .. (2024 - 1) {
119 | tmp = alloc_node();
120 | fwd(curr, tmp);
121 | bwd(tmp, curr);
122 | curr = tmp;
123 | }
124 | fwd(curr, curr);
125 |
126 | // Delete everything again, except the original:
127 | curr = get_fwd(orig);
128 | loop {
129 | tmp = get_fwd(curr);
130 | bwd(curr, 0xdeadeeee as *mut Node);
131 | fwd(curr, 0xdeadeeee as *mut Node);
132 | trash_it(curr);
133 | if tmp == curr { break; }
134 | curr = tmp;
135 | }
136 | fwd(orig, orig);
137 | bwd(orig, orig);
138 | curr = orig;
139 | tmp = orig;
140 |
141 | // Trigger GC?:
142 | tmp = alloc_node();
143 |
144 | }
145 | println!("Finished!");
146 | //layout::dump_map()
147 | }
148 |
149 |
--------------------------------------------------------------------------------
/examples/immix/src/trace.rs:
--------------------------------------------------------------------------------
1 | extern crate time;
2 |
3 | use heap;
4 | use heap::layout::*;
5 | use heap::immix::ImmixMutatorLocal;
6 | use heap::immix::ImmixSpace;
7 | use heap::freelist::FreeListSpace;
8 |
9 | use std::sync::{Arc};
10 | use std::sync::atomic::Ordering;
11 | use std::sync::RwLock;
12 |
13 | use exhaust::OBJECT_SIZE;
14 | use exhaust::OBJECT_ALIGN;
15 | use exhaust::ALLOCATION_TIMES;
16 |
17 | const TRACE_TIMES : usize = ALLOCATION_TIMES;
18 |
19 | /*
20 | struct Node<'a> {
21 | hdr : u64,
22 | next : &'a Node<'a>,
23 | unused_ptr : usize,
24 | unused_int : i32,
25 | unused_int2: i32
26 | }
27 | */
28 |
29 | #[allow(unused_variables)]
30 | pub fn alloc_trace() {
31 | let shared_space : Arc = {
32 | let space : ImmixSpace = ImmixSpace::new(heap::IMMIX_SPACE_SIZE.load(Ordering::SeqCst));
33 |
34 | Arc::new(space)
35 | };
36 | let lo_space : Arc> = {
37 | let space : FreeListSpace = FreeListSpace::new(heap::LO_SPACE_SIZE.load(Ordering::SeqCst));
38 | Arc::new(RwLock::new(space))
39 | };
40 | heap::gc::init(shared_space.clone(), lo_space.clone());
41 |
42 | let mut mutator = ImmixMutatorLocal::new(shared_space.clone());
43 |
44 | println!("Trying to allocate 1 object of (size {}, align {}). ", OBJECT_SIZE, OBJECT_ALIGN);
45 | const ACTUAL_OBJECT_SIZE : usize = OBJECT_SIZE;
46 | println!("Considering header size of {}, an object should be {}. ", 0, ACTUAL_OBJECT_SIZE);
47 |
48 | println!("Trying to allocate {} objects, which will take roughly {} bytes", TRACE_TIMES, TRACE_TIMES * ACTUAL_OBJECT_SIZE);
49 | let root : ObjectAddr = mutator.alloc(ACTUAL_OBJECT_SIZE, OBJECT_ALIGN);
50 | mutator.init_object(root, 0b00000001_11);
51 |
52 | let mut prev : ObjectAddr = root;
53 | for _ in 0..TRACE_TIMES - 1 {
54 | let res = mutator.alloc(ACTUAL_OBJECT_SIZE, OBJECT_ALIGN);
55 | mutator.init_object(res, 0b000001_11);
56 |
57 | // set prev's 1st field (offset 0) to this object
58 | prev.ptr_0().store_ObjectAddr(res);
59 |
60 | prev = res;
61 | }
62 |
63 | trace_loop(root, shared_space, lo_space);
64 | }
65 |
66 | #[inline(never)]
67 | fn trace_loop(root: ObjectAddr, shared_space: Arc, lo_space: Arc>) {
68 | println!("Start tracing");
69 | let mut roots = vec![root];
70 |
71 | let t_start = time::Instant::now();
72 |
73 | heap::gc::start_trace(&mut roots, shared_space, lo_space);
74 |
75 | let t_end = time::Instant::now();
76 |
77 | println!("time used: {} msec", (t_end - t_start).whole_milliseconds());
78 | }
79 |
--------------------------------------------------------------------------------
/examples/immix/test_mac.sh:
--------------------------------------------------------------------------------
1 | cargo build --release --features mt-trace
2 | cp ../target/release/libimmix_rust.dylib .
3 | gcc test.c libimmix_rust.dylib
4 | ./a.out
5 |
--------------------------------------------------------------------------------
/examples/layer.flp:
--------------------------------------------------------------------------------
1 | // Parser test
2 |
3 | Seq -> seq {
4 | Oops -> Bar,
5 | Foo -> Baz ptr
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/map_bits.flp:
--------------------------------------------------------------------------------
1 | // Compiler test: mapping bits to bits code gen
2 |
3 | Foo -> seq {
4 | count bytes,
5 | count Bar ||2 bytes|| -> 2 bytes
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/named_ref.flp:
--------------------------------------------------------------------------------
1 | // Parser test: a named reference to a flp type specified elsewhere
2 |
3 | Seq -> seq {
4 | a : # A,
5 | b : B
6 | }
7 |
8 | A -> 1 words
9 |
10 | B -> 2 bytes
11 |
12 |
--------------------------------------------------------------------------------
/examples/nested.flp:
--------------------------------------------------------------------------------
1 | // Parser test: nested groups (union and seq) with
2 | // fixed-width fields.
3 |
4 | Block -> union {
5 | A -> seq { a : 0 words, b : 2 words }
6 | | B -> seq { a : 0 words, c : 2 words }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/examples/nested_union.flp:
--------------------------------------------------------------------------------
1 | // Parser test: union in various places (both in a flake and in a field)
2 |
3 | Block -> union {
4 | A -> seq {
5 | a : union { 1 words | 1 words }
6 | , b : 2^2 words
7 | }
8 | | B -> seq { a : 0 words, c : (2^4) words }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/examples/parens.flp:
--------------------------------------------------------------------------------
1 |
2 | Bar -> 1 words - 1 bytes
3 |
4 | Foo -> (1 words - 1 bytes)
5 |
6 |
--------------------------------------------------------------------------------
/examples/seq.flp:
--------------------------------------------------------------------------------
1 | // Parser test: basic sequence with fixed-width fields.
2 |
3 | Seq -> seq {
4 | a : Oops,
5 | b : Oops ptr
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/union.flp:
--------------------------------------------------------------------------------
1 | // Parser test: basic union test.
2 |
3 | Union -> union {
4 | Oops
5 | | Oops ptr
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/examples/uniq_fail.flp:
--------------------------------------------------------------------------------
1 |
2 | Outer -> seq {
3 | foo : 1 words,
4 | bar : 2 words,
5 | Inner -> seq {
6 | Outer -> 2 words
7 | }
8 | }
9 |
10 | Other -> seq {
11 | foo : 2 words
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/flp-compiler/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /Cargo.lock
3 |
--------------------------------------------------------------------------------
/flp-compiler/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "flp-compiler"
5 | version = "0.1.0"
6 |
7 |
--------------------------------------------------------------------------------
/flp-compiler/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "flp-compiler"
3 | version = "0.1.0"
4 | authors = ["Karl Cronburg "]
5 | license = "MIT"
6 | repository = "https://github.com/RedlineResearch/floorplan"
7 | homepage = "https://redlineresearch.github.io/floorplan/"
8 | description = "Floorplan, a memory layout specification language, compiler binding for Rust."
9 |
10 | [dependencies]
11 |
12 |
--------------------------------------------------------------------------------
/flp-compiler/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! # The Official Floorplan Compiler Crate
2 | //!
3 | //! This module interfaces with the Haskell implementation of the
4 | //! Floorplan compiler, providing official support for compiling
5 | //! Floorplan specifications into Rust code directly from the
6 | //! cargo toolchain.
7 | //!
8 | //! In order to use this crate, you must have the `flp` executable
9 | //! on your `$PATH` when developing a Floorplan-based memory manager.
10 | //!
11 | //! In order to compile directly from the cargo toolchain, include a
12 | //! `build.rs` in your project root similar to the following:
13 | //!
14 | //! ```rust,ignore
15 | //! extern crate flp_compiler as flpc;
16 | //!
17 | //! fn main() {
18 | //! flpc::Build::new()
19 | //! .src("src/heap/layout.flp")
20 | //! .dest("src/heap/layout.rs")
21 | //! .compile();
22 | //! }
23 | //! ```
24 | //!
25 | //! For information on acquiring the Floorplan compiler itself,
26 | //! go see the [GitHub project here][github-project].
27 | //!
28 | //! [github-project]: https://github.com/RedlineResearch/floorplan
29 | use std::process::*;
30 | use std::io::Write;
31 |
32 | /// A build configuration.
33 | #[derive(Clone, Debug)]
34 | pub struct Build {
35 | src: Option,
36 | dest: Option,
37 | }
38 |
39 | /// A very basic implementation of a binding to an external compiler, supporting
40 | /// specification of input and output files.
41 | impl Build {
42 |
43 | /// Construct a new build configuration with default values, which will fail to compile
44 | /// by default.
45 | pub fn new() -> Build {
46 | Build {
47 | src: None,
48 | dest: None,
49 | }
50 | }
51 |
52 | /// Set the source `.flp` to be compiled.
53 | pub fn src(&mut self, s: &str) -> &mut Build {
54 | self.src = Some(s.to_string());
55 | self
56 | }
57 |
58 | /// Set the destination `.rs` file to generate the output library into.
59 | pub fn dest(&mut self, d: &str) -> &mut Build {
60 | self.dest = Some(d.to_string());
61 | self
62 | }
63 |
64 | /// Run the compiler on the current build configuration, failing miserably on error.
65 | pub fn compile(&mut self) {
66 | if let Err(e) = self.try_compile() {
67 | fail(&e);
68 | }
69 | }
70 |
71 | /// Attempt to run the compiler on the current build configuration, politely
72 | /// returning an error message if the compiler fails.
73 | pub fn try_compile(&mut self) -> Result<(), String> {
74 | let src = self.src.clone().unwrap();
75 | let dest = self.dest.clone().unwrap();
76 | Command::new("flp")
77 | .args(&[src, dest])
78 | .output()
79 | .expect("Floorplan failed to run.");
80 | Ok(())
81 | }
82 |
83 | }
84 |
85 | fn fail(s: &str) -> ! {
86 | let _ = writeln!(std::io::stderr(), "\nError: {}\n\n", s);
87 | std::process::exit(1);
88 | }
89 |
90 |
--------------------------------------------------------------------------------
/flp-framework/.gitignore:
--------------------------------------------------------------------------------
1 | Cargo.lock
2 | /target
3 |
--------------------------------------------------------------------------------
/flp-framework/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "flp-framework"
3 | version = "0.1.0"
4 | authors = ["Karl Cronburg "]
5 | license = "MIT"
6 | repository = "https://github.com/RedlineResearch/floorplan"
7 | homepage = "https://redlineresearch.github.io/floorplan/"
8 | description = "Floorplan, a memory layout specification language, code-generation necessities."
9 |
10 | [dependencies]
11 |
12 |
--------------------------------------------------------------------------------
/genrs/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /src/layout.rs
3 |
--------------------------------------------------------------------------------
/genrs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dist"
3 | version = "0.1.0"
4 | authors = ["Karl Cronburg "]
5 | build = "build.rs"
6 |
7 | [build-dependencies]
8 | flp-compiler = { path = "../flp-compiler" }
9 |
10 | [[bin]]
11 | name = "main"
12 | path = "src/main.rs"
13 |
14 | [dependencies]
15 | flp-framework = { path = "../flp-framework" }
16 | libc = "0.2.2"
17 |
18 |
--------------------------------------------------------------------------------
/genrs/build.rs:
--------------------------------------------------------------------------------
1 | extern crate flp_compiler as flpc;
2 |
3 | fn main() {
4 |
5 | flpc::Build::new()
6 | .src("src/layout.flp")
7 | .dest("src/layout.rs")
8 | .compile();
9 |
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/genrs/src/layout.flp:
--------------------------------------------------------------------------------
1 |
2 | HelloRust -> 32 bytes
3 |
4 |
--------------------------------------------------------------------------------
/genrs/src/main.rs:
--------------------------------------------------------------------------------
1 | mod layout;
2 | use layout::*;
3 |
4 | fn main() {
5 | print!("Hello, world: {}\n", BYTES_IN_HELLORUST);
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/package.yaml:
--------------------------------------------------------------------------------
1 | name: flp
2 | version: 0.1.0.0
3 | github: "RedlineResearch/floorplan"
4 | license: MIT
5 | author: "Karl Cronburg"
6 | maintainer: "karl@cs.tufts.edu"
7 | copyright: "2019 Karl Cronburg"
8 |
9 | extra-source-files:
10 | - README.md
11 | - LICENSE
12 | - LICENSE.language-rust
13 | - ChangeLog.md
14 | - examples/app.flp
15 | - examples/arith.flp
16 | - examples/arith_id.flp
17 | - examples/arith_power.flp
18 | - examples/bits.flp
19 | - examples/bump.flp
20 | - examples/dynamic_prim.flp
21 | - examples/dyn_choice.flp
22 | - examples/dyn_seq.flp
23 | - examples/empty.flp
24 | - examples/enum_bad0.flp
25 | - examples/enum.flp
26 | - examples/layer.flp
27 | - examples/map_bits.flp
28 | - examples/named_ref.flp
29 | - examples/nested.flp
30 | - examples/nested_union.flp
31 | - examples/parens.flp
32 | - examples/seq.flp
33 | - examples/union.flp
34 | - examples/uniq_fail.flp
35 |
36 | # Metadata used when publishing your package
37 | synopsis: A layout spec language for memory managers implemented in Rust.
38 | category: Compiler
39 |
40 | # To avoid duplicated efforts in documentation and dealing with the
41 | # complications of embedding Haddock markup inside cabal files, it is
42 | # common to point users to the README.md file.
43 | description: Please see the README on GitHub at
44 |
45 | dependencies:
46 | - base >= 4.7 && < 5
47 | - array >= 0.5.3.0
48 | - template-haskell
49 | - th-lift
50 | - containers
51 | # The following dependencies are required by language-rust code in our src/ tree:
52 | - deepseq
53 | - prettyprinter
54 | - transformers
55 | - haskell-src-meta
56 | - language-c-quote >= 0.12.2.1
57 | - mainland-pretty
58 | - text
59 | - srcloc
60 | - regex >= 1.1.0.0
61 |
62 | library:
63 | source-dirs: src
64 |
65 | build-tools:
66 | - alex
67 | - happy
68 |
69 | executables:
70 | flp:
71 | source-dirs: app
72 | main: floorplan.hs
73 | dependencies:
74 | - flp
75 | sem:
76 | source-dirs: app
77 | main: semantics.hs
78 | dependencies:
79 | - flp
80 | - pretty-simple
81 |
82 | tests:
83 | parser:
84 | main: Main.hs
85 | source-dirs: test/parser
86 | ghc-options:
87 | - -threaded
88 | - -rtsopts
89 | - -with-rtsopts=-N
90 | dependencies:
91 | - flp
92 | - test-framework >= 0.8
93 | - test-framework-hunit >= 0.3
94 | - HUnit >= 1.6
95 |
96 |
--------------------------------------------------------------------------------
/proofs/.gitignore:
--------------------------------------------------------------------------------
1 | *.vo
2 | *.glob
3 | .*.aux
4 |
--------------------------------------------------------------------------------
/proofs/Common.v:
--------------------------------------------------------------------------------
1 | Require Import Coq.Strings.String.
2 | From Coq Require Import Bool Ascii String.
3 | Require Import Coq.Bool.Bool.
4 |
5 | Theorem if_f_app: forall (X : Type) (Y : Type) (f : X -> Y) (cond : bool) (a : X) (b : X),
6 | f (if cond then a else b) = if cond then f a else f b.
7 | Proof. intros X Y f cond a b. destruct cond. reflexivity. reflexivity. Qed.
8 |
9 | Theorem if_app: forall (A : Type) (B : Type) (cond : bool) (a b : A -> B) (c : A),
10 | (if cond then a else b) c = if cond then a c else b c.
11 | Proof. intros A B cond a b c. destruct cond ; reflexivity. Qed.
12 |
13 | Definition eq_ascii (a1 a2 : ascii) :=
14 | match a1, a2 with
15 | | Ascii b1 b2 b3 b4 b5 b6 b7 b8, Ascii c1 c2 c3 c4 c5 c6 c7 c8 =>
16 | (eqb b1 c1) && (eqb b2 c2) && (eqb b3 c3) && (eqb b4 c4) &&
17 | (eqb b5 c5) && (eqb b6 c6) && (eqb b7 c7) && (eqb b8 c8)
18 | end.
19 |
20 | Theorem eq_ascii_correct: forall (a b : ascii),
21 | eq_ascii a b = eq_ascii b a.
22 | Proof. intros a. destruct a. destruct b.
23 | - destruct b0, b1, b2, b3, b4, b5, b6 ; reflexivity.
24 | - destruct b0, b1, b2, b3, b4, b5, b6 ; reflexivity. Qed.
25 |
26 | Theorem eq_ascii_a_a: forall (a : ascii),
27 | eq_ascii a a = true.
28 | Proof. destruct a.
29 | - destruct b, b0, b1, b2, b3, b4, b5, b6 ; reflexivity. Qed.
30 |
31 | Fixpoint eq_string (s1 s2 : string) :=
32 | match s1, s2 with
33 | | EmptyString, EmptyString => true
34 | | String x1 s1, String x2 s2 => eq_ascii x1 x2 && eq_string s1 s2
35 | | _, _ => false
36 | end.
37 |
38 | Theorem eq_string_correct: forall (a b : string),
39 | eq_string a b = eq_string b a.
40 | Proof. intros a. induction a.
41 | - destruct b ; reflexivity.
42 | - induction b.
43 | + simpl. reflexivity.
44 | + simpl. rewrite IHa. rewrite eq_ascii_correct. reflexivity. Qed.
45 |
46 | Theorem eq_string_s_s: forall (s : string),
47 | eq_string s s = true.
48 | Proof. induction s.
49 | - reflexivity.
50 | - simpl. rewrite IHs. rewrite eq_ascii_a_a. reflexivity. Qed.
51 |
52 | Fixpoint ble_nat n m :=
53 | match n, m with
54 | | O, _ => true
55 | | S n', O => false
56 | | S n', S m' => ble_nat n' m'
57 | end.
58 |
59 | Lemma eqb_eq': forall (a b : bool),
60 | eqb a b = true -> a = b.
61 | Proof. apply eqb_true_iff. Qed.
62 |
63 | Hint Resolve eqb_eq'.
64 |
65 | (*
66 | Hint Resolve -> eqb_true_iff.
67 | Check eqb_true_iff. *)
68 |
69 | (* == firstorder *)
70 | Ltac break_conjs := repeat match goal with
71 | | H : _ /\ _ |- _ => inversion H; subst; clear H
72 | end.
73 |
74 | Check eqb_true_iff.
75 | Lemma eq_ascii_refl: forall (z y x : ascii),
76 | eq_ascii x y = true -> eq_ascii x z = eq_ascii y z.
77 | Proof with auto. induction z.
78 | - destruct y.
79 | * destruct x. simpl. intros. repeat rewrite andb_true_iff in H.
80 | break_conjs.
81 | replace b7 with b15 ... (* ... == "; auto" *)
82 | replace b8 with b16 ...
83 | replace b9 with b17 ...
84 | replace b10 with b18 ...
85 | replace b11 with b19 ...
86 | replace b12 with b20 ...
87 | replace b13 with b21 ...
88 | replace b14 with b22 ... Qed.
89 |
90 | Lemma eq_ascii_refl_false: forall (a a0 a1 : ascii),
91 | eq_ascii a a0 = false
92 | -> eq_ascii a a1 = true
93 | -> eq_ascii a0 a1 = false.
94 | Proof. intros. rewrite eq_ascii_refl with (y := a1) in H. rewrite eq_ascii_correct.
95 | apply H. apply H0. Qed.
96 |
97 | Theorem eq_string_refl: forall (c b a : string),
98 | eq_string a b = true -> eq_string a c = eq_string b c.
99 | Proof. induction c.
100 | - destruct b.
101 | * destruct a. reflexivity. intros. inversion H.
102 | * intros. destruct a0. inversion H. reflexivity.
103 | - destruct b.
104 | * intros. destruct a0. reflexivity. inversion H.
105 | * intros. destruct a1. inversion H. simpl. simpl in H. rewrite andb_true_iff in H.
106 | inversion H. rewrite IHc with (a := a2) (b := b). 2: { apply H1. }
107 | rewrite eq_ascii_refl with (x := a1) (y := a0). 2: { apply H0. }
108 | reflexivity. Qed.
109 |
110 | Lemma eq_string_refl_false: forall (s s0 s1 : string),
111 | eq_string s s0 = false
112 | -> eq_string s s1 = true
113 | -> eq_string s0 s1 = false.
114 | Proof. induction s.
115 | - destruct s0.
116 | * intros. destruct s1. inversion H. reflexivity.
117 | * intros. destruct s1. reflexivity. inversion H0.
118 | - destruct s0.
119 | * intros. destruct s1. inversion H0. reflexivity.
120 | * intros. destruct s1.
121 | + reflexivity.
122 | + simpl. simpl in H. simpl in H0. rewrite andb_true_iff in H0. inversion H0.
123 | rewrite eq_string_correct. rewrite eq_string_refl with (a := s1) (c := s0) (b := s).
124 | 2: { rewrite eq_string_correct. apply H2. }
125 | destruct (eq_string s s0). 2: { rewrite andb_comm. reflexivity. }
126 | rewrite andb_comm. simpl. rewrite andb_comm in H. simpl in H.
127 | rewrite eq_ascii_refl_false with (a := a). reflexivity. apply H. apply H1. Qed.
128 |
--------------------------------------------------------------------------------
/proofs/ListSet.v:
--------------------------------------------------------------------------------
1 | Require Import Common.
2 | Require Import Coq.Bool.Bool.
3 | Require Import Coq.Arith.EqNat.
4 |
5 | Inductive set (A : Type) : Type :=
6 | | S0 : set A
7 | | St : A -> set A -> set A.
8 |
9 | (* Is the value v syntactically equivalent to something in the set: *)
10 | Fixpoint set_mem (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : bool :=
11 | match vs with
12 | | S0 _ => false
13 | | St _ v2 vs' => if eq_A v v2 then true else (set_mem A eq_A) vs' v
14 | end.
15 |
16 | Fixpoint set_add (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : set A :=
17 | match vs with
18 | | S0 _ => St _ v (S0 _)
19 | | St _ v2 vs' => if eq_A v v2 then St _ v vs' else St _ v2 ((set_add A eq_A) vs' v)
20 | end.
21 |
22 | (* Adding a value and then checking for its membership always returns true: *)
23 | Theorem set_add_correct: forall (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A),
24 | eq_A v v = true -> (set_mem A eq_A) ((set_add A eq_A) vs v) v = true.
25 | Proof. intros A eq_A vs. induction vs.
26 | - intros v H. simpl. rewrite H. reflexivity.
27 | - intros v0. simpl. intros H. destruct (eq_A v0 a).
28 | * simpl. rewrite H. reflexivity.
29 | * simpl. rewrite IHvs. destruct (eq_A v0 a) ; reflexivity. apply H. Qed.
30 |
31 | Fixpoint set_rem (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : set A :=
32 | match vs with
33 | | S0 _ => S0 _
34 | | St _ v2 vs' => if eq_A v v2 then (set_rem A eq_A) vs' v else St _ v2 ((set_rem A eq_A) vs' v)
35 | end.
36 |
37 | Theorem set_rem_correct: forall (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A),
38 | eq_A v v = true -> (set_mem A eq_A) ((set_rem A eq_A) vs v) v = false.
39 | Proof. intros A eq_A vs. induction vs.
40 | - intros v H. reflexivity.
41 | - intros v H. simpl. rewrite if_f_app. rewrite if_app. simpl.
42 | rewrite IHvs. destruct (eq_A v a) ; reflexivity. apply H. Qed.
43 |
44 | (* set_fold_left {A B:Type} : (B -> A -> B) -> set A -> B -> B *)
45 | Fixpoint set_fold_left (A : Type) (B : Type) (fncn : B -> A -> B) (s : set A) (init : B) : B :=
46 | match s with
47 | | S0 _ => init
48 | | St _ v s' => set_fold_left A B fncn s' (fncn init v)
49 | end.
50 |
51 | Compute (set_add nat beq_nat).
52 |
53 | Compute (set_fold_left _ _ (set_add _ beq_nat) (St _ 3 (St _ 4 (S0 _))) (St _ 42 (St _ 43 (S0 _)))).
54 |
55 | Theorem set_fold_left_correct: forall (A : Type) (eq_A : A -> A -> bool) (s : set A),
56 | (forall v, eq_A v v = true) -> set_fold_left A (set A) (set_add A eq_A) (S0 _) s = s.
57 | Proof. intros. destruct s ; auto. Qed.
58 |
59 | Fixpoint union2 (A : Type) (eq_A : A -> A -> bool) (a b : set A) : set A :=
60 | match a with
61 | | S0 _ => b
62 | | St _ v s' => (union2 A eq_A) s' ((set_add A eq_A) b v)
63 | end.
64 |
65 | Fixpoint set_init (A : Type) (eq_A : A -> A -> bool) (fncn : nat -> A) (n : nat) : set A :=
66 | match n with
67 | | O => St _ (fncn O) (S0 _)
68 | | S n' => set_add _ eq_A (set_init _ eq_A fncn n') (fncn n)
69 | end.
70 |
71 | Fixpoint set_map (A : Type) (B : Type) (fncn : A -> B) (input : set A) : set B :=
72 | match input with
73 | | S0 _ => S0 _
74 | | St _ x xs => St _ (fncn x) (set_map _ _ fncn xs)
75 | end.
76 |
77 | Definition singleton (V : Type) (v : V) : set V := St _ v (S0 _).
78 |
79 | Fixpoint filter (V : Type) (pred : V -> bool) (vs : set V) : set V :=
80 | match vs with
81 | | S0 _ => S0 _
82 | | St _ v vs' => if pred v then St _ v (filter _ pred vs') else filter _ pred vs'
83 | end.
84 |
85 | (* Union from [0,m] of the given function: *)
86 | Fixpoint unionAll (A : Type) (eq_A : A -> A -> bool) (m : nat) (fncn : nat -> set A) : set A :=
87 | match m with
88 | | O => fncn O
89 | | S m' => union2 _ eq_A (fncn m) (unionAll _ eq_A m' fncn)
90 | end.
91 |
92 | Fixpoint all_satisfy (X : Type) (pred : X -> bool) (xs : set X) : bool :=
93 | set_fold_left X bool (fun b x => b && pred x) xs true.
94 |
95 | Compute all_satisfy nat (beq_nat 3) (St _ 3 (St _ 3 (St _ 4 (S0 _)))).
96 |
97 | Lemma set_mem_map_preserve: forall (X : Type) (eq_X : X -> X -> bool) (xs : set X)
98 | (x2 : X) (fncn : X -> X) (prop : X -> Prop),
99 | (forall (x1 : X), set_mem X eq_X xs x1 = true -> prop x1) (* induction hypothesis *) (* YES *)
100 | -> (forall (i : X), eq_X i i = true) (* eq_X is reflexive *) (* YES *)
101 | -> (forall (i j : X), eq_X i j = true -> prop i = prop j) (* eq_X is prop-preserving *) (* YES *)
102 | -> (forall (i : X), prop (fncn i) = prop i) (* fncn is prop-preserving *) (* YES *)
103 | -> (set_mem X eq_X (set_map _ _ fncn xs) x2 = true -> prop x2). (* then map is leaves preserving *) (* OBV *)
104 | Proof. induction xs.
105 | - simpl. intros. inversion H3.
106 | - simpl. intro x2. intro fncn. intro prop. intro IHx1. intro eq_refl.
107 | (* intro eq_symm. *) intro eq_prop_pres. (* intro eq_prop_pres_neg. *) intro fncn_prop_pres.
108 | (* intro eq_X_fncn_pres. *) intro set_mem_prop. destruct (eq_X x2 (fncn a)) eqn:X2_is_fna.
109 | (* x2 = fncn(a) *)
110 | + rewrite eq_prop_pres with (j := fncn a). rewrite -> fncn_prop_pres. apply IHx1. rewrite eq_refl. reflexivity.
111 | apply X2_is_fna.
112 | (* x2 /= fncn(a) *)
113 | + apply IHxs with (fncn := fncn).
114 | * intro x1. intro set_mem_x1. apply IHx1. rewrite -> set_mem_x1. destruct (eq_X x1 a) ; reflexivity.
115 | * apply eq_refl.
116 | * apply eq_prop_pres.
117 | * apply fncn_prop_pres.
118 | * apply set_mem_prop. Qed.
119 |
--------------------------------------------------------------------------------
/proofs/README.md:
--------------------------------------------------------------------------------
1 | # Proofs
2 |
3 | Warning: The proofs in here are outdated, they do not seem to compile at present
4 | due to newer libraries / dependencies, and the semantics as implemented in Coq
5 | in this directory likely have a semantic bug: one which does not meaningfully
6 | affect the correctness of the size_correctness proof defined herein. To
7 | view the updated semantics look at `app/semantics.hs` in the root of this
8 | repository, or checkout the [preprint](https://cronburg.com/papers/floorplan19.pdf)
9 | of the associated paper.
10 |
11 |
--------------------------------------------------------------------------------
/proofs/Set.v:
--------------------------------------------------------------------------------
1 | Require Import Common.
2 |
3 | Inductive set (A : Type) : Type :=
4 | | S0 : set A
5 | | St : A -> set A -> set A.
6 |
7 | (* Is the value v syntactically equivalent to something in the set: *)
8 | Fixpoint set_mem (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : bool :=
9 | match vs with
10 | | S0 _ => false
11 | | St _ v2 vs' => if eq_A v v2 then true else (set_mem A eq_A) vs' v
12 | end.
13 |
14 | Fixpoint set_add (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : set A :=
15 | match vs with
16 | | S0 _ => St _ v (S0 _)
17 | | St _ v2 vs' => if eq_A v v2 then St _ v vs' else St _ v2 ((set_add A eq_A) vs' v)
18 | end.
19 |
20 | (* Adding a value and then checking for its membership always returns true: *)
21 | Theorem set_add_correct: forall (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A),
22 | eq_A v v = true -> (set_mem A eq_A) ((set_add A eq_A) vs v) v = true.
23 | Proof. intros A eq_A vs. induction vs.
24 | - intros v H. simpl. rewrite H. reflexivity.
25 | - intros v0. simpl. intros H. destruct (eq_A v0 a).
26 | * simpl. rewrite H. reflexivity.
27 | * simpl. rewrite IHvs. destruct (eq_A v0 a) ; reflexivity. apply H. Qed.
28 |
29 | Fixpoint set_rem (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A) : set A :=
30 | match vs with
31 | | S0 _ => S0 _
32 | | St _ v2 vs' => if eq_A v v2 then (set_rem A eq_A) vs' v else St _ v2 ((set_rem A eq_A) vs' v)
33 | end.
34 |
35 | Theorem set_rem_correct: forall (A : Type) (eq_A : A -> A -> bool) (vs : set A) (v : A),
36 | eq_A v v = true -> (set_mem A eq_A) ((set_rem A eq_A) vs v) v = false.
37 | Proof. intros A eq_A vs. induction vs.
38 | - intros v H. reflexivity.
39 | - intros v H. simpl. rewrite if_f_app. rewrite if_app. simpl.
40 | rewrite IHvs. destruct (eq_A v a) ; reflexivity. apply H. Qed.
41 |
42 | Fixpoint set_fold_left (A : Type) (B : Type) (fncn : B -> A -> B) (s : set A) (init : B) : B :=
43 | match s with
44 | | S0 _ => init
45 | | St _ v s' => set_fold_left A B fncn s' (fncn init v)
46 | end.
47 |
48 | Fixpoint union2 (A : Type) (eq_A : A -> A -> bool) (a b : set A) : set A :=
49 | match a with
50 | | S0 _ => b
51 | | St _ v s' => (union2 A eq_A) s' ((set_add A eq_A) b v)
52 | end.
53 |
54 | Fixpoint set_init (A : Type) (eq_A : A -> A -> bool) (fncn : nat -> A) (n : nat) : set A :=
55 | match n with
56 | | O => St _ (fncn O) (S0 _)
57 | | S n' => set_add _ eq_A (set_init _ eq_A fncn n') (fncn n)
58 | end.
59 |
60 | Fixpoint set_map (A : Type) (B : Type) (fncn : A -> B) (input : set A) : set B :=
61 | match input with
62 | | S0 _ => S0 _
63 | | St _ x xs => St _ (fncn x) (set_map _ _ fncn xs)
64 | end.
65 |
66 | Definition singleton (V : Type) (v : V) : set V := St _ v (S0 _).
67 |
68 | Fixpoint filter (V : Type) (pred : V -> bool) (vs : set V) : set V :=
69 | match vs with
70 | | S0 _ => S0 _
71 | | St _ v vs' => if pred v then St _ v (filter _ pred vs') else filter _ pred vs'
72 | end.
73 |
--------------------------------------------------------------------------------
/pygmentize/.gitignore:
--------------------------------------------------------------------------------
1 | pygments/
2 | inst/
3 |
--------------------------------------------------------------------------------
/pygmentize/Makefile:
--------------------------------------------------------------------------------
1 |
2 | PWD := $(shell pwd)
3 | PYG := $(PWD)/pygments
4 | PYGEXE := $(PYG)/inst/bin/pygmentize
5 | export PATH := $(PWD)/inst/bin:$(PATH)
6 |
7 | PYTHON_VERSION := $(shell python --version | cut -f2 -d' ' | cut -f1,2 -d'.')
8 | export PYTHONPATH := $(PWD)/inst/lib/python$(PYTHON_VERSION)/site-packages
9 |
10 | all: $(PYGEXE)
11 |
12 | clean:
13 | rm -rf $(PYG)/{build,dist,inst}
14 |
15 | $(PYG):
16 | git clone --branch 2.2.0 https://github.com/pygments/pygments.git
17 |
18 | $(PYG)/pygments/lexers/floorplan.py: floorplan.py $(PYG)
19 | cp $< $@
20 |
21 | $(PYGEXE): $(PYG)/pygments/lexers/floorplan.py
22 | mkdir -p inst
23 | cd $(PYG) && make mapfiles
24 | mkdir -p $(PYTHONPATH)
25 | cd $(PYG) && python setup.py install --prefix=$(PWD)/inst
26 |
27 |
--------------------------------------------------------------------------------
/pygmentize/README.md:
--------------------------------------------------------------------------------
1 | # Floorplan lexer for pygmentize
2 |
3 | The Makefile here clones the [pygments repo](https://github.com/pygments/pygments)
4 | and sets up the Floorplan lexer for use with the `pygmentize` command. To build
5 | and pygmentize do the following:
6 |
7 | ```bash
8 | $ cd pygmentize && make all
9 | ...
10 | $ export PYTHONPATH=`pwd`/inst/lib/python$(python --version | cut -f2 -d' ' | cut -f1,2 -d'.')/site-packages
11 | $ ./inst/bin/pygmentize -l floorplan ../examples/immix/src/heap/layout.flp
12 | ```
13 |
14 |
--------------------------------------------------------------------------------
/pygmentize/floorplan.py:
--------------------------------------------------------------------------------
1 | from pygments.lexer import RegexLexer
2 | from pygments.token import *
3 | import re
4 |
5 | __all__ = ['FloorplanLexer']
6 |
7 | class FloorplanLexer(RegexLexer):
8 | name = 'Floorplan'
9 | aliases = ['floorplan']
10 | filenames = ['*.flp']
11 |
12 | flags = re.MULTILINE | re.UNICODE
13 |
14 | reserved = ( 'ptr', 'flags', '_', 'Counter', 'bit'
15 | , 'bits', 'byte', 'bytes', 'sizeof', 'List'
16 | , 'word', 'words', 'page', 'pages'
17 | , 'enum', 'union', 'seq', 'choice')
18 |
19 | tokens = {
20 | 'root': [
21 | # Whitespace:
22 | (r'\s+', Text),
23 | # Comments:
24 | (r'\/\/.*$', Comment.Single),
25 | (r'/\*', Comment.Multiline, 'comment'),
26 |
27 | # Identifiers:
28 | (r'(%s)\b' % '|'.join(reserved), Keyword.Reserved),
29 | (r'[A-Z]\w*', Keyword.Type),
30 | (r'[a-z_]\w*', Name),
31 |
32 | # Operators:
33 | (r'({|}|#|@|\^|=>|\.\.\.|\.|->|:|,|=\||~\||\|)', Operator.Word),
34 | (r'[-+\*\/\[\]\<\>]', Operator),
35 |
36 | # Numeric Literals:
37 | (r'0[xX][\da-fA-F]+', Number.Hex), # hex literals
38 | (r'0[bB][01]+', Number.Bin), # binary literals
39 | (r'\d+', Number.Integer), # decimal literals
40 |
41 | # Special:
42 | (r'[\(\)]', Operator), # parentheses
43 | ],
44 | 'comment': [
45 | # Multiline Comments
46 | (r'[^\*/]+', Comment.Multiline),
47 | (r'/\*', Comment.Multiline, '#push'),
48 | (r'\*/', Comment.Multiline, '#pop'),
49 | (r'[\*/]', Comment.Multiline),
50 | ],
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/src/Language/Floorplan.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan
2 | ( module Language.Floorplan.Syntax
3 | , module Language.Floorplan.Parser
4 | , module Language.Floorplan.Token
5 | ) where
6 | import Language.Floorplan.Syntax
7 | import Language.Floorplan.Parser
8 | import Language.Floorplan.Token
9 |
10 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/C/Compiler.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE QuasiQuotes, TemplateHaskell, BangPatterns #-}
2 | module Language.Floorplan.C.Compiler
3 | ( genC, writeCFile
4 | ) where
5 |
6 | import System.IO (IOMode(..), openFile, hClose)
7 | import Data.Typeable (Typeable(..))
8 | import Data.Text.Lazy.IO (hPutStr)
9 | import Data.Text.Lazy (pack)
10 |
11 | import Language.C.Quote
12 | import qualified Language.C.Parser as P
13 | import qualified Language.C.Parser.Tokens as T
14 | import qualified Language.C.Syntax as C
15 |
16 | import qualified Language.C.Pretty as PrettyC
17 | import Text.PrettyPrint.Mainland (prettyLazyText)
18 | import Text.PrettyPrint.Mainland.Class (ppr)
19 |
20 | import Language.Floorplan.Core.Syntax
21 |
22 | import Data.Bifunctor ( bimap )
23 | import Data.Functor ( ($>) )
24 | import Data.Ord (comparing)
25 | import Data.List (sortBy, nub, inits, intersperse)
26 | import Data.Char (toUpper, toLower)
27 | import Data.Maybe (isJust, fromMaybe, catMaybes)
28 | import Data.Bits
29 | import qualified Debug.Trace as D
30 |
31 | import Language.C.Quote.C
32 | import qualified Language.C.Syntax as S
33 | import Data.Loc -- (SrcLoc(..), noLoc)
34 |
35 | import Text.RE.TDFA.String (RE(..), (?=~), matched)
36 |
37 | -- TODO: Introduce spans based on position in these Haskell files.
38 | fakeLoc = noLoc --Span (Position 0 0 0) (Position 0 0 0)
39 | fL = fakeLoc
40 |
41 | justName :: [([NameID], BaseExp)] -> BaseExp -> Maybe ([NameID], BaseExp)
42 | justName [] (n ::: e) = Just ([n], e)
43 | justName ((ns,_):_) (n ::: e) = Just (n : ns, e)
44 | justName _ _ = Nothing
45 |
46 | justNameExp :: BaseExp -> Maybe (NameID, BaseExp)
47 | justNameExp (n ::: e) = Just (n, e)
48 | justNameExp _ = Nothing
49 |
50 | -- | Get all of the unique name identifiers in the spec and their
51 | -- corresponding subexpression.
52 | findNames :: [BaseExp] -> [([NameID], BaseExp)]
53 | findNames = nub . concatMap (accumTopDown justName)
54 |
55 | mkInitConst s = (Nothing, ExpInitializer (Const s fL) fL)
56 |
57 | mkStrConst t = S.StringConst ['"' : t ++ "\""] t fakeLoc
58 |
59 | mkPoundDefs' :: Int -> [NameID] -> [Definition]
60 | mkPoundDefs' i (n:ns) = EscDef ("#define " ++ n ++ " ((unsigned int)" ++ show i ++ ")") fL : (mkPoundDefs' (i+1) ns)
61 | mkPoundDefs' _ [] = []
62 |
63 | mkPoundDefs = mkPoundDefs' 0
64 |
65 | -- TODO: produce a warning if there's a regex which doesn't match
66 | -- on any of the output types.
67 |
68 | doFilterOut :: [String] -> [RE] -> [Maybe String]
69 | doFilterOut types res0 = let
70 | -- | Does the given type match any of the provided regular expressions?
71 | -- If so, remove it.
72 | dFO :: String -> [RE] -> [Maybe String]
73 | dFO t [] = [Just t] -- No matches. Keep it.
74 | dFO t (re:res)
75 | | matched (t ?=~ re) = [Nothing] -- It matched. Remove it.
76 | | otherwise = dFO t res -- Check the rest of the regular expressions.
77 | in concatMap (\f -> f res0) (map dFO types)
78 |
79 | -- | Wholly filtered out types (ones resulting a FLP_ALL_* decl with an empty RHS of the #define)
80 | -- probably imply that there were unused types / dead code in the FLP spec. Either need to
81 | -- report a warning in such a case, or write a preprocessing analysis pass that checks that this
82 | -- won't happen.
83 | mkAllPrefixes :: [BaseExp] -> [RE] -> [S.Definition]
84 | mkAllPrefixes bes res = let
85 |
86 | ns = findNames bes
87 | uniques = nub $ concatMap fst ns
88 |
89 | -- Tuples of the constructed type identifier and the list of names from which it was constructed.
90 | types0 :: [String]
91 | types0 = map mkTypeIdentifier ns
92 |
93 | findFor :: String -> [(([NameID], BaseExp), Maybe String)] -> [String]
94 | findFor u [] = []
95 | findFor u ((tuple@(n_qual, _), Just _):ns0) -- Not filtered out - make and keep the identifier if it involves the type 'u'.
96 | | u `elem` n_qual = mkTypeIdentifier tuple : findFor u ns0
97 | | otherwise = findFor u ns0
98 | findFor u (((_, _), Nothing):ns0) = findFor u ns0 -- This particular name is filtered out, as indicated by the Nothing
99 |
100 | mkAll :: (String, [String]) -> S.Definition
101 | mkAll (unique_ty, paths)
102 | = EscDef ("#define FLP_ALL_" ++ unique_ty ++ " " ++ (concat . intersperse ", " . map ("FLP_" ++) $ paths)) fL
103 |
104 | in map mkAll (zip uniques (map (\f -> f $ zip ns (doFilterOut types0 res)) (map findFor uniques)))
105 |
106 | -- | (["A", "B", "C"], _) becomes "C_B_A"
107 | mkTypeIdentifier :: ([NameID], BaseExp) -> String
108 | mkTypeIdentifier = concat . intersperse "_" . reverse . fst
109 |
110 | genC :: [BaseExp] -> [RE] -> [S.Definition]
111 | genC bes res = let
112 | types0 = map mkTypeIdentifier (findNames bes)
113 | types = "FLP_UNMAPPED" : (map ("FLP_" ++) $ catMaybes $ doFilterOut types0 res)
114 | num_types = [EscDef ("#define __FLP_NUM_VALID_TYPES ((unsigned int)" ++ show (length types) ++ ")") fL]
115 | xs = map (mkInitConst . mkStrConst) types
116 | pound_defs = mkPoundDefs types
117 | initializer = CompoundInitializer xs fL
118 | all_prefixes = mkAllPrefixes bes res
119 | in pound_defs ++ num_types ++ [cunit|
120 | static const char* const __FLP_TYPES[] = $init:initializer;
121 | |] ++ all_prefixes
122 | --"PC_UNMAPPED", "PC_SOMETHING_ELSE" };
123 |
124 | -- TODO: work with Text instead of String
125 |
126 | -- writeSourceFile :: (Monoid a, Typeable a) => Handle -> SourceFile a -> IO ()
127 | -- | The pair of strings are the header and footer and to append
128 | writeCFile :: FilePath -> (String, String) -> [S.Definition] -> IO ()
129 | writeCFile outdir (header, footer) c_code = do
130 | fd <- openFile outdir WriteMode
131 | hPutStr fd $ pack header
132 | hPutStr fd (prettyLazyText 160 $ ppr c_code) -- 160-character wide terminal viewing width of source files (it's the 2020 people)
133 | hPutStr fd $ pack footer
134 | hClose fd
135 |
136 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Core/Compiler.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan.Core.Compiler where
2 | import Language.Floorplan.Core.Syntax hiding (accum)
3 | import Language.Floorplan.Syntax
4 | import qualified Data.Map.Strict as M
5 | import Data.List (sort)
6 | import Data.Maybe (fromJust, isJust)
7 | import Debug.Trace as D
8 |
9 | getNames :: Demarc -> [String]
10 | getNames = let
11 | gN (Field n _) = Just n
12 | gN (Layer { name = n }) = Just n
13 | gN _ = Nothing
14 | in accum gN
15 |
16 | -- | This can be done before or after grafting phase. It checks that every @Layer@ and @Field@
17 | -- has a globally unique name, reporting an error when the name is not unique.
18 | checkUniqNames :: [Demarc] -> [String]
19 | checkUniqNames ds =
20 | let ns = foldr (\a b -> getNames a ++ b) [] ds
21 |
22 | -- Assumes the input list is already sorted (duplicates are adjacent)
23 | getDups [] = []
24 | getDups (x:[]) = []
25 | getDups (x:y:ys)
26 | | x == y = y : getDups (dropWhile (== y) ys)
27 | | otherwise = getDups (y:ys)
28 |
29 | in getDups $ sort ns
30 |
31 | countGrafting :: [Demarc] -> Int
32 | countGrafting = let
33 | fn (Graft{}) = True
34 | fn _ = False
35 | in sum . map (countMatches fn)
36 |
37 | -- | How many bytes are required for an Enum containing the given fields.
38 | -- This computes ceiling(log_2(length(flags)) / 8).
39 | enumBytes :: [FlagID] -> Int
40 | enumBytes fs
41 | | len == 1 = 1
42 | | otherwise = (bytes 1)
43 | where len = length fs
44 | bytes acc
45 | | len <= acc = 0
46 | | otherwise = 1 + (bytes (acc * 256))
47 |
48 | -- | TODO: parametrized delta_prim for architecture-dependent types
49 | delta_prim :: Primitive -> Int
50 | delta_prim Bit = 1
51 | delta_prim Byte = 8
52 | delta_prim Word = 8 * (delta_prim Byte)
53 | delta_prim Page = 4096 * (delta_prim Byte)
54 |
55 | pow :: Int -> Int -> Int
56 | pow b e
57 | | e < 0 = error "exponentiation with negative exponent"
58 | | e == 0 && b == 0 = error "indeterminate exponent"
59 | | e == 0 = 1
60 | | e > 0 = b * (pow b (e - 1))
61 |
62 | -- | Computes over arithmetic expressions in terms of literals
63 | delta_lit :: LitArith -> Int
64 | delta_lit (Plus l r) = (+) (delta_lit l) (delta_lit r)
65 | delta_lit (Minus l r) = (-) (delta_lit l) (delta_lit r)
66 | delta_lit (Times l r) = (*) (delta_lit l) (delta_lit r)
67 | delta_lit (Div l r) = quot (delta_lit l) (delta_lit r)
68 | delta_lit (Exponent b e) = pow (delta_lit b) (delta_lit e)
69 | delta_lit (Lit l) = l
70 |
71 | -- | Computes number of bits
72 | delta :: SizeArith -> Int
73 | delta (SizePlus l r) = (+) (delta l) (delta r)
74 | delta (SizeMinus l r) = (-) (delta l) (delta r)
75 | delta (SizeLit Nothing p) = delta_prim p
76 | delta (SizeLit (Just lit) p) = (delta_lit lit) * (delta_prim p)
77 |
78 | delta_bits = delta
79 |
80 | bits2bytesUP v
81 | | v == 0 = 0
82 | | v <= 8 = 1
83 | | otherwise = 1 + bits2bytesUP (v - 8)
84 |
85 | -- | Rounds up @SizeArith@ to the nearest whole byte.
86 | delta_bytes :: SizeArith -> Int
87 | delta_bytes sz = bits2bytesUP (delta sz)
88 |
89 | fresh :: Demarc -> FormalID
90 | fresh d = head (dropWhile (flip elem $ free_vars d) $ map (("var_" ++) . show) [0..])
91 |
92 | compile :: Demarc -> BaseExp
93 | compile (Enum fs) = Attr (BaseType $ EnumBT fs) (Prim $ enumBytes fs)
94 | compile (Bits fs) = let fs' = zip (map fst fs) $ map (delta_bits . snd) fs
95 | in Attr (BaseType $ BitsBT fs') (Prim $ bits2bytesUP $ sum $ map snd fs')
96 | compile (Union []) = Prim 0
97 | compile (Union (d:ds)) = foldl (:||) (compile d) $ map compile ds
98 | compile (Seq []) = Prim 0
99 | compile (Seq (d:ds)) = foldl (:+) (compile d) $ map compile ds
100 | compile (PtrF field) = Attr (BaseType $ PtrBT field) $ compile (Blob $ SizeLit Nothing Word)
101 | compile (PtrL layer) = Attr (BaseType $ PtrBT layer) $ compile (Blob $ SizeLit Nothing Word)
102 | compile (Blob sz) = Attr (BaseType $ SizeBT sz) $ Prim $ delta_bytes sz
103 | compile (Field f d) = (:::) f (compile d)
104 | compile (Pound d) = let f = fresh d
105 | in Exists f $ f :# (compile d)
106 | compile (Repetition f d) = f :# (compile d)
107 | compile l@(Layer
108 | { name = n
109 | , formals = fs
110 | , magnitude = m
111 | , alignment = a
112 | , magAlign = ma
113 | , contains = cs
114 | , rhs = d
115 | }) = n ::: (exists $ mag $ align $ contains $ compile d)
116 | where exists :: BaseExp -> BaseExp
117 | exists e
118 | | null fs = e
119 | | otherwise = foldr Exists (Exists (head fs) e) (tail fs)
120 | mag
121 | | isJust m = Con (delta_bytes $ fromJust m)
122 | | isJust ma = Con (delta_bytes $ fromJust ma)
123 | | otherwise = id
124 | align
125 | | isJust a = (flip (:@)) (delta_bytes $ fromJust a)
126 | | isJust ma = (flip (:@)) (delta_bytes $ fromJust ma)
127 | | otherwise = id
128 | contains :: BaseExp -> BaseExp
129 | contains e
130 | | null cs = e
131 | | otherwise = foldr Attr (Attr (Contains $ head cs) e) (map Contains $ tail cs)
132 | compile (Graft _) = error "Grafting is illegal during the compile phase."
133 |
134 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Core/Syntax.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 | module Language.Floorplan.Core.Syntax where
3 |
4 | import Data.Maybe (maybeToList)
5 |
6 | import Language.Floorplan.Syntax (LayerID, FormalID, FlagID, FieldID, SizeArith(..))
7 |
8 | type Nat = Int -- >= 0
9 | type Align = Int -- >= 1
10 |
11 | type ExistsID = String -- ^ Formal identifiers (bound by Exists)
12 | type NameID = String -- ^ Names of bound layers and fields
13 |
14 | data Exp a
15 | = Prim Nat -- ^ Primitive number of bytes
16 | | Con Nat (Exp a) -- ^ Constrained
17 | | (:@) (Exp a) Align -- ^ Alignment
18 | | (:+) (Exp a) (Exp a) -- ^ Sequencing
19 | | (:||) (Exp a) (Exp a) -- ^ Union / alternation
20 | | (:::) NameID (Exp a) -- ^ Layer (and field) name binding
21 | | Exists ExistsID (Exp a) -- ^ Formal name binding
22 | | (:#) ExistsID (Exp a) -- ^ Repetitions
23 | | Attr a (Exp a) -- ^ Extensible attributes
24 | deriving (Eq, Ord, Show)
25 |
26 | data Attribute ty
27 | = Contains NameID
28 | | BaseType ty
29 | deriving (Eq, Ord, Show)
30 |
31 | data BaseType
32 | = EnumBT [FlagID]
33 | | BitsBT [(NameID, Int)]
34 | | PtrBT NameID
35 | | SizeBT SizeArith
36 | deriving (Eq, Ord, Show)
37 |
38 | -- | Default core expression type for FLP compiler targeting Rust library
39 | -- (with contains(...) attibutes):
40 | type BaseExp = Exp (Attribute BaseType)
41 |
42 | mTL :: Maybe a -> [a]
43 | mTL = maybeToList
44 |
45 | accumTopDown' :: [a] -> ([a] -> Exp b -> Maybe a) -> Exp b -> [a]
46 | accumTopDown' as fn e@(Prim{}) = mTL (fn as e)
47 | accumTopDown' as fn e1@(Con _ e2) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
48 | accumTopDown' as fn e1@(e2 :@ _) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
49 | accumTopDown' as fn e1@(e2 :+ e3) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2 ++ accumTopDown' (mTL (fn as e1) ++ as) fn e3
50 | accumTopDown' as fn e1@(e2 :|| e3) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2 ++ accumTopDown' (mTL (fn as e1) ++ as) fn e3
51 | accumTopDown' as fn e1@(_ ::: e2) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
52 | accumTopDown' as fn e1@(Exists _ e2) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
53 | accumTopDown' as fn e1@(_ :# e2) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
54 | accumTopDown' as fn e1@(Attr _ e2) = mTL (fn as e1) ++ accumTopDown' (mTL (fn as e1) ++ as) fn e2
55 |
56 | -- | Accumulate the results of a function operating over each Demarc in the Demarc IR tree,
57 | -- where the function also takes a list of the results of the same function applied to
58 | -- each of the ancestors of the current Demarc IR node, with the list in order from least
59 | -- ancestor (closest) to greatest ancestor.
60 | accumTopDown = accumTopDown' []
61 |
62 | -- | Accumulate the results of applying some function to
63 | -- every node in the Exp AST.
64 | accum :: (Exp a -> Maybe b) -> Exp a -> [b]
65 | accum fn e@(Prim{}) = maybeToList (fn e)
66 | accum fn e1@(Con _ e2) = maybeToList (fn e1) ++ accum fn e2
67 | accum fn e1@(e2 :@ _) = maybeToList (fn e1) ++ accum fn e2
68 | accum fn e1@(e2 :+ e3) = maybeToList (fn e1) ++ accum fn e2 ++ accum fn e3
69 | accum fn e1@(e2 :|| e3) = maybeToList (fn e1) ++ accum fn e2 ++ accum fn e3
70 | accum fn e1@(_ ::: e2) = maybeToList (fn e1) ++ accum fn e2
71 | accum fn e1@(Exists _ e2) = maybeToList (fn e1) ++ accum fn e2
72 | accum fn e1@(_ :# e2) = maybeToList (fn e1) ++ accum fn e2
73 | accum fn e1@(Attr _ e2) = maybeToList (fn e1) ++ accum fn e2
74 |
75 | countExpNodes :: Exp a -> Int
76 | countExpNodes e = length $ accum (const $ Just ()) e
77 |
78 | -- | Call the given function on all subexpressions. Good for fixedpoint
79 | -- functions calling on themselves in recursive case where they don't
80 | -- care which type they see. This is slightly dangerous in the case where
81 | -- something gets added to the core calculus. If this happens, PLEASE
82 | -- check all callers of this function to see if they should handle the
83 | -- new case personally.
84 | callSub :: (Exp a -> [b]) -> Exp a -> [b]
85 | callSub fn (Prim{}) = []
86 | callSub fn (Con _ e) = fn e
87 | callSub fn (e :@ _) = fn e
88 | callSub fn (e1 :+ e2) = fn e1 ++ fn e2
89 | callSub fn (e1 :|| e2) = fn e1 ++ fn e2
90 | callSub fn (_ ::: e) = fn e
91 | callSub fn (Exists _ e) = fn e
92 | callSub fn (_ :# e) = fn e
93 | callSub fn (Attr _ e) = fn e
94 |
95 | plus :: Maybe Int -> Maybe Int -> Maybe Int
96 | plus a b = do
97 | a' <- a
98 | b' <- b
99 | return $ a' + b'
100 |
101 | -- | Conservatively computes the size of the given expression,
102 | -- returning Nothing when a fixed size isn't easily known.
103 | expSize :: Exp a -> Maybe Int
104 | expSize (Prim n) = return n
105 | expSize (Con n _) = return n
106 | expSize (e :@ _) = expSize e
107 | expSize (e1 :+ e2) = expSize e1 `plus` expSize e2
108 | expSize (e1 :|| e2) =
109 | let s1 = expSize e1
110 | s2 = expSize e2
111 | in if s1 == s2 then s1 else Nothing
112 | expSize (_ ::: e) = expSize e
113 | expSize (Exists _ e) = expSize e
114 | expSize (_ :# _) = Nothing -- Conservative assumption
115 | expSize (Attr _ e) = expSize e
116 |
117 | -- | Just like @accum@, but also tracks the number of bytes of memory
118 | -- that come before the subexpression for which we call the fncn.
119 | l2r :: forall a b. (Maybe Nat -> Exp a -> Maybe b) -> Exp a -> [b]
120 | l2r fn e' = let
121 |
122 | mTL :: Maybe Nat -> Exp a -> [b]
123 | mTL i = maybeToList . fn i
124 |
125 | lr :: Maybe Nat -> Exp a -> [b]
126 | lr i e@(Prim{}) = mTL i e
127 | lr i e1@(Con _ e2) = mTL i e1 ++ lr i e2
128 | lr i e1@(e2 :@ _) = mTL i e1 ++ lr i e2
129 | lr i e1@(e2 :+ e3) = mTL i e1 ++ (lr i e2 ++ lr (i `plus` expSize e2) e3)
130 | lr i e1@(e2 :|| e3) = mTL i e1 ++ (lr i e2 ++ lr i e3)
131 | lr i e1@(_ ::: e2) = mTL i e1 ++ lr i e2
132 | lr i e1@(Exists _ e2) = mTL i e1 ++ lr i e2
133 | lr i e1@(_ :# e2) = mTL i e1 ++ lr Nothing e2
134 | lr i e1@(Attr _ e2) = mTL i e1 ++ lr i e2
135 |
136 | in lr (Just 0) e'
137 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Preproc/Types.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan.Preproc.Types where
2 | import Language.Floorplan.Core.Syntax hiding (accum)
3 | import Language.Floorplan.Syntax
4 | import qualified Data.Map.Strict as M
5 | import Data.List (sort)
6 | import Data.Maybe (fromJust, isJust)
7 | import Debug.Trace as D
8 |
9 | data PreprocError =
10 | Recursive [String] -- ^ String is a grafting ID that's part of a recursive path.
11 | | UndefinedSymbol String -- ^ String is the symbol that's undefined.
12 | | DuplicateAttribute [ScopeAttribute] -- ^ The list of attributes contains unnecessary duplicates.
13 | | UnbalancedScope Int -- ^ %begin / %end are unbalanced.
14 | | RegexError String -- ^ A regex was unable to be compiled.
15 | | InvalidAttribute String -- ^ String is an error message.
16 | deriving (Eq, Ord, Show)
17 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Rust.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan.Rust
2 | ( module Language.Floorplan.Rust.Compiler
3 | ) where
4 | import Language.Floorplan.Rust.Compiler
5 |
6 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Rust/Common.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan.Rust.Common
2 | where
3 | {- Functions and names applicable to all code gen phases / passes
4 | -}
5 |
6 | import Language.Rust.Parser as P
7 | import Language.Rust.Syntax as R
8 | import Language.Rust.Quote
9 | import Language.Rust.Data.Ident
10 | import Language.Rust.Data.Position
11 |
12 | import Language.Floorplan.Core.Syntax
13 |
14 | import Data.Functor ( ($>) )
15 | import Data.Ord (comparing)
16 | import Data.List (sortBy, nub)
17 | import Data.Char (toUpper, toLower)
18 |
19 | import Language.Floorplan.Rust.Types
20 | import qualified Debug.Trace as D
21 |
22 | bytesInWord :: Int
23 | bytesInWord = 8
24 |
25 | bitsInByte :: Int
26 | bitsInByte = 8
27 |
28 | isPow2 k = elem k $ map (2 ^) [0..k]
29 |
30 | -- | Log base `i` of `n` rounded up.
31 | log' :: Int -> Int -> Int
32 | log' i n = length (takeWhile (< n) (iterate (* 2) 1))
33 |
34 | log2 :: Int -> Int
35 | log2 = log' 2
36 |
37 | -- TODO: Introduce spans based on position in these Haskell files.
38 | fakeSpan = Span (Position 0 0 0) (Position 0 0 0)
39 | fS = fakeSpan
40 |
41 | -- TODO: put Language.Rust.* accessors in their own module:
42 | -- | Makes a Rust expression of from an integer
43 | mkIntExp :: Int -> R.Expr Span
44 | mkIntExp i = P.parse' $ P.inputStreamFromString $ show i
45 |
46 | mkBin' i
47 | | i == 0 = []
48 | | i `mod` 2 == 0 = '0' : mkBin' (i `div` 2)
49 | | i `mod` 2 == 1 = '1' : mkBin' (i `div` 2)
50 |
51 | mkBinU8 i
52 | | i >= 256 = error $ "Cannot make u8 from i=" ++ show i
53 | | otherwise = take (8 - (length $ mkBin' i)) (repeat '0') ++ (reverse $ mkBin' i)
54 |
55 | mkBinExp :: Int -> R.Expr Span
56 | mkBinExp i = P.parse' $ P.inputStreamFromString $ "0b" ++ mkBinU8 i
57 |
58 | offsetName :: NameID -> String
59 | offsetName n
60 | = allUpper n ++ "_OFFSET_BYTES"
61 |
62 | containsFnName :: NameID -> String
63 | containsFnName ns
64 | = "contains_" ++ firstLower ns
65 |
66 | firstLower [] = error "empty NameID!"
67 | firstLower (n:ns) = toLower n : ns
68 |
69 | firstUpper [] = error "empty NameID!"
70 | firstUpper (n:ns) = toUpper n : ns
71 |
72 | allUpper = map toUpper
73 |
74 | initSeqName :: [NameID] -> String
75 | initSeqName _ = "init_canonical_sequence"
76 |
77 | bumpAllocName :: NameID -> String
78 | bumpAllocName n
79 | = "bump_new_" ++ n
80 |
81 | bumpValidName :: NameID -> NameID -> String
82 | bumpValidName n1 n2
83 | = firstLower n1 ++ "_is_validly_before_" ++ firstLower n2
84 |
85 | initAfterName :: NameID -> NameID -> String
86 | initAfterName n1 n2
87 | = "init_" ++ firstLower n2 ++ "_after_" ++ firstLower n1
88 |
89 | memsetUntilName :: NameID -> NameID -> String
90 | memsetUntilName n1 n2
91 | = "memset_" ++ firstLower n1 ++ "_until_" ++ firstLower n2
92 |
93 | castFromName :: NameID -> NameID -> String
94 | castFromName from to
95 | = "cast_" ++ firstLower from ++ "_to_" ++ firstLower to
96 |
97 | fromIdxName :: NameID -> String
98 | fromIdxName from
99 | = firstLower from ++ "_from_idx"
100 |
101 | toIdxName :: NameID -> String
102 | toIdxName from
103 | = firstLower from ++ "_to_idx"
104 |
105 | logBytesName :: NameID -> String
106 | logBytesName n
107 | = "LOG_" ++ bytesName n
108 |
109 | bytesName :: NameID -> String
110 | bytesName n
111 | = "BYTES_IN_" ++ allUpper n
112 |
113 | -- | All named entities get an addrName:
114 | addrName xs = firstUpper xs ++ "Addr"
115 |
116 | addrEndName n = firstUpper n ++ "AddrEnd"
117 |
118 | enumName xs = "__FLP_IDX_" ++ map toUpper xs
119 |
120 | -- | Only for (known to be) fixed-width things:
121 | structName xs = firstUpper xs -- ++ "Struct"
122 |
123 | getterName xs = firstLower xs
124 |
125 | reverseGetterName xs = "from_" ++ firstLower xs
126 |
127 | skipperName ns = "skip_bytes_to_" ++ firstUpper ns
128 |
129 | jumperName ns = "jump_to_" ++ firstUpper ns
130 |
131 | nextName ns = "next_" ++ firstUpper ns
132 |
133 | bitsGetterName sz xs
134 | | sz == 1 = "get_" ++ xs ++ "_bit"
135 | | otherwise = "get_" ++ xs ++ "_bits"
136 |
137 | bitsFromerName _ [] = error "empty NameID!"
138 | bitsFromerName sz (x:xs)
139 | | sz == 1 = "set_" ++ x : xs ++ "_from_bool"
140 | | otherwise = "set_" ++ x : xs ++ "_from_u8"
141 |
142 | firstGetterName xs = "get_first_" ++ firstLower xs
143 |
144 | mkTy :: NameID -> Ty Span
145 | mkTy s = P.parse' $ P.inputStreamFromString s
146 |
147 | bytesAlignName n = allUpper n ++ "_BYTES_ALIGN"
148 |
149 | logAlignName n = allUpper n ++ "_LOG_BYTES_ALIGN"
150 |
151 | fieldPtrGetterName fs = "load_" ++ firstUpper fs ++ "Addr"
152 | fieldPtrSetterName fs = "store_" ++ firstUpper fs ++ "Addr"
153 |
154 | fieldStructGetterName fs = "load_" ++ firstUpper fs
155 | fieldStructSetterName fs = "store_" ++ firstUpper fs
156 |
157 | -- | Determine all existential-bound names brought into
158 | -- scope by this expression. Alignment and Constrained
159 | -- can be traversed because only a ":#" can refer to names.
160 | findExists :: BaseExp -> [BaseExp]
161 | findExists (Prim{}) = []
162 | findExists (Con _ e) = findExists e
163 | findExists (e :@ _) = findExists e
164 | findExists (_ :+ _) = []
165 | findExists (_ :|| _) = []
166 | findExists (_ ::: _) = []
167 | findExists e@(Exists _ e') = e : findExists e'
168 | findExists (_ :# _) = []
169 | findExists (Attr _ e) = findExists e
170 |
171 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Rust/Types.hs:
--------------------------------------------------------------------------------
1 | module Language.Floorplan.Rust.Types
2 | where
3 | import Language.Rust.Parser as P
4 | import Language.Rust.Syntax as R
5 | import Language.Rust.Quote
6 | import Language.Rust.Data.Ident
7 | import Language.Rust.Data.Position
8 |
9 | import Language.Floorplan.Core.Syntax
10 |
11 | -- | Stripped-down Rust Impl item with just the pieces we
12 | -- care about (name and item entries) so that we can merge lists of impls
13 | -- together by NameID equality.
14 | data RustItem =
15 | RustImpl
16 | NameID -- ^ e.g. "CellAddr"
17 | [ImplItem Span] -- ^ Contents of the Impl
18 | [Item Span] -- ^ Associated Items that go *outside* the `impl`
19 | | TopLevel [Item Span]
20 | deriving (Show)
21 |
22 | rustItemComparator (RustImpl n _ _) = n
23 | rustItemComparator (TopLevel _) = "______________________FLP__" -- TODO
24 |
25 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Semantics.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveAnyClass, DeriveGeneric, DeriveDataTypeable #-}
2 | module Language.Floorplan.Semantics where
3 |
4 | import Data.Maybe (fromJust)
5 | import Language.Floorplan.Core.Syntax
6 |
7 | data Tree =
8 | T Tree Tree
9 | | N String Tree
10 | | B0 | B1
11 | deriving (Eq, Ord, Show)
12 |
13 | leaves (T t1 t2) = leaves t1 + leaves t2
14 | leaves (N _ t) = leaves t
15 | leaves B0 = 0
16 | leaves B1 = 1
17 |
18 | addStep s xs = map (\(r,d) -> (r, s : d)) xs
19 |
20 | g p0 p1 p2 p3 = let
21 |
22 | g' a m theta ((:+) e1 e2) =
23 | let params = \r1 -> (a + leaves r1, m - leaves r1, theta, e2)
24 | in
25 | [ (T r1 r2, ("Plus: i = 0.." ++ show m ++ ", Pause (g " ++ show (params r1) ++ ")") : deriv1
26 | ++ ["Resume (g " ++ show (params r1) ++ ")" ++ ", leaves r1 = " ++ show (leaves r1)] ++ deriv2)
27 | | (r1, deriv1) <- concat [ addStep ("Plus_r1: Choose i=" ++ show i) $ g a i theta e1 | i <- [0..m]]
28 | , (r2, deriv2) <- addStep ("Plus_r2: leaves(" ++ show e1 ++ ")=" ++ show (leaves r1)) $ g (a + leaves r1) (m - leaves r1) theta e2
29 | ]
30 | g' a m theta ((:||) e1 e2) = let
31 | r1 = addStep ("Or_e1") (g a m theta e1)
32 | r2 = addStep ("Or_e2") (g a m theta e2)
33 | in r1 ++ r2
34 | g' a m theta (Prim n)
35 | | m == n = [(foldl T B0 (take n $ repeat B1), ["Prim: Evaluate " ++ show n ++ "-leaf tree"])]
36 | | otherwise = []
37 | g' a m theta (Con n e)
38 | | m == n = addStep ("Con: " ++ show n) (g a m theta e)
39 | | m /= n = []
40 | g' a m theta ((:@) e aln)
41 | | a `mod` aln == 0 = g a m theta e
42 | | otherwise = []
43 | g' a m theta ((:::) l e) =
44 | [ (N l r, ("HasType: " ++ l) : deriv)
45 | | (r, deriv) <- g a m theta e
46 | ]
47 | g' a m theta (Exists f e) = concat
48 | [ addStep ("Exists: Choose " ++ f ++ "=" ++ show i)
49 | $ g a m ((f, i) : theta) e | i <- [0 .. m] ]
50 | g' a m theta ((:#) f e)
51 | | (Just m) == lookup f theta && m == 0 = [(T B0 B0, ["Pound: m = 0"])]
52 | | (Just 0) == lookup f theta = []
53 | | otherwise = let -- Implies lookup theta f > 0
54 | params = \r1 -> (a + leaves r1, m - leaves r1, (f, (fromJust $ lookup f theta) - 1) : theta, (:#) f e)
55 | in case lookup f theta of
56 | Nothing -> error "Nothing"
57 | Just thetaF -> [ (T r1 r2, ("Pound: theta(" ++ f ++ ")=" ++ show thetaF ++ ", Pause (g " ++ show (params r1)) : deriv1
58 | ++ ["Resume (g " ++ show (params r1) ++ ")"] ++ deriv2)
59 | | (r1, deriv1) <- concat [addStep ("Pound_r1: Choose i=" ++ show i) $ g a i theta e | i <- [0..m]]
60 | , (r2, deriv2) <- addStep ("Pound_r2: leaves(" ++ show e ++ ")=" ++ show (leaves r1)) $
61 | g (a + leaves r1) (m - leaves r1) ((f, (fromJust $ lookup f theta) - 1) : theta) ((:#) f e)
62 | ]
63 |
64 | addLabel (r, d:ds) = (r, (d ++ "[[g " ++ show (p0, p1, p2, p3) ++ "]] ") : ds)
65 |
66 | in map addLabel (g' p0 p1 p2 p3)
67 |
68 |
--------------------------------------------------------------------------------
/src/Language/Floorplan/Token.x:
--------------------------------------------------------------------------------
1 | {
2 | {-# OPTIONS_GHC -w #-}
3 | module Language.Floorplan.Token (Token(..), scanTokens) where
4 | import Language.Floorplan.Syntax
5 | import qualified Debug.Trace as D
6 | }
7 |
8 | %wrapper "basic"
9 |
10 | $digit = 0-9
11 | $alpha = [a-zA-Z]
12 | $eol = [\n]
13 |
14 | tokens :-
15 | $eol ;
16 | $white+ ;
17 | "//".* ;
18 | seq { \s -> TokenSeq }
19 | union { \s -> TokenUnion }
20 | contains { \s -> TokenContains }
21 | bits { \s -> TokenBits }
22 | bytes { \s -> TokenBytes }
23 | words { \s -> TokenWords }
24 | pages { \s -> TokenPages }
25 | ptr { \s -> TokenPtr }
26 | enum { \s -> TokenEnum }
27 | "->" { \s -> TokenArrow }
28 | "@(" { \s -> TokenAtParen }
29 | ")@" { \s -> TokenParenAt }
30 | "@|" { \s -> TokenAtBar }
31 | "|@" { \s -> TokenBarAt }
32 | "<$" { \s -> TokenLessD }
33 | "$>" { \s -> TokenGreaterD }
34 | \( { \s -> TokenLParen }
35 | \) { \s -> TokenRParen }
36 | \< { \s -> TokenLess }
37 | \> { \s -> TokenGreater }
38 | \| { \s -> TokenBar }
39 | \|\| { \s -> TokenBarBar }
40 | \{ { \s -> TokenLCurl }
41 | \} { \s -> TokenRCurl }
42 | \# { \s -> TokenPound }
43 | \: { \s -> TokenColon }
44 | \, { \s -> TokenComma }
45 | [\+] { \s -> TokenPlus }
46 | [\-] { \s -> TokenMinus }
47 | [\*] { \s -> TokenTimes }
48 | [\/] { \s -> TokenDiv }
49 | [\^] { \s -> TokenExponent }
50 | "%begin" { \s -> TokenBeginScope }
51 | "%end" { \s -> TokenEndScope }
52 | "%filterout" { \s -> TokenFilterOut } -- filter out rules
53 | noglobal { \s -> TokenNoGlobal }
54 | \"[^\"]*\" { \s -> TokenStringLiteral $ tail $ init s }
55 | $digit+ { \s -> TokenNum (read s) }
56 | 0b [01]+ { \s -> TokenNum (bin2int s) }
57 | [a-z] [$alpha $digit \_]* { \s -> TokenLowerID s }
58 | [A-Z] [$alpha $digit \_]* { \s -> TokenUpperID s}
59 |
60 | {
61 | data Token =
62 | TokenSeq
63 | | TokenUnion
64 | | TokenContains
65 | | TokenBits
66 | | TokenBytes
67 | | TokenWords
68 | | TokenPages
69 | | TokenPtr
70 | | TokenEnum
71 | | TokenArrow
72 | | TokenAtParen
73 | | TokenParenAt
74 | | TokenAtBar
75 | | TokenBarAt
76 | | TokenLParen
77 | | TokenRParen
78 | | TokenLess
79 | | TokenGreater
80 | | TokenLessD
81 | | TokenGreaterD
82 | | TokenBar
83 | | TokenBarBar
84 | | TokenLCurl
85 | | TokenRCurl
86 | | TokenPound
87 | | TokenColon
88 | | TokenComma
89 | | TokenPlus
90 | | TokenMinus
91 | | TokenTimes
92 | | TokenDiv
93 | | TokenExponent
94 | | TokenNum Int
95 | | TokenUpperID String
96 | | TokenLowerID String
97 | | TokenBeginScope | TokenEndScope | TokenNoGlobal
98 | | TokenStringLiteral String | TokenFilterOut
99 | | TokenHeader String | TokenFooter String
100 | deriving (Eq, Ord, Show)
101 |
102 | -- | Returns a tuple of all contents before the first instance of '%end' along with
103 | -- the rest of the input string *after* the '%end'.
104 | getUntilEnd :: String -> String -> (String, String)
105 | getUntilEnd _ ('%':'e':'n':'d':rest) = ("", rest)
106 | getUntilEnd kind "" = error $ "Reached end of input while parsing " ++ kind
107 | getUntilEnd kind (c:cs) =
108 | let (got, rest) = getUntilEnd kind cs
109 | in (c : got, rest)
110 |
111 | getHeader = getUntilEnd "header"
112 | getFooter = getUntilEnd "footer"
113 |
114 | -- | Special case for parsing C-like or Rust-like headers and footers of output.
115 | scanTokens :: String -> [Token]
116 | scanTokens "" = []
117 | scanTokens s =
118 | case alexScan ('\n',[],s) 0 of
119 | AlexEOF -> []
120 | (AlexError e) -> error $ show e -- TODO: bad failure mode
121 | (AlexSkip (_,_,s') _) -> scanTokens s'
122 | (AlexToken (_,_,s') len act) ->
123 | case act (take len s) of
124 | TokenBeginScope -> checkBegin s'
125 | tok -> tok : scanTokens s' -- Fall-through case
126 |
127 | checkBegin :: String -> [Token]
128 | checkBegin s =
129 | case alexScan ('\n', [], s) 0 of
130 | AlexEOF -> []
131 | (AlexError e) -> error $ show e -- TODO: bad failure mode
132 | (AlexSkip (_,_,s') _) -> checkBegin s'
133 | (AlexToken (_,_,s') len act) ->
134 | case act (take len s) of
135 | TokenLowerID "header" ->
136 | let (header, s'') = getHeader s'
137 | in TokenHeader header : scanTokens s''
138 | TokenLowerID "footer" ->
139 | let (footer, s'') = getFooter s'
140 | in TokenFooter footer : scanTokens s''
141 | -- Fall-through case: it wasn't a header or a footer, so we need
142 | -- to include the '%begin' scope token that we already parsed in the input.
143 | tok -> TokenBeginScope : tok : scanTokens s'
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/src/Language/Rust/Data/Ident.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Data.Ident
3 | Description : Identifiers
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : portable
9 |
10 | Data structure behind identifiers.
11 | -}
12 | {-# LANGUAGE DeriveDataTypeable #-}
13 | {-# LANGUAGE DeriveGeneric #-}
14 | {-# LANGUAGE DeriveAnyClass #-}
15 |
16 | module Language.Rust.Data.Ident (Ident(..), mkIdent, Name, IdentName(..) ) where
17 |
18 | import GHC.Generics ( Generic )
19 |
20 | import Control.DeepSeq ( NFData )
21 | import Data.Data ( Data )
22 | import Data.Typeable ( Typeable )
23 |
24 | import Data.List ( foldl' )
25 | import Data.Char ( ord )
26 | import Data.String ( IsString(..) )
27 | --import Data.Semigroup as Sem
28 |
29 | -- | An identifier
30 | data Ident
31 | = Ident { name :: IdentName -- ^ payload of the identifier
32 | , raw :: Bool -- ^ whether the identifier is raw
33 | , hash :: {-# UNPACK #-} !Int -- ^ hash for quick comparision
34 | } deriving (Data, Typeable, Generic, NFData)
35 |
36 | -- | Shows the identifier as a string (for use with @-XOverloadedStrings@)
37 | instance Show Ident where
38 | show = show . name
39 |
40 | instance IsString Ident where
41 | fromString = mkIdent
42 |
43 | -- | Uses 'hash' to short-circuit
44 | instance Eq Ident where
45 | i1 == i2 = hash i1 == hash i2 && name i1 == name i2 && raw i1 == raw i2
46 | i1 /= i2 = hash i1 /= hash i2 || name i1 /= name i2 || raw i1 /= raw i2
47 |
48 | -- | Uses 'hash' to short-circuit
49 | instance Ord Ident where
50 | compare i1 i2 = case compare i1 i2 of
51 | EQ -> compare (raw i1, name i1) (raw i2, name i2)
52 | rt -> rt
53 |
54 | -- | "Forgets" about whether either argument was raw
55 | instance Monoid Ident where
56 | mappend = (<>)
57 | mempty = mkIdent ""
58 |
59 | -- | "Forgets" about whether either argument was raw
60 | -- <<<<<<< HEAD
61 | instance Semigroup Ident where
62 | Ident (Name n1) _ _ <> Ident (Name n2) _ _ = mkIdent (n1 <> n2)
63 | Ident (HaskellName n1) _ _ <> Ident (HaskellName n2) _ _ = mkIdent (n1 <> n2)
64 | Ident (Name n1) _ _ <> Ident (HaskellName n2) _ _ = mkIdent (n1 <> n2)
65 | Ident (HaskellName n1) _ _ <> Ident (Name n2) _ _ = mkIdent (n1 <> n2)
66 | -- =======
67 | -- instance Sem.Semigroup Ident where
68 | -- Ident n1 _ _ <> Ident n2 _ _ = mkIdent (n1 <> n2)
69 | -- >>>>>>> upstream/master
70 |
71 | -- | Smart constructor for making an 'Ident'.
72 | mkIdent :: String -> Ident
73 | mkIdent ('$':'{':'i':'|':ss) = Ident (HaskellName $ init $ init ss) False (hashString $ init $ init ss)
74 | mkIdent s = Ident (Name s) False (hashString s)
75 |
76 | -- | Hash a string into an 'Int'
77 | hashString :: String -> Int
78 | hashString = foldl' f golden
79 | where f m c = fromIntegral (ord c) * magic + m
80 | magic = 0xdeadbeef
81 | golden = 1013904242
82 |
83 | -- | The payload of an identifier
84 | type Name = String
85 |
86 | data IdentName =
87 | Name Name
88 | | HaskellName String
89 | deriving (Eq, Ord, Data, Typeable, Generic, NFData)
90 |
91 | instance Show IdentName where
92 | show (Name n) = show n
93 | show (HaskellName n) = "${i|" ++ n ++ "|}"
94 |
95 |
--------------------------------------------------------------------------------
/src/Language/Rust/Data/InputStream.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Data.InputStream
3 | Description : Interface to the underlying input of parsing
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : portable
9 |
10 | These are the only functions that need to be implemented in order to use the parser. Whether this
11 | wraps 'BS.ByteString' or 'String' depends on whether the @useByteStrings@ option is on or not (it is
12 | by default). Using 'BS.ByteString' means better handling of weird characters ('takeByte' for plain
13 | 'String' fails badly if you try to take a byte that doesn't fall on a character boundary), but it
14 | means incurring a dependency on the [utf8-string](https://hackage.haskell.org/package/utf8-string)
15 | package.
16 | -}
17 | {-# LANGUAGE CPP #-}
18 |
19 | module Language.Rust.Data.InputStream (
20 | -- * InputStream type
21 | InputStream,
22 | countLines,
23 | inputStreamEmpty,
24 |
25 | -- * Introduction forms
26 | readInputStream,
27 | hReadInputStream,
28 | inputStreamFromString,
29 |
30 | -- * Elimination forms
31 | inputStreamToString,
32 | takeByte,
33 | takeChar,
34 | peekChars,
35 | ) where
36 |
37 | import Data.Word ( Word8 )
38 | import Data.Coerce ( coerce )
39 | import Data.String ( IsString(..) )
40 | import System.IO
41 |
42 | #ifdef USE_BYTESTRING
43 | import qualified Data.ByteString as BS
44 | import qualified Data.ByteString.UTF8 as BE
45 | #else
46 | import qualified Data.Char as Char
47 | #endif
48 |
49 | -- | Read an encoded file into an 'InputStream'
50 | readInputStream :: FilePath -> IO InputStream
51 | {-# INLINE readInputStream #-}
52 |
53 | -- | Read an 'InputStream' from a 'Handle'
54 | hReadInputStream :: Handle -> IO InputStream
55 | {-# INLINE hReadInputStream #-}
56 |
57 | -- | Convert 'InputStream' to 'String'.
58 | inputStreamToString :: InputStream -> String
59 | {-# INLINE inputStreamToString #-}
60 |
61 | -- | Convert a 'String' to an 'InputStream'.
62 | inputStreamFromString :: String -> InputStream
63 | {-# INLINE inputStreamFromString #-}
64 |
65 | -- | Uses 'inputStreamFromString'
66 | instance IsString InputStream where fromString = inputStreamFromString
67 |
68 | -- | Read the first byte from an 'InputStream' and return that byte with what remains of the
69 | -- 'InputStream'. Behaviour is undefined when 'inputStreamEmpty' returns 'True'.
70 | --
71 | -- >>> takeByte "foo bar"
72 | -- (102, "oo bar")
73 | --
74 | -- >>> takeByte "Ĥăƨĸëļļ"
75 | -- (196, "\ETX\168\&8\235<<")
76 | --
77 | takeByte :: InputStream -> (Word8, InputStream)
78 | {-# INLINE takeByte #-}
79 |
80 | -- | Read the first character from an 'InputStream' and return that 'Char' with what remains of the
81 | -- 'InputStream'. Behaviour is undefined when 'inputStreamEmpty' returns 'True'.
82 | --
83 | -- >>> takeChar "foo bar"
84 | -- ('f', "oo bar")
85 | --
86 | -- >>> takeChar "Ĥăƨĸëļļ"
87 | -- ('Ĥ', "ăƨĸëļļ")
88 | --
89 | takeChar :: InputStream -> (Char, InputStream)
90 | {-# INLINE takeChar #-}
91 |
92 | -- | Return @True@ if the given input stream is empty.
93 | --
94 | -- >>> inputStreamEmpty ""
95 | -- True
96 | --
97 | -- >>> inputStreamEmpty "foo"
98 | -- False
99 | --
100 | inputStreamEmpty :: InputStream -> Bool
101 | {-# INLINE inputStreamEmpty #-}
102 |
103 | -- | Returns the first @n@ characters of the given input stream, without removing them.
104 | --
105 | -- >>> peekChars 5 "foo bar"
106 | -- "foo ba"
107 | --
108 | -- >>> peekChars 5 "foo"
109 | -- "foo"
110 | --
111 | -- >>> peekChars 3 "Ĥăƨĸëļļ"
112 | -- "Ĥăƨ"
113 | --
114 | peekChars :: Int -> InputStream -> String
115 | {-# INLINE peekChars #-}
116 |
117 | -- | Returns the number of text lines in the given 'InputStream'
118 | --
119 | -- >>> countLines ""
120 | -- 0
121 | --
122 | -- >>> countLines "foo"
123 | -- 1
124 | --
125 | -- >>> countLines "foo\n\nbar"
126 | -- 3
127 | --
128 | -- >>> countLines "foo\n\nbar\n"
129 | -- 3
130 | --
131 | countLines :: InputStream -> Int
132 | {-# INLINE countLines #-}
133 |
134 | #ifdef USE_BYTESTRING
135 |
136 | -- | Opaque input type.
137 | newtype InputStream = IS BS.ByteString deriving (Eq, Ord)
138 | takeByte bs = (BS.head (coerce bs), coerce (BS.tail (coerce bs)))
139 | takeChar bs = maybe (error "takeChar: no char left") coerce (BE.uncons (coerce bs))
140 | inputStreamEmpty = BS.null . coerce
141 | peekChars n = BE.toString . BE.take n . coerce
142 | readInputStream f = coerce <$> BS.readFile f
143 | hReadInputStream h = coerce <$> BS.hGetContents h
144 | inputStreamToString = BE.toString . coerce
145 | inputStreamFromString = IS . BE.fromString
146 | countLines = length . BE.lines . coerce
147 |
148 | instance Show InputStream where
149 | show (IS bs) = show bs
150 |
151 | #else
152 |
153 | -- | Opaque input type.
154 | newtype InputStream = IS String deriving (Eq, Ord)
155 | takeByte (IS ~(c:str))
156 | | Char.isLatin1 c = let b = fromIntegral (Char.ord c) in b `seq` (b, IS str)
157 | | otherwise = error "takeByte: not a latin-1 character"
158 | takeChar (IS ~(c:str)) = (c, IS str)
159 | inputStreamEmpty (IS str) = null str
160 | peekChars n (IS str) = take n str
161 | readInputStream f = IS <$> readFile f
162 | hReadInputStream h = IS <$> hGetContents h
163 | inputStreamToString = coerce
164 | inputStreamFromString = IS
165 | countLines (IS str) = length . lines $ str
166 |
167 | instance Show InputStream where
168 | show (IS bs) = show bs
169 |
170 | #endif
171 |
--------------------------------------------------------------------------------
/src/Language/Rust/Parser.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Parser
3 | Description : Parsing and lexing
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : GHC
9 |
10 | Selecting the right parser may require adding an annotation or using @-XTypeApplications@ to avoid
11 | an "Ambiguous type variable" error.
12 |
13 | Using 'Control.Monad.void' (as in the examples below) exploits the fact that most AST nodes are
14 | instances of 'Functor' to discard the 'Span' annotation that is attached to most parsed AST nodes.
15 | Conversely, if you wish to extract the 'Span' annotation, the 'Language.Rust.Syntax.AST.Located'
16 | typeclass provides a 'Language.Rust.Syntax.AST.spanOf' method.
17 |
18 | The examples below assume the following GHCi flags and imports:
19 |
20 | >>> :set -XTypeApplications -XOverloadedStrings
21 | >>> import Language.Rust.Syntax.AST
22 | >>> import Control.Monad ( void )
23 | >>> import System.IO
24 |
25 | -}
26 | {-# LANGUAGE FlexibleInstances #-}
27 |
28 | module Language.Rust.Parser (
29 | -- * Parsing
30 | parse,
31 | parse',
32 | readSourceFile,
33 | readTokens,
34 | Parse(..),
35 | P,
36 | execParser,
37 | execParserTokens,
38 | initPos,
39 | Span,
40 |
41 | -- * Lexing
42 | lexToken,
43 | lexNonSpace,
44 | lexTokens,
45 | translateLit,
46 |
47 | -- * Input stream
48 | readInputStream,
49 | hReadInputStream,
50 | inputStreamToString,
51 | inputStreamFromString,
52 |
53 | -- * Error reporting
54 | lexicalError,
55 | parseError,
56 | ParseFail(..),
57 | ) where
58 |
59 | import Language.Rust.Syntax
60 |
61 | import Language.Rust.Data.InputStream
62 | import Language.Rust.Data.Position ( Position, Span, Spanned, initPos )
63 |
64 | import Language.Rust.Parser.Internal
65 | import Language.Rust.Parser.Lexer ( lexToken, lexNonSpace, lexTokens, lexicalError )
66 | import Language.Rust.Parser.Literals ( translateLit )
67 | import Language.Rust.Parser.ParseMonad ( P, execParser, parseError, pushToken, ParseFail(..) )
68 |
69 | import Control.Exception ( throw )
70 | import Data.Foldable ( traverse_ )
71 | import System.IO ( Handle )
72 |
73 | -- | Parse something from an input stream (it is assumed the initial position is 'initPos').
74 | --
75 | -- >>> fmap void $ parse @(Expr Span) "x + 1"
76 | -- Right (Binary [] AddOp (PathExpr [] Nothing (Path False [PathSegment "x" Nothing ()] ()) ())
77 | -- (Lit [] (Int Dec 1 Unsuffixed ()) ())
78 | -- ())
79 | --
80 | -- >>> fmap void $ parse @(Expr Span) "x + "
81 | -- Left (parse failure at 1:4 (Syntax error: unexpected `' (expected an expression)))
82 | --
83 | parse :: Parse a => InputStream -> Either ParseFail a
84 | parse is = execParser parser is initPos
85 |
86 | -- | Same as 'parse', but throws a 'ParseFail' exception if it cannot parse. This function is
87 | -- intended for situations in which you are already stuck catching exceptions - otherwise you should
88 | -- prefer 'parse'.
89 | --
90 | -- >>> void $ parse' @(Expr Span) "x + 1"
91 | -- Binary [] AddOp (PathExpr [] Nothing (Path False [PathSegment "x" Nothing ()] ()) ())
92 | -- (Lit [] (Int Dec 1 Unsuffixed ()) ())
93 | -- ()
94 | --
95 | -- >>> void $ parse' @(Expr Span) "x + "
96 | -- *** Exception: parse failure at 1:4 (Syntax error: unexpected `' (expected an expression))
97 | --
98 | parse' :: Parse a => InputStream -> a
99 | parse' = either throw id . parse
100 |
101 | -- | Same as 'execParser', but working from a list of tokens instead of an 'InputStream'.
102 | execParserTokens :: P a -> [Spanned Token] -> Position -> Either ParseFail a
103 | execParserTokens p toks = execParser (pushTokens toks *> p) (inputStreamFromString "")
104 | where pushTokens = traverse_ pushToken . reverse
105 |
106 | -- | Given a handle to a Rust source file, read that file and parse it into a 'SourceFile'
107 | --
108 | -- >>> writeFile "empty_main.rs" "fn main() { }"
109 | -- >>> fmap void $ withFile "empty_main.rs" ReadMode readSourceFile
110 | -- SourceFile Nothing [] [Fn [] InheritedV "main"
111 | -- (FnDecl [] Nothing False ())
112 | -- Normal NotConst Rust
113 | -- (Generics [] [] (WhereClause [] ()) ())
114 | -- (Block [] Normal ()) ()]
115 | --
116 | readSourceFile :: Handle -> IO (SourceFile Span)
117 | readSourceFile hdl = parse' <$> hReadInputStream hdl
118 |
119 | -- | Given a path pointing to a Rust source file, read that file and lex it (ignoring whitespace)
120 | --
121 | -- >>> writeFile "empty_main.rs" "fn main() { }"
122 | -- >>> withFile "empty_main.rs" ReadMode readTokens
123 | -- [fn,main,(,),{,}]
124 | --
125 | readTokens :: Handle -> IO [Spanned Token]
126 | readTokens hdl = do
127 | inp <- hReadInputStream hdl
128 | case execParser (lexTokens lexNonSpace) inp initPos of
129 | Left pf -> throw pf
130 | Right x -> pure x
131 |
132 | -- | Describes things that can be parsed
133 | class Parse a where
134 | -- | Complete parser (fails if not all of the input is consumed)
135 | parser :: P a
136 |
137 | instance Parse (Lit Span) where parser = parseLit
138 | instance Parse (Attribute Span) where parser = parseAttr
139 | instance Parse (Ty Span) where parser = parseTy
140 | instance Parse (Pat Span) where parser = parsePat
141 | instance Parse (Expr Span) where parser = parseExpr
142 | instance Parse (Stmt Span) where parser = parseStmt
143 | instance Parse (Item Span) where parser = parseItem
144 | instance Parse (SourceFile Span) where parser = parseSourceFile
145 | instance Parse TokenTree where parser = parseTt
146 | instance Parse TokenStream where parser = parseTokenStream
147 | instance Parse (Block Span) where parser = parseBlock
148 | instance Parse (ImplItem Span) where parser = parseImplItem
149 | instance Parse (TraitItem Span) where parser = parseTraitItem
150 | instance Parse (TyParam Span) where parser = parseTyParam
151 | instance Parse (LifetimeDef Span) where parser = parseLifetimeDef
152 | instance Parse (Generics Span) where parser = parseGenerics
153 | instance Parse (WhereClause Span) where parser = parseWhereClause
154 |
--------------------------------------------------------------------------------
/src/Language/Rust/Parser/NonEmpty.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Parser.NonEmpty
3 | Description : Non-empty lists
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : GHC
9 |
10 | Datatype wrapping non-empty lists.
11 | -}
12 | {-# LANGUAGE CPP #-}
13 | {-# LANGUAGE FlexibleInstances #-}
14 | {-# LANGUAGE TypeFamilies #-}
15 | #if __GLASGOW_HASKELL__ < 800
16 | {-# LANGUAGE FlexibleContexts #-}
17 | #endif
18 | {-# LANGUAGE StandaloneDeriving #-}
19 | {-# LANGUAGE DeriveDataTypeable #-}
20 | {-# LANGUAGE QuasiQuotes #-}
21 | {-# LANGUAGE TemplateHaskell, DeriveFoldable, DeriveFunctor, GeneralizedNewtypeDeriving, DeriveTraversable #-}
22 |
23 | module Language.Rust.Parser.NonEmpty (
24 | NonEmpty(..),
25 | listCons,
26 | head, tail,
27 | reverse, last, init, (<|), (|:), (|>)
28 | ) where
29 | import Prelude hiding (head, tail, reverse, init, last)
30 |
31 | import qualified Data.List.NonEmpty as N
32 | import Data.Data ( Data(..) )
33 | import Control.DeepSeq ( NFData )
34 | import Data.Semigroup (Semigroup(..))
35 |
36 | -- Support for OverloadedLists of NonEmpty lists:
37 | import GHC.Exts (IsList(..))
38 |
39 | newtype NonEmpty a = NonEmpty (N.NonEmpty a)
40 | deriving (Foldable, Eq, Ord, Show, Functor, Data, NFData, Traversable)
41 |
42 | instance IsList (NonEmpty a) where
43 | type Item (NonEmpty a) = a
44 |
45 | fromList (a:as) = NonEmpty $ a N.:| as
46 | fromList [] = errorWithoutStackTrace "NonEmpty.fromList: empty list"
47 |
48 | toList ~(NonEmpty (a N.:| as)) = a : as
49 |
50 | instance Semigroup (NonEmpty a) where
51 | (NonEmpty (a N.:| as)) <> ~(NonEmpty (b N.:| bs)) = NonEmpty $ a N.:| (as ++ b : bs)
52 |
53 | listCons :: a -> [a] -> NonEmpty a
54 | listCons a as = NonEmpty $ a N.:| as
55 |
56 | head :: NonEmpty a -> a
57 | head (NonEmpty as) = N.head as
58 |
59 | tail :: NonEmpty a -> [a]
60 | tail (NonEmpty as) = N.tail as
61 |
62 | reverse :: NonEmpty a -> NonEmpty a
63 | reverse (NonEmpty as) = NonEmpty $ N.reverse as
64 |
65 | init :: NonEmpty a -> [a]
66 | init (NonEmpty as) = N.init as
67 |
68 | last :: NonEmpty a -> a
69 | last (NonEmpty as) = N.last as
70 |
71 | (<|) :: a -> NonEmpty a -> NonEmpty a
72 | (<|) a (NonEmpty as) = NonEmpty $ a N.<| as
73 |
74 | (|:) :: [a] -> a -> NonEmpty a
75 | [] |: y = NonEmpty $ y N.:| []
76 | (x:xs) |: y = NonEmpty $ x N.:| (xs ++ [y])
77 |
78 | (|>) :: NonEmpty a -> a -> NonEmpty a
79 | (|>) (NonEmpty (x N.:| xs)) y = NonEmpty $ x N.:| (xs ++ [y])
80 |
81 |
--------------------------------------------------------------------------------
/src/Language/Rust/Parser/Reversed.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Parser.Reversed
3 | Description : Parsing literals
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : GHC
9 |
10 | Datatypes wrapping lists and non-empty lists designed for fast append (as opposed to prepend)
11 | along with the usual class instances.
12 | -}
13 | {-# LANGUAGE CPP #-}
14 | {-# LANGUAGE FlexibleInstances #-}
15 | {-# LANGUAGE TypeFamilies #-}
16 | #if __GLASGOW_HASKELL__ < 800
17 | {-# LANGUAGE FlexibleContexts #-}
18 | #endif
19 | {-# LANGUAGE StandaloneDeriving #-}
20 | {-# LANGUAGE DeriveDataTypeable #-}
21 |
22 | module Language.Rust.Parser.Reversed (
23 | Reversed(..),
24 | toNonEmpty,
25 | unsnoc,
26 | snoc,
27 | NonEmpty(..),
28 | ) where
29 |
30 | import Language.Rust.Data.Position
31 |
32 | import Data.Foldable ( Foldable(toList) )
33 | import Data.Semigroup as Sem ( Semigroup(..) )
34 |
35 | import qualified Language.Rust.Parser.NonEmpty as N
36 | import Language.Rust.Parser.NonEmpty (NonEmpty(..), listCons)
37 | import qualified GHC.Exts as G
38 |
39 | import Data.Data (Data(..), Typeable)
40 |
41 | -- | Wrap a data type where all the operations are reversed
42 | newtype Reversed f a = Reversed (f a)
43 |
44 | instance Functor f => Functor (Reversed f) where
45 | fmap f (Reversed xs) = Reversed (fmap f xs)
46 |
47 | instance Foldable (Reversed []) where
48 | foldMap f (Reversed xs) = foldMap f (reverse xs)
49 | toList (Reversed xs) = reverse xs
50 |
51 | instance Foldable (Reversed NonEmpty) where
52 | foldMap f (Reversed xs) = foldMap f (N.reverse xs)
53 | toList (Reversed xs) = reverse (toList xs)
54 |
55 | instance Sem.Semigroup (f a) => Sem.Semigroup (Reversed f a) where
56 | Reversed xs <> Reversed ys = Reversed (ys Sem.<> xs)
57 |
58 | instance Monoid (f a) => Monoid (Reversed f a) where
59 | mempty = Reversed mempty
60 | mappend (Reversed xs) (Reversed ys) = Reversed (mappend ys xs)
61 |
62 | instance G.IsList (f a) => G.IsList (Reversed f a) where
63 | type Item (Reversed f a) = G.Item (f a)
64 | fromList xs = Reversed (G.fromList (reverse xs))
65 | toList (Reversed xs) = reverse (G.toList xs)
66 |
67 | instance Located (f a) => Located (Reversed f a) where
68 | spanOf (Reversed xs) = spanOf xs
69 |
70 | deriving instance (Typeable f, Typeable a, Data (f a)) => Data (Reversed f a)
71 |
72 | -- | Convert a reversed 'NonEmpty' back into a normal one.
73 | {-# INLINE toNonEmpty #-}
74 | toNonEmpty :: Reversed NonEmpty a -> NonEmpty a
75 | toNonEmpty (Reversed xs) = N.reverse xs
76 |
77 | -- | Remove an element from the end of a non-empty reversed sequence
78 | {-# INLINE unsnoc #-}
79 | unsnoc :: Reversed NonEmpty a -> (Reversed [] a, a)
80 | unsnoc (Reversed xs) = (Reversed $ N.tail xs, N.head xs)
81 |
82 | -- | Add an element to the end of a reversed sequence to produce a non-empty
83 | -- reversed sequence
84 | {-# INLINE snoc #-}
85 | snoc :: Reversed [] a -> a -> Reversed NonEmpty a
86 | snoc (Reversed xs) x = Reversed (x `listCons` xs)
87 |
--------------------------------------------------------------------------------
/src/Language/Rust/Pretty/Literals.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Pretty.Literals
3 | Description : Parsing literals
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : portable
9 |
10 | Functions for pretty printing literals.
11 | -}
12 | {-# LANGUAGE OverloadedStrings #-}
13 |
14 | module Language.Rust.Pretty.Literals (
15 | printLit,
16 | printLitSuffix,
17 | ) where
18 |
19 | import Language.Rust.Syntax.AST
20 | import Language.Rust.Pretty.Util
21 |
22 | import Data.Text.Prettyprint.Doc ( hcat, annotate, (<>), Doc, pretty, group, hardline, flatAlt )
23 |
24 | import Data.Char ( intToDigit, ord, chr )
25 | import Data.Word ( Word8 )
26 |
27 | -- | Print a literal (@print_literal@)
28 | printLit :: Lit a -> Doc a
29 | printLit lit = noIndent $ case lit of
30 | Str str Cooked s x -> annotate x (hcat [ "\"", group (foldMap (escapeChar True) str), "\"", suf s ])
31 | Str str (Raw m) s x -> annotate x (hcat [ "r", pad m, "\"", string hardline str, "\"", pad m, suf s ])
32 | ByteStr str Cooked s x -> annotate x (hcat [ "b\"", group (foldMap (escapeByte True) str), "\"", suf s ])
33 | ByteStr str (Raw m) s x -> annotate x (hcat [ "br", pad m, "\"", string hardline (map byte2Char str), "\"", pad m, suf s ])
34 | Char c s x -> annotate x (hcat [ "'", escapeChar False c, "'", suf s ])
35 | Byte b s x -> annotate x (hcat [ "b'", escapeByte False b, "'", suf s ])
36 | Int b i l s x -> annotate x (hcat [ printIntLit i b l, suf s ])
37 | Float d s x -> annotate x (hcat [ pretty d, suf s ])
38 | Bool True s x -> annotate x (hcat [ "true", suf s ])
39 | Bool False s x -> annotate x (hcat [ "false", suf s ])
40 | where
41 | pad :: Int -> Doc a
42 | pad m = pretty (replicate m '#')
43 |
44 | suf :: Suffix -> Doc a
45 | suf = printLitSuffix
46 |
47 | -- | Print literal suffix
48 | printLitSuffix :: Suffix -> Doc a
49 | printLitSuffix Unsuffixed = mempty
50 | printLitSuffix Is = "isize"
51 | printLitSuffix I8 = "i8"
52 | printLitSuffix I16 = "i16"
53 | printLitSuffix I32 = "i32"
54 | printLitSuffix I64 = "i64"
55 | printLitSuffix I128 = "i128"
56 | printLitSuffix Us = "usize"
57 | printLitSuffix U8 = "u8"
58 | printLitSuffix U16 = "u16"
59 | printLitSuffix U32 = "u32"
60 | printLitSuffix U64 = "u64"
61 | printLitSuffix U128 = "u128"
62 | printLitSuffix F32 = "f32"
63 | printLitSuffix F64 = "f64"
64 |
65 | -- | Print an integer literal
66 | printIntLit :: Integer -> IntRep -> String -> Doc a
67 | printIntLit i r len
68 | | i < 0 = "-" <> baseRep r <> printIntPrefix (show $ toNBase (abs i) (baseVal r)) len <> toNBase (abs i) (baseVal r)
69 | | i == 0 = baseRep r <> printIntPrefix "" len -- <> "0"
70 | | otherwise = baseRep r <> printIntPrefix (show $ toNBase (abs i) (baseVal r)) len <> toNBase (abs i) (baseVal r)
71 | where
72 | baseRep :: IntRep -> Doc a
73 | baseRep Bin = "0b"
74 | baseRep Oct = "0o"
75 | baseRep Dec = mempty
76 | baseRep Hex = "0x"
77 |
78 | baseVal :: IntRep -> Integer
79 | baseVal Bin = 2
80 | baseVal Oct = 8
81 | baseVal Dec = 10
82 | baseVal Hex = 16
83 |
84 | printIntPrefix :: String -> String -> Doc a
85 | printIntPrefix out ('0':'b':rest) = pretty $ replicate ((length rest) - (length out)) '0'
86 | printIntPrefix out ('0':'o':rest) = pretty $ replicate ((length rest) - (length out)) '0'
87 | printIntPrefix out ('0':'x':rest) = pretty $ replicate ((length rest) - (length out)) '0'
88 | printIntPrefix out rest = pretty $ replicate ((length rest) - (length out)) '0'
89 | -- = pretty $ take (fromIntegral $ l - (fromIntegral thing)) (repeat '0')
90 |
91 | toDigit :: Integer -> Char
92 | toDigit l = "0123456789ABCDEF" !! fromIntegral l
93 |
94 | toNBase :: Integer -> Integer -> Doc a
95 | l `toNBase` b | l < b = pretty (toDigit l)
96 | | otherwise = let ~(d,e) = l `quotRem` b in toNBase d b <> pretty (toDigit e)
97 |
98 |
99 | -- | Extend a byte into a unicode character
100 | byte2Char :: Word8 -> Char
101 | byte2Char = chr . fromIntegral
102 |
103 | -- | Constrain a unicode character to a byte
104 | -- This assumes the character is in the right range already
105 | char2Byte :: Char -> Word8
106 | char2Byte = fromIntegral . ord
107 |
108 | -- | Escape a byte. Based on @std::ascii::escape_default@.
109 | --
110 | -- If the first argument is true, newlines may become a literal newline characters if the string is
111 | -- too long.
112 | escapeByte :: Bool -> Word8 -> Doc a
113 | escapeByte nl w8 = case byte2Char w8 of
114 | '\t' -> "\\t"
115 | '\r' -> "\\r"
116 | '\\' -> "\\\\"
117 | '\'' -> "\\'"
118 | '"' -> "\\\""
119 | '\n'| nl -> flatAlt hardline "\\n"
120 | | otherwise -> "\\n"
121 | c | 0x20 <= w8 && w8 <= 0x7e -> pretty c
122 | _ -> "\\x" <> padHex 2 w8
123 |
124 | -- | Escape a unicode character. Based on @std::ascii::escape_default@.
125 | --
126 | -- If the first argument is true, newlines may become a literal newline characters if the string is
127 | -- too long.
128 | escapeChar :: Bool -> Char -> Doc a
129 | escapeChar nl c | c <= '\x7f' = escapeByte nl (char2Byte c)
130 | | c <= '\xffff' = "\\u{" <> padHex 4 (ord c) <> "}"
131 | | otherwise = "\\u{" <> padHex 6 (ord c) <> "}"
132 |
133 | -- | Convert a number to its padded hexadecimal form
134 | padHex :: Integral a => Int -> a -> Doc b
135 | padHex i 0 = pretty (replicate i '0')
136 | padHex i m = let (m',r) = m `divMod` 0x10
137 | in padHex (i-1) m' <> pretty (intToDigit (fromIntegral r))
138 |
139 |
--------------------------------------------------------------------------------
/src/Language/Rust/Pretty/Util.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Pretty.Util
3 | Description : pretty printing utilities
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : portable
9 |
10 | This module contains a variety of utility functions for pretty printing. Most of these require
11 | inspecting the internal structure of 'Doc'.
12 |
13 | Wadler's take on pretty printing is super useful because it allows us to print thinks like blocks
14 | much more nicely (see [this](http://stackoverflow.com/a/41424946/3072788)) that Hughes'.
15 | Unfortunately, unlike Hughes', Wadler does not have 'mempty' as the identity of @<+>@ - the
16 | space between the arguments of @<+>@ does not go away even if either argument is empty. The
17 | same problem shows up for @hsep@, @<#>@, @vsep@, @>@, etc.
18 |
19 | My solution has been to redefine my versions of these functions which _do_ treat 'mempty' as a
20 | neutral element for @<+>@, @hsep@, @<#>@, @vsep@, and @>@.
21 | -}
22 | {-# LANGUAGE OverloadedStrings #-}
23 |
24 | module Language.Rust.Pretty.Util where
25 |
26 | import Data.Monoid as M
27 |
28 | import qualified Data.Text.Prettyprint.Doc as PP
29 | import Data.Text.Prettyprint.Doc.Internal.Type ( Doc(..) )
30 |
31 | import Language.Rust.Syntax.Token ( Delim(..) )
32 |
33 | -- | Indentation level
34 | n :: Int
35 | n = 2
36 |
37 | -- | Similar to 'maybe', but where the 'Nothing' case is an empty 'Doc'
38 | emptyElim :: Doc a -- ^ result if scrutinee is empty
39 | -> (Doc a -> Doc a) -- ^ how to process scrutinee if it is not empty
40 | -> Doc a -- ^ scrutinee 'Doc'
41 | -> Doc a
42 | emptyElim a _ Empty = a
43 | emptyElim _ f doc = f doc
44 |
45 | -- | Vertically concatenate two 'Doc's with a collapsible line between them
46 | (<##>) :: Doc a -> Doc a -> Doc a
47 | d1 <##> d2 = d1 M.<> PP.line' M.<> d2
48 |
49 | -- | Flatten a 'Doc'
50 | flatten :: Doc a -> Doc a
51 | flatten d@Fail{} = d
52 | flatten d@Empty{} = d
53 | flatten d@Char{} = d
54 | flatten d@Text{} = d
55 | flatten d@Line{} = d
56 | flatten (FlatAlt _ d) = d
57 | flatten (Cat d1 d2) = Cat (flatten d1) (flatten d2)
58 | flatten (Nest i d) = Nest i (flatten d)
59 | flatten (Union d _) = flatten d
60 | flatten (Column f) = Column (flatten . f)
61 | flatten (WithPageWidth f) = WithPageWidth (flatten . f)
62 | flatten (Nesting f) = Nesting (flatten . f)
63 | flatten (Annotated a d) = Annotated a (flatten d)
64 |
65 | -- | Map the list of items into 'Doc's using the provided function and add comma punctuation
66 | commas :: [a] -> (a -> Doc b) -> Doc b
67 | commas xs f = PP.hsep (PP.punctuate "," (map f xs))
68 |
69 | -- | Take a binary operation on docs and lift it to one that has (left and right) identity 'mempty'
70 | liftOp :: (Doc a -> Doc a -> Doc a) -> Doc a -> Doc a -> Doc a
71 | liftOp _ Empty d = d
72 | liftOp _ d Empty = d
73 | liftOp (#) d d' = d # d'
74 |
75 | -- | Lifted version of Wadler's @<+>@
76 | (<+>) :: Doc a -> Doc a -> Doc a
77 | (<+>) = liftOp (PP.<+>)
78 |
79 | -- | Lifted version of Wadler's @hsep@
80 | hsep :: Foldable f => f (Doc a) -> Doc a
81 | hsep = foldr (<+>) mempty
82 |
83 | -- | Lifted version of Wadler's @<#>@
84 | (<#>) :: Doc a -> Doc a -> Doc a
85 | (<#>) = liftOp (\x y -> x <> PP.line <> y)
86 |
87 | -- | Lifted version of Wadler's @vsep@
88 | vsep :: Foldable f => f (Doc a) -> Doc a
89 | vsep = foldr (<#>) mempty
90 |
91 | -- | Lifted version of Wadler's @>@
92 | (>) :: Doc a -> Doc a -> Doc a
93 | (>) = liftOp (\x y -> x <> PP.softline <> y)
94 |
95 | -- | Unless the condition holds, print the document
96 | unless :: Bool -> Doc a -> Doc a
97 | unless b = when (not b)
98 |
99 | -- | When the condition holds, print the document
100 | when :: Bool -> Doc a -> Doc a
101 | when cond d = if cond then d else mempty
102 |
103 | -- | Apply a printing function to an optional value. If the value is 'Nothing', 'perhaps' returns
104 | -- the empty 'Doc'.
105 | perhaps :: (a -> Doc b) -> Maybe a -> Doc b
106 | perhaps = maybe mempty
107 |
108 | -- | Indent the given 'Doc', but only if multi-line
109 | indent :: Int -> Doc a -> Doc a
110 | indent m doc = PP.flatAlt (PP.indent m doc) (flatten doc)
111 |
112 | -- | Undo what group does. This function is pretty dangerous...
113 | ungroup :: Doc a -> Doc a
114 | ungroup (Union _ x) = x
115 | ungroup y = y
116 |
117 | -- | Remove all indent
118 | noIndent :: Doc a -> Doc a
119 | noIndent d = PP.nesting (\i -> PP.nest (negate i) d)
120 |
121 | -- | Translate '\n' in a string using the provided 'Doc' instead of 'line'
122 | string :: Doc a -> String -> Doc a
123 | string new = foldMap (\c -> case c of { '\n' -> new; _ -> Char c })
124 |
125 | -- | This is the most general function for printing blocks. It operates with any delimiter, any
126 | -- seperator, an optional leading attribute doc (which isn't followed by a seperator), and wraps a
127 | -- list of entries. It has been tweaked to look Just Right (TM) for the usual cases.
128 | --
129 | -- Note that this will try to fit things on one line when possible, so if you want a block that is
130 | -- sure /not/ to be condensed on one line (e.g. for a function), you have to construct it manually.
131 | block :: Delim -- ^ outer delimiters
132 | -> Bool -- ^ prefer to be on one line (as opposed to multiline)?
133 | -> Doc a -- ^ seperator
134 | -> Doc a -- ^ attributes doc, after which no seperator will (use 'mempty' to ignore)
135 | -> [Doc a] -- ^ entries
136 | -> Doc a
137 | block delim p s as xs = group' (lDel # PP.vsep (as' ++ ys) # rDel)
138 | where
139 | group' = if p || null (as' ++ ys) then PP.group else id
140 |
141 | -- left and right delimiter lists
142 | (lDel,rDel) = case delim of
143 | Paren -> ("(", ")")
144 | Bracket -> ("[", "]")
145 | Brace -> ("{", "}")
146 | NoDelim -> (mempty, mempty)
147 |
148 | -- method of contenating delimiters with the rest of the body
149 | (#) = case delim of { Paren -> (<##>); _ -> (<#>) }
150 |
151 | -- attributes
152 | as' = case as of
153 | Empty -> []
154 | _ -> [ PP.flatAlt (PP.indent n as) (flatten as) ]
155 |
156 | -- list of entries
157 | ys = go xs where go [] = []
158 | go [z] = [ PP.flatAlt (PP.indent n z <> s) (flatten z) ]
159 | go (z:zs) = PP.flatAlt (PP.indent n z <> s) (flatten z <> s) : go zs
160 |
161 |
162 |
--------------------------------------------------------------------------------
/src/Language/Rust/Syntax.hs:
--------------------------------------------------------------------------------
1 | {-|
2 | Module : Language.Rust.Syntax
3 | Description : Syntax data defintions
4 | Copyright : (c) Alec Theriault, 2017-2018
5 | License : BSD-style
6 | Maintainer : alec.theriault@gmail.com
7 | Stability : experimental
8 | Portability : GHC
9 |
10 | This module defines Haskell data types corresponding to the abstract syntax tree(s) of the Rust
11 | language, based on the definitions @rustc@ uses (defined in @libsyntax@) whenever possible.
12 | Unfortunately, since the internals of @rustc@ are not exposed, there are no official
13 | docs. are the
14 | unofficial docs.
15 | -}
16 |
17 | module Language.Rust.Syntax (
18 | -- * Abstract syntax trees
19 | module Language.Rust.Syntax.AST,
20 | -- * Tokens
21 | module Language.Rust.Syntax.Token,
22 | ) where
23 |
24 | import Language.Rust.Syntax.AST
25 | import Language.Rust.Syntax.Token
26 |
27 | -- Using import/export shortcut screws up Haddock
28 | {-# ANN module "HLint: ignore Use import/export shortcut" #-}
29 |
30 |
--------------------------------------------------------------------------------
/src/Language/Rust/Syntax/Token.hs-boot:
--------------------------------------------------------------------------------
1 | -- .hs-boot files play badly with associated type families like 'Rep'
2 | {-# LANGUAGE CPP #-}
3 | #if __GLASGOW_HASKELL__ >= 800
4 | {-# OPTIONS_GHC -Wno-missing-methods #-}
5 | #endif
6 |
7 | module Language.Rust.Syntax.Token where
8 |
9 | import GHC.Generics (Generic)
10 | import Data.Data (Data)
11 | -- import Data.Typeable (Typeable)
12 | import Control.DeepSeq (NFData)
13 |
14 | data LitTok
15 |
16 | data Token
17 | instance Eq Token
18 | instance Ord Token
19 | instance Show Token
20 | instance Data Token
21 | -- instance Typeable Token
22 | instance Generic Token
23 | instance NFData Token
24 |
25 | data Delim
26 | instance Eq Delim
27 | instance Ord Delim
28 | instance Data Delim
29 | -- instance Typeable Delim
30 | instance Generic Delim
31 | instance Show Delim
32 | instance NFData Delim
33 |
34 | data Space
35 | instance NFData Space
36 | instance Eq Space
37 | instance Ord Space
38 |
39 |
--------------------------------------------------------------------------------
/stack.yaml:
--------------------------------------------------------------------------------
1 | #resolver: lts-11.14
2 | #resolver: nightly-2018-10-31
3 |
4 | #resolver: lts-13.12
5 | #resolver: lts-13.29
6 | #resolver: lts-14.6
7 | #resolver: lts-11.14
8 | #resolver: lts-14.21
9 | resolver: lts-16.0
10 |
11 | packages:
12 | - .
13 | #- ../../../antlr-haskell
14 | #- location:
15 | # git: git@github.com:cronburg/antlr-haskell.git
16 | # commit: 3e428f7
17 | #extra-dep: true
18 | #- location:
19 | # git: https://github.com/harpocrates/language-rust
20 | # commit: d32e57f
21 | #extra-dep: true
22 | #- ./deps/language-rust
23 |
24 | extra-deps: []
25 | flags: {}
26 | extra-package-dbs: []
27 |
28 |
--------------------------------------------------------------------------------
/test/parser/Main.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveAnyClass, DeriveGeneric, TypeFamilies, QuasiQuotes
2 | , DataKinds, ScopedTypeVariables, OverloadedStrings, TypeSynonymInstances
3 | , FlexibleInstances, UndecidableInstances, FlexibleContexts, TemplateHaskell
4 | , DeriveDataTypeable, BangPatterns, UnicodeSyntax #-}
5 | module Main where
6 | import Test.Framework
7 | import Test.Framework.Providers.HUnit
8 | import Test.HUnit
9 | --import Test.Framework.Providers.QuickCheck2
10 | --import Test.QuickCheck (Property, quickCheck, (==>))
11 |
12 | import Language.Floorplan.Syntax
13 | import Language.Floorplan.Token
14 | import qualified Language.Floorplan.Parser as P
15 | import System.IO (openFile, IOMode(..), hGetContents)
16 | import qualified Debug.Trace as D
17 | import Data.List (sort)
18 |
19 | import Language.Rust.Pretty (pretty)
20 | import Language.Floorplan.Core.Compiler
21 | import qualified Language.Floorplan.Rust as R
22 |
23 | {-
24 | testTokenizes fn = testCase fn $ do
25 | fd <- openFile fn ReadMode
26 | contents <- hGetContents fd
27 | let !xs = show $ P.tokenize contents
28 | return ()
29 |
30 | testParses fn = testCase fn $ do
31 | fd <- openFile fn ReadMode
32 | contents <- hGetContents fd
33 | let !xs = P.parseFloorplanDemarcsGLR fn 0 0 contents
34 | return ()
35 |
36 | testParsesExp fn expected = testCase fn $ do
37 | fd <- openFile fn ReadMode
38 | contents <- hGetContents fd
39 | let xs = P.parseFloorplanDemarcsGLR fn 0 0 contents
40 | assertEqual "Not equal" xs expected
41 | -}
42 |
43 | -- Happy parser generator versions:
44 |
45 | readme fn = do
46 | fd <- openFile fn ReadMode
47 | hGetContents fd
48 |
49 | mkTest fn cont = testCase fn $ readme fn >>= cont
50 |
51 | testTokenizes fn = mkTest fn $ \contents -> do
52 | let !xs = show $ scanTokens contents
53 | return ()
54 |
55 | testParses fn = mkTest fn $ \contents -> do
56 | let !xs = P.parseLayers contents
57 | return ()
58 |
59 | testGrafts fn = mkTest fn $ \contents -> do
60 | let !xs = P.parseLayers contents
61 | let !ys = map (grafting xs) xs
62 | countGrafting ys @?= 0
63 | return ()
64 |
65 | testParsesExp fn expected = mkTest fn $ \contents ->
66 | expected @=? P.parseLayers contents
67 |
68 | test_b2i =
69 | assertEqual "incorrect binary value"
70 | (bin2int "0b1010")
71 | 10
72 |
73 | test_b2i' =
74 | assertEqual "incorrect binary value"
75 | (bin2int "0b000000")
76 | 0
77 |
78 | tokenizeSuite = testGroup "tokenization suite"
79 | [ testTokenizes "examples/empty.flp"
80 | ]
81 |
82 | parseSuite = testGroup "parsing suite"
83 | [ testParses "examples/immix/layout.flp"
84 | , testParses "examples/empty.flp"
85 | , testParses "examples/seq.flp"
86 | , testParses "examples/enum.flp"
87 | , testParses "examples/union.flp"
88 | , testParses "examples/layer.flp"
89 | , testParses "examples/bits.flp"
90 | , testParses "examples/arith_id.flp"
91 | , testParses "examples/arith_power.flp"
92 | , testParses "examples/bump.flp"
93 | , testParses "examples/dynamic_prim.flp"
94 | , testParses "examples/dyn_choice.flp"
95 | , testParses "examples/dyn_seq.flp"
96 | -- , testNotParses "examples/enum_bad0.flp"
97 | , testParses "examples/map_bits.flp"
98 | , testParses "examples/named_ref.flp"
99 | , testParses "examples/nested.flp"
100 | , testParses "examples/nested_union.flp"
101 | , testParses "examples/app.flp"
102 | , testParses "examples/parens.flp"
103 | , testParsesExp "examples/arith.flp" theLayer
104 | ]
105 |
106 | theLayer =
107 | [ Layer
108 | { name = "Arith"
109 | , formals = []
110 | , magnitude = Just (SizeLit (Just (Lit 7)) Byte)
111 | , alignment = Nothing
112 | , magAlign = Nothing
113 | , contains = []
114 | , rhs = Blob (SizeLit
115 | (Just (Plus
116 | (Times (Div (Lit 4) (Lit 2))
117 | (Lit 3))
118 | (Lit 1)))
119 | Byte
120 | )
121 | }
122 | ]
123 |
124 | graftSuite = testGroup "grafting suite"
125 | [ testGrafts "examples/immix/layout.flp"
126 | , testCase "empty unique" $ [] @=? (checkUniqNames theLayer)
127 | , mkTest "examples/uniq_fail.flp" $ \c -> (sort ["foo", "Outer"]) @=? (sort $ checkUniqNames (P.parseLayers c))
128 | ]
129 |
130 | testCompiles fn = mkTest fn $ \contents -> do
131 | let !xs = P.parseLayers contents
132 | let !ys = map (grafting xs) xs
133 | countGrafting ys @?= 0
134 | let !zs = D.traceShowId $ map compile ys
135 | let !rust = D.traceShowId $ R.genRust zs
136 | let !_ = D.traceShowId $ pretty rust
137 | return ()
138 |
139 | compileSuite = testGroup "compiling suite"
140 | [ testCase "enumBytes A" $ enumBytes (take 0 $ repeat "") @?= 0 -- Null enum requires no space.
141 | , testCase "enumBytes B" $ enumBytes (take 1 $ repeat "") @?= 1 -- Singletone requires space.
142 | , testCase "enumBytes C" $ enumBytes (take 128 $ repeat "") @?= 1 -- Most enums take 1 bytes.
143 | , testCase "enumBytes D" $ enumBytes (take 256 $ repeat "") @?= 1 -- Including this one.
144 | , testCase "enumBytes E" $ enumBytes (take 257 $ repeat "") @?= 2 -- But not this one.
145 | , testCase "enumBytes F" $ enumBytes (take (256*256) $ repeat "") @?= 2 -- Not this one either.
146 | , testCase "enumBytes G" $ enumBytes (take (256*256 + 1) $ repeat "") @?= 3 -- And definitely not this one.
147 | , testCase "enumBytes H" $ enumBytes (take (256*256*256 + 1) $ repeat "") @?= 4 -- And so on.
148 | , testCompiles "examples/immix/layout.flp"
149 | , testCase "delta_bytes round small" $ delta_bytes (SizeLit (Just $ Lit $ 8*3-1) Bit) @?= 3
150 | , testCase "delta_bytes no rounding" $ delta_bytes (SizeLit (Just $ Lit $ 8*3 ) Bit) @?= 3
151 | , testCase "delta_bytes round large" $ delta_bytes (SizeLit (Just $ Lit $ 8*3+1) Bit) @?= 4
152 | ]
153 |
154 | main :: IO ()
155 | main = defaultMainWithOpts
156 | [ testCase "Binary conversion" test_b2i
157 | , testCase "Binary conversion" test_b2i'
158 | , tokenizeSuite
159 | , parseSuite
160 | , graftSuite
161 | , compileSuite
162 | ] mempty
163 |
164 |
--------------------------------------------------------------------------------