Giter Club home page Giter Club logo

manticore's Introduction

Manticore

pipeline status

Manticore is a high-level parallel programming language aimed at general-purpose applications running on multi-core processors. Manticore supports parallelism at multiple levels: explicit concurrency and coarse-grain parallelism via CML-style constructs and fine-grain parallelism via various light-weight notations, such as parallel tuple expressions and NESL/Nepal-style parallel array comprehensions.

REQUIREMENTS

Manticore currently only supports the x86-64 (a.k.a. AMD64) architecture running on either Linux or macOS. It is possible to build the compiler on other systems, but we have not ported the runtime system to them yet.

Manticore is implemented in a mix of C and SML code. You will need a recent version of SML/NJ (version 110.81+) installed. Furthermore, your installation of SML/NJ should include the MLRISC library. Note that SML/NJ 110.82's version of MLRISC is currently incompatible with our build system.

If you would like to have the LLVM backend available for use, follow the instructions below. Otherwise, skip to "Building from Source".

Optional Prerequisite: LLVM

You must have a custom version of LLVM built prior to configuring and building Manticore in order to have the LLVM backend available for use. The following commands will obtain the right LLVM sources and place it in ./llvm/src

git submodule init llvm/src
git submodule update llvm/src

Next, we're going to build LLVM, which has its own set of prerequisites that any Unix machine setup for C++ development should already have. To configure LLVM, run the following commands

cd llvm
mkdir build
cd build
cmake -G "Unix Makefiles" -DLLVM_TARGETS_TO_BUILD="X86" -DCMAKE_BUILD_TYPE=Release ../src

Next, we will build only the parts of LLVM that we need, which will take a few minutes. Replace n below with the number of parallel jobs you would like to use during the build, such as the number of cores on your system. To get the build going, run the following

make llc opt -j n

then, move back to the root directory with

cd ../..

and continue with configuring and building Manticore below. Note that LLVM will now be available under ./llvm/build and you should not need to rebuild it again.

Building From Source

If building and installing the system from source, you first must generate the configuration script. To do so, run the following two commands:

autoheader -Iconfig
autoconf -Iconfig

Then proceed with configuration.

Configuring

Our next step is to run the configure script. If you are using the MLRISC library included with your SML/NJ installation and do not plan to use the LLVM backend, you can simply run

./configure

and then skip to the build/installation step. Otherwise, you can add some of the following options to configure before moving on to building.

Configuring with external MLRISC

If you would like to configure with external MLRISC libraries, add the --with-mlrisc option.

./configure --with-mlrisc=<path to mlrisc>

Configuring with LLVM

If you want to have the LLVM backend available, configure with the local installation of LLVM after building it (using the instructions above) by adding the --with-llvm option to configure.

./configure --with-llvm=./llvm/build

Building and Installing the Distribution

Next, to build the compiler, we use the following command.

make build

We can install locally

make local-install

or globally.

make install

If you chose to do a local install, you'll find the compiler, pmlc, under the bin directory. Run pmlc -h for usage information.

Testing

Details about running the regression suite with various backends goes here.

Benchmarks

git submodule init src/benchmarks
git submodule update src/benchmarks

Then, see the README file under src/benchmarks for more details.


Known Issues

Some of the items below are out of date.

Larger Issues

  • The frontend does not support signatures, functors, record types, and a slew of corner cases in the language.

  • PVal and PTuples cannot be used together. The "fast clone" translation breaks invariants relied on by the work-stealing scheduler with regards to the valid intermediate states of the work queues.

  • Exception handling is not implemented.

  • The inatomic/from-atomic/to-atomic naming convention used in inline BOM is still a bugfest and should really be replaced by a static annotation that is checked by the compiler.

  • The basis library is a hodgepodge mess. The few structures that exist are typically dramatically different from the SML basis library due to the subset of the language implemented, which both makes existing code from another system hard to reuse and sometimes the interface cannot even be written.

Smaller Issues

  • The effect analysis defined in bom-opt/remove-atomics.sml should be changed from being name-based to instead either have a trackable annotation or other better marker for user-level code that uses mutable state. Additionally, while we remove ATOMIC operations around PURE functions, we do not handle reducing them in the case where the code between the parallel spawn and another lock is PURE.

  • We cannot handle allocations larger than a single heap page size (minus some slop). These allocations result in an exception, which is tough to debug because there is no exception handling.

  • The work-stealing scheduler cannot handle more than a stack of 32k tasks, and crashes quietly when that is exceeded.

  • Memoization and mutable state exist only as hand-performed translations to call basis library functions.

Incomplete projects

  • The safe-for-space closure conversion was not completed. While its code may be used for inspiration, we were not able to get a full write-up on its status before the student graduated.

  • In CFG, we now have code that performs rudimentary loop identification and can also generate a DOT file for visualization of basic blocks. Loop unrolling was not implemented.

  • A branch was created for the BOM implementation of flattening, but it is still in the design phase.

manticore's People

Contributors

johnreppy avatar kavon avatar matthewfluet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

manticore's Issues

Large list literals (nucleic)

From the nucleic benchmark's TODO, there seems to be an issue with allocating a large list in the heap from a literal. I believe this is a problem regardless of what backend is used.

I think this program hits a segfault because it tries to allocate too much in
the local heap (aka, a lot of allocation within one function). In particular,
take a look at how huge the list literals are in this program.

In addition, it seems compiling this program can cause LLC to hang for a
very long time (over 2 minutes).

I see two solutions:

(1) Try adding a test to see how much allocation is checked for by
    add-alloc-checks in CFG. If the value is too high, maybe we should split
    the block or insert an additional heap test.

(2) [preferred] move the data stored in the list constant into a file, and
    read it in line-by-line to build the list up.

See the TODO file in: https://github.com/ManticoreProject/benchmark/tree/master/benchmarks/programs/seq-nucleic

Manticore Nix package incompatible with SML/NJ >= 110.81

@kavon Hello! I am facing an issue with building manticore using Nix using SML/NJ v110.84 on x86_64. (Failing build log)

The error is being encountered with code in amd64-gen-fn.sml

[compiling driver/(x86_64-linux.cm):../codegen/(sources.cm):(wrapper.cm):(group.cm):amd64/amd64-gen-fn.sml]
codegen/amd64/amd64-gen-fn.sml:110.33-118.7 Error: value type in structure doesn't match signature spec
    name: signBit
  spec:   int -> AMD64MLTree.rexp
  actual: int -> Label.label
codegen/amd64/amd64-gen-fn.sml:110.33-118.7 Error: value type in structure doesn't match signature spec
    name: negateSignBit
  spec:   int -> AMD64MLTree.rexp
  actual: int -> Label.label
Error: Compiler bug: ModuleUtil: getStr: bad entity
Compilation failed.
make[2]: *** [Makefile:83: pmlc.x86-linux] Error 1
make[2]: Leaving directory '/nix/store/5zy5w8jjm8vzaqjhhqpn9z7mpncmk01m-manticore-2017.08.22/repo_checkout/src/tools/mc'
make[1]: *** [Makefile:39: build] Error 2
make[1]: Leaving directory '/nix/store/5zy5w8jjm8vzaqjhhqpn9z7mpncmk01m-manticore-2017.08.22/repo_checkout/src'
make: *** [Makefile:58: build] Error 2
builder for '/nix/store/g21hjshf6pha15fdqmg6k34id718gijn-manticore-2017.08.22.drv' failed with exit code 2
error: build of '/nix/store/g21hjshf6pha15fdqmg6k34id718gijn-manticore-2017.08.22.drv' failed

The same build succeeds with SML/NJ v110.79. (Working build log) -

[compiling driver/(x86_64-linux.cm):../codegen/(sources.cm):(wrapper.cm):(group.cm):amd64/amd64-gen-fn.sml]
codegen/amd64/amd64-gen-fn.sml:102.23-104.30 Warning: match nonexhaustive
          32 => ...
          64 => ...
  
codegen/amd64/amd64-gen-fn.sml:87.17-89.24 Warning: match nonexhaustive
          32 => ...
          64 => ...

Any help on why this is failing would be greatly appreciated!

Stack slot alignment

it seems that the alignment of stack slots setup by the prologue does not match the LLVM code generator's expectations. From the ffi-fib benchmark on all stack-based variants:

Thread 2 "a.out" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff63ff700 (LWP 28269)]
go_cfg3CDE_49A0 () at mc-seq.s:732
732		movapd	%xmm1, 40(%rsp)         # 16-byte Spill

Odd issue in alt-ret-tuple

See the comment in alt-ret-tuple.pml from the regression suite for details. I'm not sure what's going on actually. Referenced in commit 9ad955a

Unboxing an uncurried function

From the old "curried vs uncurried" bug report, when compiling with -sequential, which also turns on unboxing, we get the following broken BOM after unboxing

➤ ../../../bin/pmlc -sequential -Cbom.check-all=true curried_vs_uncurried.pml 
***** Bogus BOM in Main after unbox *****
** type mismatch in Apply _t<BB80>#1.1 in h_uncurried<C6AA>#2.2
==   expected  int
==   but found [int]
** type mismatch in Apply _t<BA8F>#1.1 in h_uncurried<C6AA>#2.2
==   expected  int
==   but found [int]
broken BOM dumped to broken-BOM
bom/check-bom.sml:544.11-544.28: Fail: broken BOM

Here's the program:

fun theFunction x i = 
    let fun g x accum =
            case x
                of x::xs => h xs (accum + x)
                 | nil => accum
        and h x accum =
             case x
                of y::nil => g x accum
                 | x::xs => h xs (accum + x)
                 | nil => accum
    in  g x 0 end

val _ = print("Result = " ^ Int.toString (theFunction (List.tabulate(100, fn _ => 1)) 1000 ) ^ "\n")

Uncurrying mutually recursive funs

The new seq-deriv benchmark program crashes Manticore with:

➤ ../../../../../bin/pmlc -sequential mc-seq.mlb 
../../smlnj-lib/Util/hash-table-fn.sml:85.31-85.40: Fail: UncurriedFns

This failure is raised from the uncurrying pass in BOM. Here's the program:

(* adapted from the Larceny benchmark

;;; DERIV -- Symbolic derivation.

;;; Returns the wrong answer for quotients.
;;; Fortunately these aren't used in the benchmark.
*)


datatype expr
  = Add of expr list
  | Sub of expr list
  | Mul of expr list
  | Div of expr list
  | Num of int
  | X

structure Benchmark = struct

val hd = List.hd
val tl = List.tl
val map = List.map
val app = List.app
val foldl = List.foldl

fun prnt p = (case p
  of X => print "x"
   | Num a => print (Int.toString a)
   | Add xs => prntLst "+" xs
   | Sub xs => prntLst "-" xs
   | Mul xs => prntLst "*" xs
   | Div xs => prntLst "/" xs
  (* end case *))
and prntLst symb xs = (print "("; print symb;
                       app prnt xs; print ")\n")

fun equal p = (case p
  of (X, X) => true
   | (Num a, Num b) => a = b
   | (Add xs, Add ys) => chkLists (xs, ys)
   | (Sub xs, Sub ys) => chkLists (xs, ys)
   | (Mul xs, Mul ys) => chkLists (xs, ys)
   | (Div xs, Div ys) => chkLists (xs, ys)
   | _ => false
  (* end case *))
and chkLists p = ListPair.foldlEq chk true p
and chk (a, b, acc) = acc andalso equal (a, b)

fun deriv a = (case a
  of X => Num 1
   | Num _ => Num 0
   | Add es => Add (map deriv es)
   | Sub es  => Sub (map deriv es)
   | Mul es  => Mul [
            a,
            Add (map (fn x => Div [deriv x, x]) es)
            ]

   | Div (x :: y :: nil) => Sub [
        Div [deriv x, y],
        Div [x, Mul [y, y, deriv y]]
      ]
   | _ => raise Fail "No derivation method available"
  (* end case *))


fun go (ans, exp) = let
  val computed = deriv exp
  in
    if equal (ans, computed)
    then ()
    else (prnt computed ; raise Fail "wrong answer!")
  end

end


structure Main =
  struct

  	val iterations = 2000000

    fun main _ = let

      fun mkExp a b =
        Add [
          Mul [Num 3, X, X],
          Mul [a, X, X],
          Mul [b, X],
          Num 5
        ]

      fun mkAns a b =
        Add [
          Mul [
            Mul [Num 3, X, X],
            Add [
              Div [Num 0, Num 3],
              Div [Num 1, X],
              Div [Num 1, X]
            ]
          ],
          Mul [
            Mul [a, X, X],
            Add [
              Div [Num 0, a],
              Div [Num 1, X],
              Div [Num 1, X]
            ]
          ],
          Mul [
            Mul [b, X],
            Add [
              Div [Num 0, b],
              Div [Num 1, X]
            ]
          ],
          Num 0
        ]

      val a = Num 5
      val b = Num 7
      val exp = mkExp a b
      val ans = mkAns a b

      fun doit () = Benchmark.go (ans, exp)

      fun lp 0 = ()
      	| lp n = (doit(); lp (n-1))

      fun start () = lp iterations

  	in
      	start ()
  	end

end

val _ = Main.main (CommandLine.name (), CommandLine.arguments ())

See the file here: https://smlnj-gitlab.cs.uchicago.edu/manticore/benchmarks/blob/704571634ff114d06cff9f7d87256eab627536a2/benchmarks/programs/seq-deriv/prog.pml

Case simplify produces invalid BOM

Seems there's a bug in case simplify? I'm using d5dfda2

➤ ../trunk-pmlc/bin/pmlc -Cbom.check-all=true fib-datatype.pml 
***** Bogus BOM in Main after case-simplify *****
** invalid cast:(_t<D762>#2:int) = (int)left<F8FC>#5:any
** invalid cast:(_t<D773>#1:int) = (int)right<F8FD>#7:any
** type mismatch in return from fib<F8FA>#3.3
==   expected  any
==   but found int
** type mismatch in return from fib<F8FA>#3.3
==   expected  any
==   but found int
** invalid cast:(_t<D751>#1:int) = (int)right<F8FD>#7:any
** type mismatch in return from fib<F8FA>#3.3
==   expected  any
==   but found int
** type mismatch in return from fib<F8FA>#3.3
==   expected  any
==   but found int
** invalid cast:(_t<D78A>#1:int) = (int)ans<F90E>#3:any
broken BOM dumped to broken-BOM
bom/check-bom.sml:544.11-544.28: Fail: broken BOM

See fib-datatype.pml below:

datatype fib_result
  = Zero
  | One
  | Val of int

fun add a b = (case (a, b)
  of (Zero, b) => b
   | (a, Zero) => a
   | (One, One) => Val 2
   | (One, Val b) => Val (b+1)
   | (Val a, One) => Val (a+1)
   | (Val a, Val b) => Val (a+b)
  (* end case *))

fun show a = (case a
  of Zero => "0"
   | One => "1"
   | Val a => Int.toString a
  (* end case *))

fun fib n = (case n
  of 0 => Zero
   | 1 => One
   | _ => let
        val left = fib (n-1)
        val right = fib (n-2)
      in
        add left right
      end
  (* end case *))

val i2s = Int.toString

val n = 30  (* https://oeis.org/A000045/list *)
val ans = fib n

val _ = print ("fib(" ^ i2s n ^ ") = " ^ show ans ^ "\n")

Linked Frame GC Race

Caught this in the benchmarks directory's parallel cml-merge when the message size was set very high.

==================
WARNING: ThreadSanitizer: data race (pid=3448)
  Read of size 8 at 0x7f78c4652ea8 by thread T5:
    #0 majorGCscanLINKFRAMEpointer ../../gc/major-gc-scan.c:66 (a.out+0x20437)
    #1 ScanMajorToSpace ../../gc/major-gc.c:503 (a.out+0x1e3a4)
    #2 PromoteObj ../../gc/major-gc.c:420 (a.out+0x1e000)
    #3 <null> <null> (a.out+0x4ab15)
    #4 IdleVProc ../../vproc/vproc.c:673 (a.out+0x3f950)
    #5 NewVProc ../../vproc/vproc.c:394 (a.out+0x3ecd5)
    #6 InitWithLocation ../../cpu/topology.c:106 (a.out+0x4a3f3)
    #7 <null> <null> (libtsan.so.0+0x296ad)

  Previous write of size 8 at 0x7f78c4652ea8 by thread T3:
    #0 ForwardObjMajor ../../gc/major-gc.c:52 (a.out+0x1cd17)
    #1 majorGCscanBITPATpointer ../../gc/major-gc-scan.c:148 (a.out+0x207b0)
    #2 ScanMajorToSpace ../../gc/major-gc.c:503 (a.out+0x1e3a4)
    #3 MajorGC ../../gc/major-gc.c:322 (a.out+0x1d8ad)
    #4 MinorGC ../../gc/minor-gc.c:527 (a.out+0x18822)
    #5 RunManticore ../../vproc/apply.c:115 (a.out+0x43d29)
    #6 IdleVProc ../../vproc/vproc.c:673 (a.out+0x3f950)
    #7 NewVProc ../../vproc/vproc.c:394 (a.out+0x3ecd5)
    #8 InitWithLocation ../../cpu/topology.c:106 (a.out+0x4a3f3)
    #9 <null> <null> (libtsan.so.0+0x296ad)

  Thread T5 (tid=3469, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x2bcee)
    #1 ThreadCreate ../../include/os-threads.h:30 (a.out+0x49918)
    #2 SpawnAt ../../cpu/topology.c:118 (a.out+0x4a49b)
    #3 VProcInit ../../vproc/vproc.c:218 (a.out+0x3df67)
    #4 main ../../misc/main.c:121 (a.out+0x414c5)

  Thread T3 (tid=3465, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x2bcee)
    #1 ThreadCreate ../../include/os-threads.h:30 (a.out+0x49918)
    #2 SpawnAt ../../cpu/topology.c:118 (a.out+0x4a49b)
    #3 VProcInit ../../vproc/vproc.c:218 (a.out+0x3df67)
    #4 main ../../misc/main.c:121 (a.out+0x414c5)

Runaway Memory Usage

The following benchmark program (seq-mazefun) executes pretty quickly in MLton, but after compiling with Manticore it has runaway memory usage. Of course this causes GC thrashing so the program doesn't seem to terminate before hitting the swapfile.

There must be some bug causing some data to stay live according to the GC longer than it should.

(* from the Larceny benchmark suite

;;; MAZEFUN -- Constructs a maze in a purely functional way,
;;; written by Marc Feeley.
;;; ported to SML by Kavon Farvardin

*)

datatype maze_elm
  = Pt of int * int
  | Empty

structure Benchmark = struct

  val hd = List.hd
  val tl = List.tl
  val concat = List.concat
  val length = List.length
  val null = List.null
  val foldr = List.foldr

  fun tup1 (x, _) = x
  fun tup2 (_, x) = x

  (* operations on maze elms *)
  fun fst e = (case e
    of Pt (x, _) => x
     | _ => raise Fail "not a point"
     (* end case *))

  fun snd e = (case e
    of Pt (_, x) => x
     | _ => raise Fail "not a point"
     (* end case *))

  fun mazeElmEqual p = (case p
    of (Pt (x, y), Pt (a, b)) => x = a andalso y = b
     | (Empty, Empty) => true
     | _ => false
     (* end case *))

  fun mazeElmToString p = (case p
    of Pt _ => " _"
     | Empty => " *"
    (* end case *))

  fun printStringMat mat =
    app (fn lst => (app print lst; print "\n")) mat
(***********)

  (* the args to f are flipped,
     i.e. acc is on the left in Scheme *)
  fun foldl f id xs = let
    fun lp (xs, acc) = (case xs
      of nil => acc
       | x :: xs => lp(xs, f (acc, x))
      (* end case *))
    in
      lp (xs, id)
    end

  fun for lo hi f = let
      fun lp lo =
        if lo < hi
          then f lo :: lp (lo + 1)
          else nil
    in
      lp lo
    end

  fun listRead lst i =
    if i = 0
      then hd lst
      else listRead (tl lst) (i-1)

  fun listWrite lst i new =
    if i = 0
      then new :: tl lst
      else hd lst :: listWrite (tl lst) (i-1) new

  fun listRemovePos lst i =
    if i = 0
      then tl lst
      else hd lst :: listRemovePos (tl lst) (i-1)

  fun member x = List.exists (fn y => mazeElmEqual (x, y))

  fun hasDuplicates lst = (case lst
    of nil => false
     | x :: xs => member x xs orelse hasDuplicates xs
    (* end case *))

  fun makeMatrix n m init =
    for 0 n (fn i =>
      for 0 m (fn j =>
        init i j
      )
    )

  fun matrixRead mat i j = listRead (listRead mat i) j

  fun matrixWrite mat i j new =
    listWrite mat i (listWrite (listRead mat i) j new)

  fun matrixSize mat = (length mat, length (hd mat))

  fun matrixMap f mat = map (fn lst => map f lst) mat

  fun nextRandom cur =
    ((cur * 3581) + 12751) mod 131072

  fun shuffle lst = let
    fun shuf lst rand =
      if null lst
        then nil
        else let
          val newRand = nextRandom rand
          val i = newRand mod (length lst)
        in
          listRead lst i
            :: shuf (listRemovePos lst i) newRand
        end
  in
    shuf lst 0 (* <- the seed *)
  end

  fun odd n = n mod 2 = 1
  fun even n = n mod 2 = 0

  fun caveToMaze cave = matrixMap mazeElmToString cave

  fun pierce pos cave = matrixWrite cave (fst pos) (snd pos) pos

  fun neighboringCavities pos cave = let
      val size = matrixSize cave
      val n = tup1 size
      val m = tup2 size
      val i = fst pos
      val j = snd pos

      fun notEmpty (i, j) = (case matrixRead cave i j
        of Empty => false
         | _ => true
         (* end case *))
    in
      concat [
        if i > 0 andalso notEmpty (i-1, j)
          then [Pt (i-1, j)]
          else nil,
        if i < n-1 andalso notEmpty (i+1, j)
          then [Pt (i+1, j)]
          else nil,
        if j > 0 andalso notEmpty (i, j-1)
          then [Pt (i, j-1)]
          else nil,
        if j < m-1 andalso notEmpty (i, j+1)
          then [Pt (i, j+1)]
          else nil
      ]
    end

  and changeCavity cave pos newID = let
      fun change cave pos newID oldID = let
        val i = fst pos
        val j = snd pos
        val cavityID = matrixRead cave i j
      in
        if mazeElmEqual (cavityID, oldID)
          then foldl (fn (c, nc) =>
                        change c nc newID oldID)
                     (matrixWrite cave i j newID)
                     (neighboringCavities pos cave)
          else cave
      end
    in
      change cave pos newID (matrixRead cave (fst pos) (snd pos))
    end

  and tryToPierce pos cave = let
    val ncs = neighboringCavities pos cave
  in
    if hasDuplicates
        (map (fn nc => matrixRead cave (fst nc) (snd nc)) ncs)
      then cave
      else pierce
              pos
              (foldl (fn (c, nc) => changeCavity c nc pos)
                     cave
                     ncs)
  end

  and pierceRandomly possibleHoles cave = (case possibleHoles
    of nil => cave
     | hole :: rest => pierceRandomly rest (tryToPierce hole cave)
    (* end case *))

  fun makeMaze n m = if not (odd n andalso odd m)
    then raise Fail "n and m must be odd"
    else let
      fun init i j = if even i andalso even j
                      then Pt (i, j)
                      else Empty

      val cave = makeMatrix n m init

      val possibleHoles = concat (
            for 0 n (fn i => concat (
              for 0 m (fn j =>
                if even i andalso even j
                  then nil
                  else [Pt (i, j)]
              ))
            ))

    in
      caveToMaze (pierceRandomly (shuffle possibleHoles) cave)
    end


  fun go (n, m) =
    printStringMat (makeMaze n m)

end


structure Main =
  struct

    val iterations = 1

    val n = 11
    val m = 11

    fun main _ = let

      fun doit () = Benchmark.go (n, m)

      fun lp 0 = ()
      	| lp n = (doit(); lp (n-1))

      fun start () = lp iterations

  	in
      	start ()
  	end

end

val _ = Main.main (CommandLine.name (), CommandLine.arguments ())

Wrapping captures in the context of an unused return cont

Before wrap-captures we have

fun contFib_uncurried<C6EA>#4.4 (unboxP<C6F5>#9:int,param<C6F6>#4.4:cont(any) / retK<C6F7>#0:cont(int),_exh<C6F8>#4:cont(any)) =
(* ... *)
  cont currentContinuation<C717>#1 (ccArg<C718>#1:any) =
      let c<C712>#1:[int] = ([int])ccArg<C718>#1
      let _t<C713>#1:int = #0(c<C70B>#1)
      let _t<C714>#1:int = #0(c<C712>#1)
      let _t<C715>#1:int = I32Add(_t<C713>#1,_t<C714>#1)
      let res<C716>#1:[int] = alloc (_t<C715>#1)
      throw param<C6F6>#4.4 res<C716>#1
  (**** End currentContinuation<C717>#1 ****)
  apply contFib_uncurried<C6EA>#4.4 (_t<C70D>#1,currentContinuation<C717>#1 / letJoinK<C85D>#1,_exh<C6F8>#4)

Note that contFib_uncurried does not return. We're currently generating the following wrapping

fun manipK2<C93C>#1 (currentContinuation<C939>#1:cont(any) / landingPadK<C93A>#1.1:cont(int,any),deadExnK<C93B>#0:cont(any)) =
    cont invokeRetk<C93D>#0 (param<C93E>#1:any) =
        let t<C93F>#1:int = 1:int
        throw landingPadK<C93A>#1.1 (t<C93F>#1,param<C93E>#1)
    (* ... *)
    
cont setjmpLandingPad<C933>#1 (regularRet<C934>#1:int,arg<C935>#2:any) =
    let t<C938>#1:int = 0:int
    if I32NEq(regularRet<C934>#1,t<C938>#1) then
      let arg<C936>#1:int = (int)arg<C935>#2  (* invalid cast! *)
      throw retK<C6F7>#2.2 arg<C936>#1        (* this return never will happen *)
    else
      let arg<C937>#1:any = (any)arg<C935>#2
      throw currentContinuation<C717>#1.1 arg<C937>#1

callec (manipK2<C93C>#1 / setjmpLandingPad<C933>#1,deadExh<C941>#1)

Previously this was just a bit inefficient, but it's also incorrect since
an we don't know how to match up the types even if a return did occur (the
unboxing of the int).

It's better to just omit the landing pad in this case and just generate:

callec (manipK2<C93C>#1 / currentContinuation<C717>#1,deadExh<C941>#1)

so that C717 turns into a return cont, instead of a join cont.

CPS inliner issue with recursive function being mapped

Greetings from Sweden and Chalmers University of Technology!

I may have found something when exploring parallel arrays in Manticore. Here is a piece of code that works perfectly fine:

fun f n = if n <= 0 then 0 else 1 + f (n - 1)
val xs = [ 1, 2, 3 ]
val _ = map f xs

Here is a similar program with a parallel array and PArray.map instead of a list and the regular map:

(* f same as before *)
val xs = PArray.fromRope (Rope.tabulateSequential (fn x => x) (1, 3))
val _ = PArray.map (fn x => f x) xs

That code also works. But watch what happens when I eta-reduce the mapped function:

val _ = PArray.map f xs

The code doesn't compile anymore:

$ pmlc eta.pml
***** Bogus CPS in Main after inline *****
** unbound variable f<11431>#4.4
** unbound variable f<11431>#4.4 in Apply
** unbound variable f<11431>#4.4
** unbound variable f<11431>#4.4 in Apply
broken CPS dumped to broken-CPS

The dumped file is 1.7 MB, but I can't really extract any useful information from it.

It also turns out that everything works fine if f is not recursive:

fun f n = if n <= 0 then 0 else 1
val xs = PArray.fromRope (Rope.tabulateSequential (fn x => x) (1, 3))
val _ = PArray.map f xs

I'm not an expert, but there seems to be something going on here. What do you think?

Compilation failure on SMLNJ 110.95

smlnj 110.95 (probably 110.94 too, as it switched the type of source-file positions from Position.int to Int.int) can't build the latest revision of manticore:

[compiling driver/(x86_64-linux.cm):../codegen/(sources.cm):(wrapper.cm):(group.cm):../common/(sources.cm):error.sml]
common/error.sml:137.39-137.59 Error: operator and operand do not agree [tycon mismatch]
  operator domain: (Int64.int * Int64.int) option *
                   (Int64.int * Int64.int) option
  operand:         span option * span option
  in expression:
    lt ((fn <pat> => <exp>) e1,(fn <pat> => <exp>) e2)
Compilation failed.

It works with the following change:

diff --git a/src/tools/mc/common/error.sml b/src/tools/mc/common/error.sml
index e880c5f18..370ce05eb 100644
--- a/src/tools/mc/common/error.sml
+++ b/src/tools/mc/common/error.sml
@@ -129,9 +129,9 @@ structure Error :> sig
 	  fun lt (NONE, NONE) = false
 	    | lt (NONE, _) = true
 	    | lt (_, NONE) = false
-	    | lt (SOME(l1, r1), SOME(l2, r2)) = (case Position.compare(l1, l2)
+	    | lt (SOME(l1, r1), SOME(l2, r2)) = (case Int.compare(l1, l2)
 		 of LESS => true
-		  | EQUAL => (Position.compare(r1, r2) = LESS)
+		  | EQUAL => (Int.compare(r1, r2) = LESS)
 		  | GREATER => false
 		(* end case *))
 	  fun cmp (e1 : error, e2 : error) = lt(#pos e1, #pos e2)

I'm not sure if it's the right approach as it would break compilation with smlnj versions before 110.94 (and I didn't see any pragmas/etc for backwards-compatibility of this sort; though I'm really new to SML and might've missed it).
What's your usual approach to this?

String literals in assembly file

➤ ../bin/pmlc -sequential -mlrisc literal.pml 
literal.s:2131:9: error: invalid escape sequence (unrecognized character) in '.asciz' directive
        .asciz  "mlrisc\'s string literal bug"
                ^
error compiling generated assembly code

from the following program

val _ = print "mlrisc's string literal bug"

On Ubuntu 18.04, likely using clang to compile.

Accessing to a nested array causes segmentation fault

When I write a parallel sorting program, I found that the following code causes segmentation fault both on my mac and linux.

fun copy a b e d j =
    if e < b then ()
    else let val v = Array.sub (Array.sub (a, 0), b)
         in Array.update (Array.sub (d, 0), j, v);
            copy a (b+1) e d (j+1)
         end

fun sort a b e =
    if e <= b then () else
    let val q = Int.quot (b + e, 2)
    in sort a b q;
       sort a (q+1) e;
       copy a b e a b
    end

val a = Array.array (1, Array.array (1024, 0))
val _ = sort a 0 1023

I checked out the repository and compiled the compiler as follows:

git clone https://github.com/ManticoreProject/manticore.git
git checkout 757fbcc4d   # to make clear which revision I used
autoconf -Iconfig
autoheader -Iconfig
./configure --prefix=$PWD/dest
mkdir -p dest/bin dest/lib
make install

I saved the above code in bug.pml and executed it as follows:

$ dest/bin/pmlc -o bug bug.pml
$ ./bug
Segmentation fault

Eta-reduce pass

There's a CPS eta-reduce pass already written in the partial_abort_stm branch, which is run just before eta-expand.

It might be worth the effort to pull this optimization over to trunk, though I'm not sure what motivated @lematt1991 to implement this, i.e., is it meant to improve general programs, or clean-up some STM-specific transform?

Bad 8-bit registers

Opening issue related to this bug, since the workaround below was removed from trunk:

    (* MLRISC is incorrectly naming some of the 8-bit registers, so as a simple
     * workaround, we are just going to find/replace them in the output text file
     *)
    fun replace8BitRegisters asmFile = let
	  val cmd = "sed -i \"s/%ah/%spl/g; s/%ch/%bpl/g; s/%dh/%sil/g; s/%bh/%dil/g\" " ^ asmFile
	  in
	    ignore (OS.Process.system cmd)
	  end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.