├── bench ├── .gitignore ├── factorial-worker-wrapper-optimised.stg ├── fibonacci-worker-wrapper-optimised.stg ├── ackerman-worker-wrapper-optimised.stg ├── compile-and-run-program.sh ├── compare-o0-and-o3.sh ├── lit.cfg └── lit.site.cfg.in ├── lib ├── optimizer.cpp └── libstg.c ├── src ├── StackAnalysis.cpp ├── jit.h ├── lexer.l ├── stgir.cpp ├── parser.yy ├── RuntimeDebugBuilder.cpp └── cxxopts.hpp ├── test ├── clean.sh ├── .gitignore ├── hello.stg ├── prim-case.stg ├── recursive-type-decl.stg ├── multiply-intrinsic.stg ├── simple-ap-prim-ints.stg ├── simple-let.stg ├── simple-case.stg ├── two-slot-case.stg ├── case-prim-alt-capture-var.stg ├── k-combinator.stg ├── sum-type-case.stg ├── case-scrutinee-literal.stg ├── seq.stg ├── case-boxed-alt-capture-var.stg ├── nested-nonrecursive-datatypes.stg ├── nested-case-alts.stg ├── partial-apply-let.stg ├── compile-and-run-program.sh ├── factorial-worker-wrapper-optimised.stg ├── compose-simple.stg ├── tunnel-through-cases.stg ├── tear-through-simple-recursion.stg ├── subtract.stg ├── multiply.stg ├── compose.stg ├── factorial.stg ├── ackerman.stg ├── lit.cfg └── lit.site.cfg.in ├── include └── sxhc │ ├── types.h │ ├── libstg.h │ ├── mir.h │ ├── StackAnalysis.h │ ├── RuntimeDebugBuilder.h │ ├── optimizer.h │ └── stgir.h ├── default.nix ├── docs ├── Doxyfile.in ├── DoxygenLayout.xml └── customdoxygen.css ├── .gitignore ├── CMakeLists.txt └── README.md /bench/.gitignore: -------------------------------------------------------------------------------- 1 | *.dot 2 | -------------------------------------------------------------------------------- /lib/optimizer.cpp: -------------------------------------------------------------------------------- 1 | #include "sxhc/optimizer.h" 2 | -------------------------------------------------------------------------------- /src/StackAnalysis.cpp: -------------------------------------------------------------------------------- 1 | #include "sxhc/StackAnalysis.h" 2 | -------------------------------------------------------------------------------- /test/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm *.ll 3 | rm *.out 4 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*.stg 3 | !lit.cfg 4 | !lit.site.cfg.in 5 | -------------------------------------------------------------------------------- /include/sxhc/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "llvm/IR/IRBuilder.h" 3 | 4 | using StgIRBuilder = llvm::IRBuilder<>; 5 | -------------------------------------------------------------------------------- /include/sxhc/libstg.h: -------------------------------------------------------------------------------- 1 | #include 2 | extern "C" { 3 | void printOnlyInt(int64_t i) __attribute__((used)); 4 | } 5 | -------------------------------------------------------------------------------- /test/hello.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 42 3 | 4 | binding main = \() -> Boxed { printInt (42) }; 5 | -------------------------------------------------------------------------------- /test/prim-case.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 4 3 | 4 | binding main = \() -> Boxed { 5 | case 4 of 6 | x -> printInt (x); 7 | }; 8 | -------------------------------------------------------------------------------- /test/recursive-type-decl.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 5 3 | # Check that we can declare a recursive type. 4 | 5 | data List = ListCons (PrimInt List); 6 | binding main = \() -> Boxed { printInt (5) }; 7 | -------------------------------------------------------------------------------- /test/multiply-intrinsic.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 200 3 | 4 | binding main = \() -> Boxed { case primMultiply (10 20) of 5 | r -> printInt (r); 6 | }; 7 | -------------------------------------------------------------------------------- /test/simple-ap-prim-ints.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 4 3 | 4 | data Int = Int (PrimInt); 5 | binding three = \(i: PrimInt, j: PrimInt, k: PrimInt) -> Boxed { printInt (j) }; 6 | binding main = \() -> Boxed { three (500 4 100) }; 7 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { }: 2 | 3 | let 4 | pkgs = import { }; 5 | in 6 | pkgs.stdenv.mkDerivation { 7 | name = "simplexhc-1.0.0"; 8 | src = ./.; 9 | buildInputs = [ pkgs.cmake pkgs.flex pkgs.bison pkgs.boost pkgs.llvm_8 10 | pkgs.doxygen pkgs.graphviz]; 11 | } 12 | -------------------------------------------------------------------------------- /test/simple-let.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 3 3 | 4 | data Int = Int (PrimInt); 5 | binding main = \() -> Boxed { 6 | let 7 | binding three = \() -> Int { Int (3) }; 8 | in case three () of 9 | Int ( x ) -> printInt (x); 10 | }; 11 | -------------------------------------------------------------------------------- /test/simple-case.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 3 3 | 4 | data Int = Int (PrimInt); 5 | binding three = \() -> Int { Int (3) }; 6 | binding main = \() -> Boxed { case three () of 7 | Int ( x ) -> printInt (x); 8 | }; 9 | -------------------------------------------------------------------------------- /test/two-slot-case.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 2 3 | 4 | data Int = Int2(PrimInt PrimInt); 5 | binding three = \() -> Int { Int2 (5 2) }; 6 | binding main = \() -> Boxed { case three () of 7 | Int2(x y) -> printInt (y); 8 | }; 9 | -------------------------------------------------------------------------------- /docs/Doxyfile.in: -------------------------------------------------------------------------------- 1 | OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doc_doxygen/ 2 | INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src/ @CMAKE_CURRENT_SOURCE_DIR@/docs 3 | HTML_EXTRA_STYLESHEET = @CMAKE_CURRENT_SOURCE_DIR@/docs/customdoxygen.css 4 | LAYOUT_FILE = @CMAKE_CURRENT_BINARY_DIR@/docs/DoxygenLayout.xml 5 | -------------------------------------------------------------------------------- /test/case-prim-alt-capture-var.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 4 3 | 4 | data Int = IntCons (PrimInt); 5 | 6 | binding foo = \(primIntParam: PrimInt) -> Boxed { 7 | case 4 of 8 | r -> printInt (primIntParam); 9 | }; 10 | 11 | binding main = \() -> Boxed { 12 | foo (4) 13 | }; 14 | -------------------------------------------------------------------------------- /test/k-combinator.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 4 3 | 4 | data Int = Int2 (PrimInt PrimInt); 5 | binding k = \(x: PrimInt y:PrimInt) -> Boxed { printInt (x) }; 6 | binding three = \() -> Int { Int2 (3 4) }; 7 | binding main = \() -> Boxed { case three () of 8 | Int2 (x y) -> k (y x); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/libstg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef void(*Continuation)(void); 5 | 6 | typedef struct { 7 | Continuation *cont; 8 | } Closure; 9 | 10 | extern void *stackReturn[5000]; 11 | extern uint64_t stackReturnTop; 12 | 13 | extern int64_t popInt(); 14 | extern void pushInt(int64_t); 15 | 16 | 17 | void printOnlyInt(int64_t i) { 18 | printf("%ld", i); 19 | } 20 | -------------------------------------------------------------------------------- /test/sum-type-case.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 10 3 | 4 | data DInt = Int (PrimInt) | Int2 (PrimInt PrimInt); 5 | 6 | binding three = \() -> DInt { Int2 (5 10) }; 7 | binding main = \() -> Boxed { case three () of 8 | Int2 (x y) -> printInt (y); 9 | Int (x) -> printInt (x); 10 | }; 11 | -------------------------------------------------------------------------------- /test/case-scrutinee-literal.stg: -------------------------------------------------------------------------------- 1 | # Check that a case with a literal scrutinee `pi` works. 2 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 3 | # CHECK: 3 4 | 5 | data Int = IntCons (PrimInt); 6 | binding f = \(pi: PrimInt) -> Int { 7 | case pi () of 8 | 0 -> IntCons (3); 9 | }; 10 | 11 | 12 | binding main = \() -> Boxed { case f (0) of 13 | IntCons (x) -> printInt (x); 14 | }; 15 | -------------------------------------------------------------------------------- /test/seq.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 3 3 | # CHECK: 4 4 | 5 | 6 | binding seq = \(a:()->Boxed b:()->Boxed) -> Boxed { 7 | case a () of 8 | default -> b (); 9 | }; 10 | 11 | binding printfour = \() -> Boxed { printInt (4) }; 12 | binding printthree = \() -> Boxed { printInt (3) }; 13 | 14 | binding printthreetwice = \() -> Boxed { seq (printthree printfour) }; 15 | 16 | binding main = \() -> Boxed { 17 | seq (printthreetwice printthree) 18 | }; 19 | -------------------------------------------------------------------------------- /bench/factorial-worker-wrapper-optimised.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 120 3 | 4 | 5 | binding factorial = \(a : PrimInt) -> PrimInt { 6 | case a () of 7 | 0 -> 1; 8 | x -> case primSubtract (x 1) of 9 | xmin1 -> case factorial (xmin1) of 10 | factxmin1 -> primMult (x factxmin1);;; 11 | }; 12 | 13 | 14 | binding main = \() -> Boxed { 15 | case factorial (5) of 16 | x -> printInt (x); 17 | }; 18 | -------------------------------------------------------------------------------- /test/case-boxed-alt-capture-var.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 4 3 | # Check if the alt (which captures primIntParam) is correctly codegened 4 | # in the case where the case scrutinee is boxed (three) 5 | 6 | data Int = IntCons (PrimInt); 7 | 8 | binding three = \() -> Int { IntCons (42) }; 9 | 10 | binding foo = \(primIntParam: PrimInt) -> Boxed { 11 | case three () of 12 | r -> printInt (primIntParam); 13 | }; 14 | 15 | binding main = \() -> Boxed { 16 | foo (4) 17 | }; 18 | -------------------------------------------------------------------------------- /test/nested-nonrecursive-datatypes.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 2 3 | 4 | data IntTy = IntCons (PrimInt); 5 | data IntWrappedTy = IntWrappedCons (IntTy); 6 | 7 | binding two = \() -> IntTy { IntCons (2) }; 8 | binding twowrapped = \() -> IntWrappedTy { IntWrappedCons (two) }; 9 | 10 | binding processint = \(int: (IntTy) -> Boxed) -> Boxed { 11 | case int () of 12 | IntCons (x) -> printInt (x); 13 | }; 14 | 15 | 16 | binding main = \() -> Boxed { 17 | case twowrapped () of 18 | IntWrappedCons (x) -> processint (x); 19 | }; 20 | -------------------------------------------------------------------------------- /include/sxhc/mir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace mir { 8 | class Program { 9 | }; 10 | 11 | 12 | // free variables, bound variables, calling convention 13 | class Lambda { 14 | }; 15 | 16 | 17 | class CasePrimInt { 18 | }; 19 | 20 | class CaseConstructor {}; 21 | 22 | // application / call 23 | class Ap {}; 24 | 25 | // packing information 26 | class Data { 27 | }; 28 | } // end namespace mir 29 | 30 | -------------------------------------------------------------------------------- /test/nested-case-alts.stg: -------------------------------------------------------------------------------- 1 | # Check that a case with a literal scrutinee `pi` works. 2 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 3 | # CHECK: 10 4 | 5 | data Int = IntCons (PrimInt); 6 | binding f = \(aprim: PrimInt, bprim: PrimInt) -> Int { 7 | case aprim () of 8 | 0 -> IntCons (1); 9 | aother -> case bprim () of 10 | 0 -> IntCons (10); 11 | bother -> IntCons(20);; 12 | }; 13 | 14 | 15 | binding main = \() -> Boxed { case f (1 0) of 16 | IntCons (x) -> printInt (x); 17 | }; 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/partial-apply-let.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 1 3 | 4 | data IntT = IntCons (PrimInt); 5 | 6 | binding one = \() -> Boxed { IntCons (1) }; 7 | binding two = \() -> Boxed { IntCons (2) }; 8 | 9 | binding k = \(x:Boxed y:Boxed) -> Boxed { x () }; 10 | binding main = \() -> Boxed { let 11 | binding k1 = \() -> Boxed { k (one) }; 12 | binding k12 = \(k1: Boxed) () -> Boxed { k1 (two) }; 13 | in case k12 () of 14 | IntCons (x) -> printInt (x); 15 | }; 16 | -------------------------------------------------------------------------------- /test/compile-and-run-program.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o xtrace 3 | set -e 4 | SIMPLEXHC=../../build/sxhc 5 | LIBSTGPATH=../../build/ 6 | LLC=llc 7 | CC=gcc 8 | 9 | OUTO=$1".out.o" 10 | 11 | $SIMPLEXHC $1 -o $OUTO ${@:2} -O 3 12 | $CC $OUTO -L $LIBSTGPATH -lstgruntime -o $1.out 13 | rm $OUTO 14 | 15 | # If this is at the CWD, then we need to execute it with `./` prepended 16 | # to the path. Otherwise, we can directly execute it. 17 | if [ $(dirname $1) == $(dirname $0) ]; then 18 | # need to figure out how to set STG programs's exit code :) 19 | ./$1.out || true 20 | else 21 | $1.out || true 22 | fi; 23 | 24 | exit 0 25 | 26 | -------------------------------------------------------------------------------- /test/factorial-worker-wrapper-optimised.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit -O 3| FileCheck %s 2 | # CHECK: 120 3 | # 4 | # RUN: %loadSimplexhc %s --emit-llvm -O 3 | FileCheck %s -check-prefix=IR 5 | # IR: tail call void @printOnlyInt(i64 120) 6 | 7 | 8 | binding factorial = \(a : PrimInt) -> PrimInt { 9 | case a () of 10 | 0 -> 1; 11 | x -> case primSubtract (x 1) of 12 | xmin1 -> case factorial (xmin1) of 13 | factxmin1 -> primMult (x factxmin1);;; 14 | }; 15 | 16 | 17 | binding main = \() -> Boxed { 18 | case factorial (5) of 19 | x -> printInt (x); 20 | }; 21 | -------------------------------------------------------------------------------- /test/compose-simple.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 2 3 | 4 | data IntTy = IntCons (PrimInt); 5 | 6 | binding intcons = \() -> IntTy { IntCons (2) }; 7 | 8 | binding id = \(idxparam: () -> IntTy) -> IntTy { 9 | case idxparam () of 10 | IntCons (x) -> IntCons (x); 11 | }; 12 | 13 | 14 | binding compose = \(f:(Boxed) -> Boxed g:(Boxed) -> Boxed x:(Boxed) -> Boxed) -> Boxed { 15 | let 16 | binding gx = \(g:Boxed x:Boxed) () -> Boxed { g (x) }; 17 | in f (gx) 18 | }; 19 | 20 | binding pullapart = \() -> IntTy { compose (id id intcons) }; 21 | 22 | binding main = \() -> Boxed { 23 | case pullapart () of 24 | IntCons (x) -> printInt (x); 25 | }; 26 | -------------------------------------------------------------------------------- /test/tunnel-through-cases.stg: -------------------------------------------------------------------------------- 1 | # Check that LLVM is smart enough to tunnel throguh cases 2 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 3 | # CHECK: 7 4 | 5 | # RUN: %loadSimplexhc %s --emit-llvm -O 3 | FileCheck %s -check-prefix=IR 6 | # IR: tail call void @printOnlyInt(i64 0) 7 | # 8 | #primSubtract(xmin3 4);;; 9 | 10 | binding sub7 = \(a : PrimInt) -> PrimInt { 11 | case a () of 12 | 0 -> 1; 13 | x -> case primSubtract (x 1) of 14 | xmin1 -> case primSubtract (xmin1 6) of 15 | xmin3 -> xmin3 ();;; 16 | }; 17 | 18 | 19 | binding main = \() -> Boxed { 20 | case sub7 (7) of 21 | x -> printInt (x); 22 | }; 23 | -------------------------------------------------------------------------------- /test/tear-through-simple-recursion.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 1 3 | 4 | # RUN: %loadSimplexhc %s --emit-llvm -O 3 | FileCheck %s -check-prefix=IR 5 | #IR: tail call void @printOnlyInt(i64 1) 6 | 7 | # a complicated way to return 1 for positive inputs. 8 | # here to check that llvm can tear through recursion. Let's see. 9 | # f(x) = f(x - 1) 10 | # f(0) = 1 11 | binding complexreturn1 = \(a : PrimInt) -> PrimInt { 12 | case a () of 13 | 0 -> 1; 14 | x -> case primSubtract (x 1) of 15 | xmin1 -> complexreturn1 (xmin1) ;; 16 | }; 17 | 18 | 19 | binding main = \() -> Boxed { 20 | case complexreturn1 (10) of 21 | x -> printInt (x); 22 | }; 23 | -------------------------------------------------------------------------------- /test/subtract.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: -1 3 | 4 | data Int = IntCons (PrimInt); 5 | 6 | 7 | binding sub = \(int_i: () -> Int, int_j: () -> Int) -> Int { 8 | case int_i () of 9 | IntCons (iprim) -> case int_j () of 10 | IntCons(jprim) -> case primSubtract (iprim jprim) of 11 | r -> IntCons (r);;; 12 | 13 | }; 14 | 15 | binding three = \() -> Int { IntCons (3) }; 16 | binding four = \() -> Int { IntCons (4) }; 17 | 18 | binding three_sub_four = \() -> Int { sub (three, four) }; 19 | 20 | 21 | binding main = \() -> Boxed { 22 | case three_sub_four () of 23 | IntCons (x) -> printInt (x); 24 | }; 25 | -------------------------------------------------------------------------------- /test/multiply.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 12 3 | 4 | data Int = IntCons (PrimInt); 5 | 6 | 7 | binding mult = \(int_i: () -> Int, int_j: () -> Int) -> Int { 8 | case int_i () of 9 | IntCons (iprim) -> case int_j () of 10 | IntCons(jprim) -> case primMultiply (iprim jprim) of 11 | r -> IntCons (r);;; 12 | }; 13 | 14 | binding three = \() -> Int { IntCons (3) }; 15 | binding four = \() -> Int { IntCons (4) }; 16 | 17 | binding three_x_four = \() -> Int { mult (three, four) }; 18 | 19 | binding main = \() -> Boxed { 20 | case three_x_four () of 21 | IntCons (three_x_four_prim) -> printInt (three_x_four_prim); 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | compile_commands.json 3 | build 4 | *out 5 | *ll 6 | *swap 7 | cmake-build-debug 8 | *.generated.* 9 | test/*dSYM*/ 10 | *tags* 11 | 12 | # C++ gitignore 13 | # Prerequisites 14 | *.d 15 | 16 | # Compiled Object files 17 | *.slo 18 | *.lo 19 | *.o 20 | *.obj 21 | 22 | # Precompiled Headers 23 | *.gch 24 | *.pch 25 | 26 | # Compiled Dynamic libraries 27 | *.so 28 | *.dylib 29 | *.dll 30 | 31 | # Fortran module files 32 | *.mod 33 | *.smod 34 | 35 | # Compiled Static libraries 36 | *.lai 37 | *.la 38 | *.a 39 | *.lib 40 | 41 | # Executables 42 | *.exe 43 | *.out 44 | *.app 45 | 46 | 47 | # vim gitignore 48 | # ============ 49 | 50 | # Swap 51 | [._]*.s[a-v][a-z] 52 | [._]*.sw[a-p] 53 | [._]s[a-v][a-z] 54 | [._]sw[a-p] 55 | 56 | # Session 57 | Session.vim 58 | 59 | # Temporary 60 | .netrwhist 61 | *~ 62 | # Auto-generated tag files 63 | tags 64 | 65 | -------------------------------------------------------------------------------- /bench/fibonacci-worker-wrapper-optimised.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 120 3 | 4 | 5 | # a complicated way to return 1 for positive inputs. 6 | # here to check that llvm can tear through recursion. Let's see. 7 | # fib(0) = 0 8 | # fib(1) = 1 9 | # fib(n) = fib(n - 1) + fib(n - 2) 10 | binding fib = \(a : PrimInt) -> PrimInt { 11 | case a () of 12 | 0 -> 0; 13 | 1 -> 1; 14 | x -> case primSubtract (x 1) of 15 | xmin1 -> case fib (xmin1) of 16 | fibxmin1 -> case primSubtract (x 2) of 17 | xmin2 -> case fib (xmin2) of 18 | fibxmin2 -> primAdd (fibxmin1 fibxmin2);;;;; 19 | }; 20 | 21 | 22 | binding main = \() -> Boxed { 23 | case fib (40) of 24 | x -> printInt (x); 25 | }; 26 | -------------------------------------------------------------------------------- /bench/ackerman-worker-wrapper-optimised.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 7 3 | # 4 | # ack :: Int -> Int -> Int 5 | # ack 0 b = b+1 6 | # ack a 0 = ack (a - 1) 1 7 | # ack a b = ack (a - 1) (ack a (b - 1)) 8 | 9 | binding ackerman = \(aint: PrimInt, bint: PrimInt) -> PrimInt { 10 | case aint () of 11 | 0 -> primAdd (bint 1); 12 | aval -> case bint () of 13 | 0 -> case primSubtract (aval 1) of 14 | adec -> ackerman (adec 1);; 15 | bval -> case primSubtract (bval 1) of 16 | bdec -> case primSubtract (aval 1) of 17 | adec -> case ackerman (aval bdec) of 18 | bnew -> ackerman (adec bnew);;;;; 19 | }; 20 | 21 | 22 | binding main = \() -> Boxed { 23 | case ackerman (3 11) of 24 | x -> printInt (x); 25 | }; 26 | -------------------------------------------------------------------------------- /test/compose.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 3 3 | 4 | data Int2Ty = Int2Cons (PrimInt PrimInt); 5 | data IntTy = IntCons (PrimInt); 6 | 7 | binding int_2_cons = \() -> Int2Ty { Int2Cons (2 3) }; 8 | 9 | binding swap = \(x_swap: () -> Int2Ty) -> Int2Ty { 10 | case x_swap () of 11 | Int2Cons (x y) -> Int2Cons (y x); }; 12 | 13 | binding extractIntWrapped = \(x_int_wrapped: () -> Int2Ty) -> IntTy { 14 | case x_int_wrapped () of 15 | Int2Cons (x y) -> IntCons (x); 16 | }; 17 | 18 | binding compose = \(f: (Boxed) -> Boxed g:(Boxed) -> Boxed x:(Boxed) -> Boxed) -> Boxed { 19 | let 20 | binding gx = \(g:Boxed x:Boxed) () -> Boxed { g (x) }; 21 | in f (gx) 22 | }; 23 | 24 | binding pullapart = \() -> IntTy { compose (extractIntWrapped swap int_2_cons) }; 25 | 26 | binding main = \() -> Boxed { 27 | case pullapart () of 28 | IntCons(x) -> printInt (x); 29 | }; 30 | -------------------------------------------------------------------------------- /bench/compile-and-run-program.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o xtrace 3 | set -e 4 | SIMPLEXHC=sxhc 5 | LIBSTGPATH=/home/bollu/work/sxhc/build/ 6 | # LLC=llc 7 | LLVMEXTRACT=/Users/bollu/work/LLVM-all/polly/llvm_build_ninja/bin/llvm-extract 8 | LLC=/home/bollu/build/llvm-5.0/build/bin/llc 9 | OPT=/home/bollu/build/llvm-5.0/build/bin/opt 10 | CC=gcc 11 | 12 | OUTO=$1".out.o" 13 | OUTLL=$1".out.ll" 14 | 15 | $SIMPLEXHC $1 --emit-llvm -o $OUTLL ${@:2} -O 3 16 | $OPT -O3 -instnamer $OUTLL -S -o temp; mv temp $OUTLL 17 | 18 | $LLC $OUTLL -o $OUTO -filetype=obj -O 3 19 | 20 | $CC $OUTO -L $LIBSTGPATH -lstgruntime -o $1.out 21 | rm $OUTO 22 | 23 | echo "----SUCCESSFULLY COMPILED----" 24 | 25 | # If this is at the CWD, then we need to execute it with `./` prepended 26 | # to the path. Otherwise, we can directly execute it. 27 | if [ $(dirname $1) == $(dirname $0) ]; then 28 | # need to figure out how to set STG programs's exit code :) 29 | time ./$1.out || true 30 | else 31 | time $1.out || true 32 | fi; 33 | 34 | exit 0 35 | 36 | -------------------------------------------------------------------------------- /bench/compare-o0-and-o3.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o xtrace 3 | set -e 4 | SIMPLEXHC=sxhc 5 | LIBSTGPATH=/home/bollu/work/sxhc/build/ 6 | # LLC=llc 7 | LLVMEXTRACT=/home/bollu/build/llvm-5.0/build/bin/llvm-extract 8 | LLC=/home/bollu/build/llvm-5.0/build/bin/llc 9 | OPT=/home/bollu/build/llvm-5.0/build/bin/opt 10 | CC=gcc 11 | 12 | OUTO0=$1".out.o0" 13 | OUTO3=$1".out.o3" 14 | OUTO3_FROM_OUTO0=$1".out.o3.from.o0" 15 | 16 | $SIMPLEXHC $1 --emit-llvm -o $OUTO0".ll" -O 0 17 | $LLVMEXTRACT -func=main -S -o $OUTO0".main.ll" $OUTO0".ll" 18 | $OPT -dot-cfg $OUTO0".main.ll" 19 | mv cfg.main.dot $OUTO0".main.dot" 20 | 21 | $OPT -O3 -S $OUTO0".ll" -o $OUTO3_FROM_OUTO0".ll" 22 | $LLVMEXTRACT -func=main -S -o $OUTO3_FROM_OUTO0".main.ll" $OUTO3_FROM_OUTO0".ll" 23 | $OPT -dot-cfg $OUTO3_FROM_OUTO0".main.ll" 24 | mv cfg.main.dot $OUTO3_FROM_OUTO0".main.dot" 25 | 26 | 27 | $SIMPLEXHC $1 --emit-llvm -o $OUTO3".ll" -O 3 28 | $LLVMEXTRACT -func=main -S -o $OUTO3".main.ll" $OUTO3".ll" 29 | $OPT -dot-cfg $OUTO3".main.ll" 30 | mv cfg.main.dot $OUTO3".main.dot" 31 | -------------------------------------------------------------------------------- /test/factorial.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 24 3 | 4 | data Int = IntCons (PrimInt); 5 | 6 | binding mult = \(int_a: () -> Int, int_b: () -> Int) -> Int { 7 | case int_a () of 8 | IntCons (iprim) -> case int_b () of 9 | IntCons(jprim) -> case primMultiply (iprim jprim) of 10 | r -> IntCons (r);;; 11 | }; 12 | 13 | binding sub = \(int_i: () -> Int, int_j: () -> Int) -> Int { 14 | case int_i () of 15 | IntCons (iprim) -> case int_j () of 16 | IntCons(jprim) -> case primSubtract (iprim jprim) of 17 | r -> IntCons (r);;; 18 | 19 | }; 20 | 21 | binding one = \() -> Int { IntCons (1) }; 22 | binding four = \() -> Int { IntCons (4) }; 23 | 24 | binding factorial = \(fint: () -> Int) -> Int { 25 | case fint () of 26 | IntCons (fint_prim) -> case fint_prim () of 27 | 0 -> IntCons (1); 28 | r -> let 29 | binding fint_dec = \(fint: () -> Int) () -> Int { sub (fint one) }; 30 | binding factorial_dec = \(fint_dec: () -> Int) () -> Int { factorial (fint_dec) }; 31 | in mult (fint factorial_dec);; 32 | }; 33 | 34 | 35 | binding factorial_four = \() -> Int { factorial (four) }; 36 | 37 | binding main = \() -> Boxed { 38 | case factorial_four () of 39 | IntCons (x) -> printInt (x); 40 | }; 41 | -------------------------------------------------------------------------------- /test/ackerman.stg: -------------------------------------------------------------------------------- 1 | # RUN: %loadSimplexhc %s --jit | FileCheck %s 2 | # CHECK: 11 3 | # 4 | # ack :: Int -> Int -> Int 5 | # ack 0 n = n+1 6 | # ack m 0 = ack (m - 1) 1 7 | # ack m n = ack (m - 1) (ack m (n - 1)) 8 | 9 | data Int = IntCons (PrimInt); 10 | 11 | binding sub = \(int_i: () -> Int, int_j: () -> Int) -> Int { 12 | case int_i () of 13 | IntCons (iprim) -> case int_j () of 14 | IntCons(jprim) -> case primSubtract (iprim jprim) of 15 | r -> IntCons (r);;; 16 | 17 | }; 18 | 19 | 20 | binding add = \(int_i2: () -> Int, int_j2: () -> Int) -> Int { 21 | case int_i2 () of 22 | IntCons (iprim2) -> case int_j2 () of 23 | IntCons(jprim2) -> case primAdd (iprim2 jprim2) of 24 | r -> IntCons (r);;; 25 | 26 | }; 27 | 28 | binding zero = \() -> Int { IntCons (0) }; 29 | binding one = \() -> Int { IntCons (1) }; 30 | binding two = \() -> Int { IntCons (2) }; 31 | binding three = \() -> Int { IntCons (3) }; 32 | binding four = \() -> Int { IntCons (4) }; 33 | binding five = \() -> Int { IntCons (5) }; 34 | binding eleven = \() -> Int { IntCons (11) }; 35 | 36 | binding ackerman = \(aint: () -> Int, bint: () -> Int) -> Int { 37 | case aint () of 38 | IntCons (aprim) -> case aprim () of 39 | 0 -> add (bint one); 40 | aval -> case bint () of 41 | IntCons (bprim) -> case bprim () of 42 | 0 -> let 43 | binding adec = \(aint: () -> Int) () -> Int { sub (aint one) }; 44 | in ackerman (adec one); 45 | bval -> let 46 | binding bdec = \(bint: () -> Int) () -> Int { sub (bint one) }; 47 | binding adec = \(aint: () -> Int) () -> Int { sub (aint one) }; 48 | binding bnew = \(aint: () -> Int, bdec: Boxed) () -> Int { ackerman (aint bdec) }; 49 | in ackerman (adec bnew);;;; 50 | }; 51 | 52 | 53 | binding main = \() -> Boxed { 54 | case ackerman (two four) of 55 | IntCons (x) -> printInt (x); 56 | }; 57 | -------------------------------------------------------------------------------- /bench/lit.cfg: -------------------------------------------------------------------------------- 1 | # -*clang- Python -*- 2 | 3 | import os 4 | import platform 5 | import re 6 | import subprocess 7 | 8 | import lit.formats 9 | import lit.util 10 | 11 | # Configuration file for the 'lit' test runner. 12 | 13 | # name: The name of this test suite. 14 | config.name = 'simplechc' 15 | 16 | # testFormat: The test format to use to interpret tests. 17 | # 18 | # For now we require '&&' between commands, until they get globally killed and 19 | # the test runner updated. 20 | execute_external = platform.system() != 'Windows' 21 | config.test_format = lit.formats.ShTest(execute_external) 22 | 23 | # suffixes: A list of file extensions to treat as test files. 24 | config.suffixes = ['.stg'] 25 | 26 | # test_source_root: The root path where tests are located. 27 | config.test_source_root = os.path.dirname(__file__) 28 | 29 | # test_exec_root: The root path where tests should be run. 30 | config.test_exec_root = os.path.dirname(__file__) 31 | 32 | # Tweak the PATH to include the tools dir and the scripts dir. 33 | base_paths = [config.environment['PATH']] 34 | path = os.path.pathsep.join(base_paths) 35 | config.environment['PATH'] = path 36 | 37 | path = config.environment.get('LD_LIBRARY_PATH','') 38 | config.environment['LD_LIBRARY_PATH'] = path 39 | 40 | # opt knows whether it is compiled with -DNDEBUG. 41 | import subprocess 42 | # try: 43 | # opt_cmd = subprocess.Popen([os.path.join(config.llvm_tools_dir, 'opt'), '-version'], 44 | # stdout = subprocess.PIPE, 45 | # env=config.environment) 46 | # except OSError: 47 | # print("Could not find opt in " + config.llvm_tools_dir) 48 | # exit(42) 49 | # 50 | # if re.search(r'with assertions', opt_cmd.stdout.read().decode('ascii')): 51 | # config.available_features.add('asserts') 52 | # opt_cmd.wait() 53 | 54 | # try: 55 | # llvm_config_cmd = subprocess.Popen([os.path.join( 56 | # config.llvm_tools_dir, 57 | # 'llvm-config'), 58 | # '--targets-built'], 59 | # stdout = subprocess.PIPE, 60 | # env=config.environment) 61 | # except OSError: 62 | # print("Could not find llvm-config in " + config.llvm_tools_dir) 63 | # exit(42) 64 | # 65 | # if re.search(r'NVPTX', llvm_config_cmd.stdout.read().decode('ascii')): 66 | # config.available_features.add('nvptx-registered-target') 67 | # llvm_config_cmd.wait() 68 | 69 | # HACK: load position is HACKED INTO THE BUILD SCRIPT, PLEASE FIX THIS. 70 | # config.substitutions.append("%loadSimplexhc", "../build/shxc") 71 | config.substitutions.append(("%loadSimplexhc", "sxhc")) 72 | -------------------------------------------------------------------------------- /test/lit.cfg: -------------------------------------------------------------------------------- 1 | # -*clang- Python -*- 2 | 3 | import os 4 | import platform 5 | import re 6 | import subprocess 7 | 8 | import lit.formats 9 | import lit.util 10 | 11 | # Configuration file for the 'lit' test runner. 12 | 13 | # name: The name of this test suite. 14 | config.name = 'simplechc' 15 | 16 | # testFormat: The test format to use to interpret tests. 17 | # 18 | # For now we require '&&' between commands, until they get globally killed and 19 | # the test runner updated. 20 | execute_external = platform.system() != 'Windows' 21 | config.test_format = lit.formats.ShTest(execute_external) 22 | 23 | # suffixes: A list of file extensions to treat as test files. 24 | config.suffixes = ['.stg'] 25 | 26 | # test_source_root: The root path where tests are located. 27 | config.test_source_root = os.path.dirname(__file__) 28 | 29 | # test_exec_root: The root path where tests should be run. 30 | config.test_exec_root = os.path.dirname(__file__) 31 | 32 | # Tweak the PATH to include the tools dir and the scripts dir. 33 | base_paths = [config.environment['PATH']] 34 | path = os.path.pathsep.join(base_paths) 35 | config.environment['PATH'] = path 36 | 37 | path = config.environment.get('LD_LIBRARY_PATH','') 38 | config.environment['LD_LIBRARY_PATH'] = path 39 | 40 | # opt knows whether it is compiled with -DNDEBUG. 41 | import subprocess 42 | # try: 43 | # opt_cmd = subprocess.Popen([os.path.join(config.llvm_tools_dir, 'opt'), '-version'], 44 | # stdout = subprocess.PIPE, 45 | # env=config.environment) 46 | # except OSError: 47 | # print("Could not find opt in " + config.llvm_tools_dir) 48 | # exit(42) 49 | # 50 | # if re.search(r'with assertions', opt_cmd.stdout.read().decode('ascii')): 51 | # config.available_features.add('asserts') 52 | # opt_cmd.wait() 53 | 54 | # try: 55 | # llvm_config_cmd = subprocess.Popen([os.path.join( 56 | # config.llvm_tools_dir, 57 | # 'llvm-config'), 58 | # '--targets-built'], 59 | # stdout = subprocess.PIPE, 60 | # env=config.environment) 61 | # except OSError: 62 | # print("Could not find llvm-config in " + config.llvm_tools_dir) 63 | # exit(42) 64 | # 65 | # if re.search(r'NVPTX', llvm_config_cmd.stdout.read().decode('ascii')): 66 | # config.available_features.add('nvptx-registered-target') 67 | # llvm_config_cmd.wait() 68 | 69 | # HACK: load position is HACKED INTO THE BUILD SCRIPT, PLEASE FIX THIS. 70 | # config.substitutions.append("%loadSimplexhc", "../build/shxc -O 3") 71 | config.substitutions.append(("%loadSimplexhc", "sxhc")) 72 | -------------------------------------------------------------------------------- /src/jit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "llvm/ADT/StringRef.h" 7 | #include "llvm/ExecutionEngine/JITSymbol.h" 8 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 9 | #include "llvm/ExecutionEngine/Orc/Core.h" 10 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 11 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 12 | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 13 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 14 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 15 | #include "llvm/IR/DataLayout.h" 16 | #include "llvm/IR/LLVMContext.h" 17 | #include 18 | #include "llvm/IR/DataLayout.h" 19 | #include "llvm/IR/Mangler.h" 20 | #include "llvm/Support/DynamicLibrary.h" 21 | #include "llvm/Support/Error.h" 22 | #include "llvm/Support/raw_ostream.h" 23 | #include "llvm/Target/TargetMachine.h" 24 | #include "sxhc/libstg.h" 25 | 26 | // http://releases.llvm.org/8.0.0/docs/tutorial/BuildingAJIT1.html 27 | 28 | using namespace llvm; 29 | using namespace orc; 30 | using namespace llvm::orc; 31 | 32 | class SimpleJIT { 33 | private: 34 | ExecutionSession ES; 35 | RTDyldObjectLinkingLayer ObjectLayer; 36 | IRCompileLayer CompileLayer; 37 | 38 | DataLayout DL; 39 | MangleAndInterner Mangle; 40 | ThreadSafeContext Ctx; 41 | 42 | public: 43 | SimpleJIT(JITTargetMachineBuilder JTMB, DataLayout DL) 44 | : ObjectLayer(ES, 45 | []() { return llvm::make_unique(); }), 46 | CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))), 47 | DL(std::move(DL)), Mangle(ES, this->DL), 48 | Ctx(llvm::make_unique()) { 49 | ES.getMainJITDylib().setGenerator( 50 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); 51 | } 52 | 53 | static Expected> Create() { 54 | auto JTMB = JITTargetMachineBuilder::detectHost(); 55 | 56 | if (!JTMB) 57 | return JTMB.takeError(); 58 | 59 | auto DL = JTMB->getDefaultDataLayoutForTarget(); 60 | if (!DL) 61 | return DL.takeError(); 62 | 63 | return llvm::make_unique(std::move(*JTMB), std::move(*DL)); 64 | } 65 | 66 | const DataLayout &getDataLayout() const { return DL; } 67 | 68 | LLVMContext &getContext() { return *Ctx.getContext(); } 69 | 70 | Error addModule(std::unique_ptr M) { 71 | auto &MainDL = ES.getMainJITDylib(); 72 | return CompileLayer.add(MainDL, 73 | ThreadSafeModule(std::move(M), Ctx)); 74 | } 75 | 76 | Expected lookup(StringRef Name) { 77 | return ES.lookup({&ES.getMainJITDylib()}, Mangle(Name.str())); 78 | } 79 | }; 80 | 81 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.3) 2 | project(sxhc C CXX) 3 | 4 | # Colors when compiling with clang 5 | # set(CMAKE_CXX_FLAGS "-fdiagnostics-color=always -fno-rtti -g -Wall -Werror") 6 | set(CMAKE_CXX_FLAGS "-fdiagnostics-color=always -fno-rtti -g -Wall") 7 | 8 | # LLVM 9 | find_package(LLVM REQUIRED) 10 | 11 | # Boost 12 | find_package(Boost REQUIRED) 13 | # HACK: I'm using all. I need to ask on the mailing list how to fix this. 14 | 15 | llvm_map_components_to_libnames(LLVM_LIBS Target 16 | Core Support ORCJIT MCJIT ExecutionEngine 17 | ${LLVM_TARGETS_TO_BUILD} 18 | ASMPrinter 19 | CodeGen 20 | AllTargetsAsmParsers 21 | AllTargetsAsmPrinters 22 | AllTargetsDescs 23 | AllTargetsInfos TransformUtils Object 24 | ipo Passes Analysis 25 | nativecodegen MC) 26 | 27 | #llvm_map_components_to_libnames(LLVM_LIBS AllTargetsAsmPrinters 28 | # AllTargetsAsmParsers Core Support ORCJIT MCJIT ExecutionEngine 29 | # ASMPrinter CodeGen AllTargetsDescs AllTargetsInfos TransformUtils Object 30 | # ipo Passes Analysis 31 | # nativecodegen MC) 32 | 33 | # FLEX AND BISON 34 | find_package(BISON) 35 | find_package(FLEX) 36 | bison_target(Parser src/parser.yy ${CMAKE_SOURCE_DIR}/src/parser.generated.cpp COMPILE_FLAGS -v) 37 | flex_target(Lexer src/lexer.l ${CMAKE_SOURCE_DIR}/src/lexer.generated.cpp) 38 | add_flex_bison_dependency(Lexer Parser) 39 | 40 | 41 | # INCLUDES AND SOURCES 42 | include_directories(include ${LLVM_INCLUDE_DIRS} include/sxhc) 43 | 44 | add_executable(sxhc 45 | src/main.cpp 46 | src/RuntimeDebugBuilder.cpp 47 | src/stgir.cpp 48 | src/StackAnalysis.cpp 49 | ${BISON_Parser_OUTPUTS} 50 | ${FLEX_Lexer_OUTPUTS} 51 | ) 52 | 53 | target_link_libraries(sxhc ${LLVM_LIBS} -lstdc++ stgruntime) 54 | set_target_properties(sxhc PROPERTIES CXX_STANDARD 14) 55 | 56 | # CLANG FORMAT 57 | file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h) 58 | 59 | find_program(CLANG_FORMAT clang-format) 60 | add_custom_target( 61 | format 62 | COMMAND ${CLANG_FORMAT} 63 | -style=LLVM 64 | -i 65 | ${ALL_SOURCE_FILES} 66 | ) 67 | 68 | # libstg 69 | add_library(stgruntime 70 | SHARED lib/libstg.c) 71 | set_target_properties(stgruntime PROPERTIES C_STANDARD 99) 72 | 73 | # DOXYGEN 74 | # first we can indicate the documentation build as an option and set it to ON by default 75 | option(BUILD_DOC "Build documentation" OFF) 76 | 77 | # check if Doxygen is installed 78 | find_package(Doxygen) 79 | if (DOXYGEN_FOUND) 80 | # set input and output files 81 | set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in) 82 | set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 83 | 84 | # request to configure the file 85 | configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) 86 | message("Doxygen build started") 87 | 88 | # note the option ALL which allows to build the docs together with the application 89 | add_custom_target(doc 90 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} 91 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 92 | COMMENT "Generating API documentation with Doxygen" 93 | VERBATIM ) 94 | else (DOXYGEN_FOUND) 95 | message("Doxygen need to be installed to generate the doxygen documentation") 96 | endif (DOXYGEN_FOUND) 97 | 98 | -------------------------------------------------------------------------------- /bench/lit.site.cfg.in: -------------------------------------------------------------------------------- 1 | ## Autogenerated by LLVM/Polly configuration. 2 | # Do not edit! 3 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 4 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 5 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 6 | config.llvm_libs_dir = "@LLVM_LIBS_DIR@" 7 | config.polly_obj_root = "@POLLY_BINARY_DIR@" 8 | config.polly_lib_dir = "@POLLY_LIB_DIR@" 9 | config.target_triple = "@TARGET_TRIPLE@" 10 | config.enable_gpgpu_codegen = "@GPU_CODEGEN@" 11 | config.link_polly_into_tools = "@LINK_POLLY_INTO_TOOLS@" 12 | config.targets_to_build = "@TARGETS_TO_BUILD@" 13 | config.extra_paths = "@POLLY_TEST_EXTRA_PATHS@".split(";") 14 | 15 | ## Check the current platform with regex 16 | import re 17 | EAT_ERR_ON_X86 = ' ' 18 | if (re.match(r'^x86_64*', '@TARGET_TRIPLE@') == None) : 19 | EAT_ERR_ON_X86 = '|| echo \"error is eaten\"' 20 | 21 | for arch in config.targets_to_build.split(): 22 | config.available_features.add(arch.lower() + '-registered-target') 23 | 24 | # Support substitution of the tools and libs dirs with user parameters. This is 25 | # used when we can't determine the tool dir at configuration time. 26 | try: 27 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 28 | config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params 29 | except KeyError: 30 | e = sys.exc_info()[1] 31 | key, = e.args 32 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 33 | 34 | # excludes: A list of directories to exclude from the testsuite. The 'Inputs' 35 | # subdirectories contain auxiliary inputs for various tests in their parent 36 | # directories. 37 | config.excludes = ['Inputs'] 38 | 39 | if config.link_polly_into_tools == '' or \ 40 | config.link_polly_into_tools.lower() == '0' or \ 41 | config.link_polly_into_tools.lower() == 'n' or \ 42 | config.link_polly_into_tools.lower() == 'no' or \ 43 | config.link_polly_into_tools.lower() == 'off' or \ 44 | config.link_polly_into_tools.lower() == 'false' or \ 45 | config.link_polly_into_tools.lower() == 'notfound' or \ 46 | config.link_polly_into_tools.lower() == 'link_polly_into_tools-notfound': 47 | config.substitutions.append(('%loadPolly', '-load ' 48 | + config.polly_lib_dir + '/LLVMPolly@LLVM_SHLIBEXT@' 49 | + ' -polly-process-unprofitable ' 50 | + ' -polly-remarks-minimal ' 51 | + ' -polly-use-llvm-names ' 52 | + ' -polly-import-jscop-dir=%S ' 53 | + ' -polly-codegen-verify ' 54 | )) 55 | else: 56 | config.substitutions.append(('%loadPolly', '' 57 | + ' -polly-process-unprofitable ' 58 | + ' -polly-remarks-minimal ' 59 | + ' -polly-use-llvm-names ' 60 | + ' -polly-import-jscop-dir=%S ' 61 | + ' -polly-codegen-verify ' 62 | )) 63 | 64 | if config.enable_gpgpu_codegen == 'TRUE' : 65 | config.available_features.add('pollyacc') 66 | 67 | # Let the main config do the real work. 68 | lit_config.load_config(config, "@POLLY_SOURCE_DIR@/test/lit.cfg") 69 | -------------------------------------------------------------------------------- /test/lit.site.cfg.in: -------------------------------------------------------------------------------- 1 | ## Autogenerated by LLVM/Polly configuration. 2 | # Do not edit! 3 | config.llvm_src_root = "@LLVM_SOURCE_DIR@" 4 | config.llvm_obj_root = "@LLVM_BINARY_DIR@" 5 | config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" 6 | config.llvm_libs_dir = "@LLVM_LIBS_DIR@" 7 | config.polly_obj_root = "@POLLY_BINARY_DIR@" 8 | config.polly_lib_dir = "@POLLY_LIB_DIR@" 9 | config.target_triple = "@TARGET_TRIPLE@" 10 | config.enable_gpgpu_codegen = "@GPU_CODEGEN@" 11 | config.link_polly_into_tools = "@LINK_POLLY_INTO_TOOLS@" 12 | config.targets_to_build = "@TARGETS_TO_BUILD@" 13 | config.extra_paths = "@POLLY_TEST_EXTRA_PATHS@".split(";") 14 | 15 | ## Check the current platform with regex 16 | import re 17 | EAT_ERR_ON_X86 = ' ' 18 | if (re.match(r'^x86_64*', '@TARGET_TRIPLE@') == None) : 19 | EAT_ERR_ON_X86 = '|| echo \"error is eaten\"' 20 | 21 | for arch in config.targets_to_build.split(): 22 | config.available_features.add(arch.lower() + '-registered-target') 23 | 24 | # Support substitution of the tools and libs dirs with user parameters. This is 25 | # used when we can't determine the tool dir at configuration time. 26 | try: 27 | config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params 28 | config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params 29 | except KeyError: 30 | e = sys.exc_info()[1] 31 | key, = e.args 32 | lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) 33 | 34 | # excludes: A list of directories to exclude from the testsuite. The 'Inputs' 35 | # subdirectories contain auxiliary inputs for various tests in their parent 36 | # directories. 37 | config.excludes = ['Inputs'] 38 | 39 | if config.link_polly_into_tools == '' or \ 40 | config.link_polly_into_tools.lower() == '0' or \ 41 | config.link_polly_into_tools.lower() == 'n' or \ 42 | config.link_polly_into_tools.lower() == 'no' or \ 43 | config.link_polly_into_tools.lower() == 'off' or \ 44 | config.link_polly_into_tools.lower() == 'false' or \ 45 | config.link_polly_into_tools.lower() == 'notfound' or \ 46 | config.link_polly_into_tools.lower() == 'link_polly_into_tools-notfound': 47 | config.substitutions.append(('%loadPolly', '-load ' 48 | + config.polly_lib_dir + '/LLVMPolly@LLVM_SHLIBEXT@' 49 | + ' -polly-process-unprofitable ' 50 | + ' -polly-remarks-minimal ' 51 | + ' -polly-use-llvm-names ' 52 | + ' -polly-import-jscop-dir=%S ' 53 | + ' -polly-codegen-verify ' 54 | )) 55 | else: 56 | config.substitutions.append(('%loadPolly', '' 57 | + ' -polly-process-unprofitable ' 58 | + ' -polly-remarks-minimal ' 59 | + ' -polly-use-llvm-names ' 60 | + ' -polly-import-jscop-dir=%S ' 61 | + ' -polly-codegen-verify ' 62 | )) 63 | 64 | if config.enable_gpgpu_codegen == 'TRUE' : 65 | config.available_features.add('pollyacc') 66 | 67 | # Let the main config do the real work. 68 | lit_config.load_config(config, "@POLLY_SOURCE_DIR@/test/lit.cfg") 69 | -------------------------------------------------------------------------------- /include/sxhc/StackAnalysis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "llvm/ADT/APFloat.h" 6 | #include "llvm/ADT/STLExtras.h" 7 | #include "llvm/ADT/SmallVector.h" 8 | #include "llvm/Bitcode/BitcodeWriter.h" 9 | #include "llvm/IR/BasicBlock.h" 10 | #include "llvm/IR/Constants.h" 11 | #include "llvm/IR/DerivedTypes.h" 12 | #include "llvm/IR/Function.h" 13 | #include "llvm/IR/IRBuilder.h" 14 | #include "llvm/IR/Instructions.h" 15 | #include "llvm/IR/LLVMContext.h" 16 | #include "llvm/IR/LegacyPassManager.h" 17 | #include "llvm/IR/Module.h" 18 | #include "llvm/IR/Type.h" 19 | #include "llvm/IR/Verifier.h" 20 | #include "llvm/Passes/PassBuilder.h" 21 | #include "llvm/Support/CommandLine.h" 22 | #include "llvm/Support/FileSystem.h" 23 | #include "llvm/Target/TargetMachine.h" 24 | #include "llvm/Target/TargetOptions.h" 25 | #include "llvm/Transforms/IPO/AlwaysInliner.h" 26 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 27 | #include "llvm/Transforms/Utils/Cloning.h" 28 | #include "llvm/Transforms/Utils/ModuleUtils.h" 29 | #include "sxhc/types.h" 30 | 31 | class StackInstruction { 32 | public: 33 | enum StackInstructionType { SIP_Push, SIP_Pop }; 34 | 35 | 36 | // llvm::CallInst *getCall() const { return CI; } 37 | 38 | bool isPush() const { return type == SIP_Push; } 39 | bool isPop() const { return type == SIP_Pop; } 40 | 41 | llvm::StringRef getName() const { 42 | return CI->getName(); 43 | } 44 | 45 | llvm::Value *getPushedVal() { 46 | assert(isPush()); 47 | return CI->getArgOperand(0); 48 | } 49 | 50 | llvm::Type *getType() { 51 | return CI->getType(); 52 | } 53 | 54 | llvm::CallInst *getInstruction() { return CI; } 55 | 56 | static StackInstruction createPush(llvm::CallInst *CI) { 57 | return StackInstruction(SIP_Push, CI); 58 | } 59 | 60 | static StackInstruction createPop(llvm::CallInst *CI) { 61 | return StackInstruction(SIP_Pop, CI); 62 | } 63 | 64 | private: 65 | StackInstructionType type; 66 | // StackInstructionType getType() const { return type; } 67 | llvm::CallInst *CI; 68 | 69 | StackInstruction(StackInstructionType type, llvm::CallInst *CI) 70 | : type(type), CI(CI){}; 71 | }; 72 | 73 | using StackBB = std::vector; 74 | using StackAnalysis = std::map; 75 | 76 | using namespace llvm; 77 | template 78 | // class StackAnalysisPass : public llvm::AnalysisInfoMixin> { 79 | class StackAnalysisPass : public llvm::AnalysisInfoMixin> { 80 | public: 81 | static llvm::AnalysisKey Key; 82 | // static AnalysisKey *ID() { return &Key; } 83 | 84 | using Result = StackAnalysis; 85 | StackAnalysisPass() {}; 86 | static llvm::StringRef name(){ return "StackAnalysis"; }; 87 | 88 | 89 | StackAnalysis run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) { 90 | StackAnalysis SA; 91 | for (llvm::BasicBlock &BB : F) { 92 | SA[&BB] = makeAnalaysis(&BB); 93 | } 94 | return SA; 95 | }; 96 | 97 | private: 98 | friend llvm::AnalysisInfoMixin>; 99 | 100 | 101 | StackBB makeAnalaysis(llvm::BasicBlock *BB) { 102 | StackBB stackbb; 103 | 104 | for (llvm::Instruction &I : *BB) { 105 | llvm::CallInst *CI = dyn_cast(&I); 106 | 107 | if (CI == nullptr) continue; 108 | if (!CI->getCalledFunction()) continue; 109 | 110 | const std::string funcname = CI->getCalledFunction()->getName(); 111 | 112 | if (funcname == std::string("push") + std::string(stackName)) { 113 | stackbb.push_back(StackInstruction::createPush(CI)); 114 | } else if (funcname == std::string("pop") + std::string(stackName)) { 115 | stackbb.push_back(StackInstruction::createPop(CI)); 116 | } 117 | } 118 | return stackbb; 119 | } 120 | }; 121 | 122 | template 123 | llvm::AnalysisKey StackAnalysisPass::Key; 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simplexhc 2 | --------- 3 | 4 | Simplexhc is a compiler for a lazy functional programming language 5 | similar to [`STG (Spineless, Tagless, G-machine`](https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/GeneratedCode), which is an intermediate representation used by the 6 | Glasgow Haskell compiler (hence "hc" = "haskell compiler"). 7 | `simplex` so it is vaguely related to polyhedral compilation, and because 8 | simplicies are cool (as are simplicial complexes). 9 | 10 | ## Aims 11 | The aim of this project is to have a fully functioning backend for GHC that 12 | takes `STG` straight to `LLVM`. 13 | 14 | Bonus point if I can steal ideas from polyhedral compilation. I'd like to model 15 | the sum & product types in `STG` as spaces, perhaps modeled with integer 16 | polyhedra, so I can reuse the machinery of [`isl`](http://isl.gforge.inria.fr/). 17 | This doesn't really work, since `isl` needs affine maps, and I don't think 18 | there's a reasonable interpretation of "affine" that works to analyse 19 | parallelism for lazy languages. 20 | 21 | Some of the ideas related to this moonshot project are 22 | [written down in my repo: `bollu/dependence-analysis-hask`](https://github.com/bollu/dependence-analysis-hask) 23 | 24 | # Progress 25 | ## Minimum viable 26 | - [x] Function application (only bound parameters). 27 | - [x] Function application (free parameters). 28 | - [x] Constructors. 29 | - [x] Primitive Case (over `Int#`). 30 | - [x] Boxed Case (over regular types). 31 | - [x] Free variables in Case 32 | - [x] Let (non-recursive). 33 | - [x] Letrec. 34 | - [ ] Threads 35 | - [ ] Garbage collection. 36 | 37 | ## Completeness 38 | - [ ] Black holes 39 | - [ ] Basic IO (Right now, only `printInt` exists). 40 | - [ ] ST(?). 41 | - [ ] SMT. 42 | - [ ] Add `double` and `float` types. 43 | - [ ] GHC `Core` plugin to dump out `simplexhc` style STG. 44 | 45 | 46 | 47 | 48 | # Relationship to the other `simplechc` repo. 49 | 50 | This is a rewrite of [`simplexhc`](https://github.com/bollu/simplexhc) 51 | in C++, simply because I am much more comfortable doing compiler related 52 | things in C++. 53 | 54 | 55 | `simplexhc` was written in Haskell. However, as time progressed, I found that 56 | Haskell was an impediment to my joy coding (in this project, I usually love 57 | the language). Perhaps I don't really know how to "think functional" for 58 | mostly mutable problems: graphs, state-based code generation, etc. 59 | 60 | 61 | I don't believe this is a shortcoming of Haskell. Rather, this is a sign 62 | of my immaturity when it comes to Haskell design patterns. 63 | 64 | 65 | To fix holes in my knowledge, I have been reading through the 66 | [Futhark source code](https://github.com/diku-dk/futhark), which is an 67 | optimising compiler written in Haskell. It is extremely well written, and I 68 | wish I could design Haskell programs as well as they can. 69 | 70 | 71 | Either way, as soon as I switched to C++, I felt much happier with coding this 72 | in my hobby time. I've been procrastinating on `simplexhc` due to my fear of 73 | returning to the codebase with spaghetti code 74 | [such as this](https://github.com/bollu/simplexhc/blob/master/src/StgToIR.hs#L259). 75 | 76 | 77 | Perhaps it is just me, but I find that code very unclear to read. I do not 78 | know how to convey intent with Haskell. I would love for pointers to other 79 | haskell compilers that are cleanly designed, so I can learn. 80 | 81 | # Current optimisations planned 82 | - Mark `@alloc` as pure so that the optimiser can safely remove them. 83 | - Understand what screws up when I allow my stack simulator pass to run on BBs with unique predecessors. 84 | 85 | # Autocompletion / tooling 86 | I use vim with `YouCompleteMe`. To enable this, setup `YouCompleteMe` with 87 | the clang plugin. To generate autocomplete information, invoke: 88 | 89 | ``` 90 | $ cmake /path/to/simplexhc-cpp -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 91 | $ cp compile_commands.json /path/to/simplexhc/cpp 92 | ``` 93 | 94 | The `compile_commands.json` file contains autocomplete information. 95 | 96 | # References 97 | - [llvm 8.0 documentation](http://releases.llvm.org/8.0.0/docs/index.html) 98 | - [Implementing functional languages on stock hardware: the spineless, tagless, G machine](https://www.dcc.fc.up.pt/~pbv/aulas/linguagens/peytonjones92implementing.pdf) 99 | - [Making a fast curry, Push/Enter versus Eval/Apply](http://www.cs.tufts.edu/~nr/cs257/archive/simon-peyton-jones/eval-apply-jfp.pdf) 100 | - [GRIN - Whole program optimisation for lazy functional languages](http://web.archive.org/web/20080506021638/http://www.cs.chalmers.se:80/~boquist/phd/index.html) 101 | - [FLRC: intel haskell research compiler](https://github.com/IntelLabs/flrc) 102 | 103 | -------------------------------------------------------------------------------- /src/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #pragma clang diagnostic ignored "-Wdeprecated-register" 3 | #pragma clang diagnostic ignored "-Wunused-function" 4 | #pragma clang diagnostic ignored "-Wunneeded-internal-declaration" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "sxhc/stgir.h" 10 | #include "llvm/Support/CommandLine.h" 11 | #include "cxxopts.hpp" 12 | #include 13 | 14 | static const char *VERSION_STRING = "0.1.0"; 15 | 16 | using namespace stg; 17 | 18 | // Keep this at the end so it can see all the includes made here. 19 | #include "parser.generated.hpp" 20 | 21 | 22 | using namespace std; 23 | //extern "C" int yylex(); 24 | //extern "C" int yyparse(); 25 | 26 | 27 | int yylex(); 28 | int yyparse(); 29 | 30 | extern stg::Program *g_program; 31 | int g_lexer_line_num = 1; 32 | int g_lexer_success = 1; 33 | 34 | int compile_program(stg::Program *p, cxxopts::Options &options); 35 | 36 | llvm::cl::opt OPTION_INPUT_FILENAME(llvm::cl::Positional, llvm::cl::desc(""), llvm::cl::Required); 37 | %} 38 | 39 | 40 | %% 41 | "=" return ASSIGN; 42 | "(" return OPENPAREN; 43 | ")" return CLOSEPAREN; 44 | "{" return OPENFLOWER; 45 | "}" return CLOSEFLOWER; 46 | "of" return OF; 47 | "case" return CASE; 48 | ";" return SEMICOLON; 49 | ":" return COLON; 50 | "->" return THINARROW; 51 | "|" return PIPE; 52 | "\\" return LAMBDA; 53 | "binding" return BINDING; 54 | "data" return DATA; 55 | "let" return LET; 56 | "in" return IN; 57 | "default" return DEFAULT; 58 | 59 | [ \t] ; 60 | 61 | [0-9]+ { 62 | yylval.atom= new AtomInt(atoi(yytext)); 63 | return ATOMINT; 64 | } 65 | [a-z_][a-zA-Z0-9_]* { 66 | yylval.atom = new AtomIdent(yytext); 67 | return ATOMSTRING; 68 | } 69 | [A-Z][a-zA-Z0-9_]* { 70 | yylval.constructorName = new std::string(yytext); 71 | return CONSTRUCTORNAME; 72 | } 73 | 74 | #.* { /* DO NOTHING */ } 75 | \n { ++g_lexer_line_num; } 76 | %% 77 | 78 | int yywrap(void) { 79 | return 1; 80 | } 81 | 82 | void printHelp(cxxopts::Options &options) { 83 | if (options.count("help")) 84 | { 85 | std::cout << options.help({"", "Group"}) << std::endl; 86 | exit(0); 87 | } 88 | } 89 | 90 | int main(int argc, char **argv) { 91 | cxxopts::Options options(argv[0], " - command line options."); 92 | options.positional_help("[optional args]"); 93 | options.add_options() 94 | ("i,input-filepath", "Input file path.", cxxopts::value()) 95 | ("emit-llvm", "Emit LLVM IR instead of assembly.") 96 | ("emit-asm", "Emit assembly.") 97 | ("jit", "JIT compiles and runs the input file.") 98 | ("o,output-filepath", "Output file path.", cxxopts::value()->default_value("-")) 99 | ("O,optlevel", "Optimization level", cxxopts::value()) 100 | ("version", "version information.") 101 | ("help", "print help.") 102 | ("positional-container", "Continer for positional args.", cxxopts::value>()) 103 | ; 104 | options.parse_positional(std::vector({"input-filepath", "positional-container"})); 105 | options.parse(argc, argv); 106 | 107 | 108 | if (options.count("help")) { 109 | printHelp(options); 110 | return 0; 111 | 112 | }; 113 | 114 | if(options.count("emit-llvm") && options.count("emit-asm")) { 115 | std::cerr << "cannot emit both assembly and LLVM at the same time"; 116 | printHelp(options); 117 | return 1; 118 | } 119 | 120 | if (options.count("version")) { 121 | printf("%s\n", VERSION_STRING); 122 | return 0; 123 | } 124 | 125 | if(!options.count("input-filepath")){ 126 | std::cerr << "input filename not given!"; 127 | printHelp(options); 128 | return 1; 129 | } 130 | 131 | std::string OPTION_INPUT_FILENAME = options["input-filepath"].as(); 132 | 133 | yyin = fopen(OPTION_INPUT_FILENAME.c_str(), "r"); 134 | if (yyin == NULL) { 135 | printf("unable to open input file: %s\n", OPTION_INPUT_FILENAME.c_str()); 136 | perror("file open error: "); 137 | return 1; 138 | } 139 | 140 | do { 141 | yyparse(); 142 | } while (!feof(yyin)); 143 | if (g_lexer_success) { 144 | return compile_program(g_program, options); 145 | } else { 146 | return 1; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/stgir.cpp: -------------------------------------------------------------------------------- 1 | #include "sxhc/stgir.h" 2 | 3 | using namespace stg; 4 | 5 | template 6 | void printSepBy(std::ostream &os, std::string prefix, ArrayRef ts, std::string separator, std::string postfix) { 7 | os << prefix; 8 | 9 | for(unsigned i = 0; i < ts.size(); i++) { 10 | os << *ts[i]; 11 | if (i + 2 <= ts.size()) { 12 | os << separator; 13 | } 14 | 15 | } 16 | os << postfix; 17 | } 18 | 19 | template 20 | void printSepBy(std::ostream &os, std::string prefix, ArrayRef ts, std::string separator, std::string postfix) { 21 | os << prefix; 22 | 23 | for(unsigned i = 0; i < ts.size(); i++) { 24 | os << ts[i]; 25 | if (i + 2 <= ts.size()) { 26 | os << separator; 27 | } 28 | 29 | } 30 | os << postfix; 31 | } 32 | // DataConstructor 33 | void DataConstructor::print(std::ostream &os) const { 34 | os << name; 35 | printSepBy(os, "(", ArrayRef(types), " ", ")"); 36 | }; 37 | 38 | std::ostream &stg::operator <<(std::ostream &os, const DataConstructor &decl) { 39 | decl.print(os); 40 | return os; 41 | } 42 | 43 | // DataType 44 | void DataType::print(std::ostream &os) const { 45 | os << "data "; 46 | os << name; 47 | os << " = "; 48 | printSepBy(os, "", ArrayRef(constructors), "|", ""); 49 | }; 50 | 51 | std::ostream &stg::operator <<(std::ostream &os, const DataType &decl) { 52 | decl.print(os); 53 | return os; 54 | } 55 | 56 | 57 | // Atom 58 | std::ostream &stg::operator<<(std::ostream &os, const Atom &a) { 59 | a.print(os); 60 | return os; 61 | } 62 | 63 | 64 | void AtomInt::print(std::ostream &os) const { 65 | os << "atom-" << val; 66 | }; 67 | 68 | void AtomIdent::print(std::ostream &os) const { 69 | os << "atom-" << ident; 70 | }; 71 | 72 | //Parameter 73 | std::ostream &stg::operator<<(std::ostream &os, const Parameter &p) { 74 | p.print(os); 75 | return os; 76 | } 77 | 78 | void Parameter::print(std::ostream &os) const { 79 | os << "(" << name << " : " << type << ")"; 80 | } 81 | 82 | // Lambda 83 | 84 | std::ostream &stg::operator<<(std::ostream &os, const Lambda &l) { 85 | l.print(os); 86 | return os; 87 | } 88 | 89 | std::string getIndentStr(const int nest, const int nlines) { 90 | static const int NUM_GUIDES=6; 91 | std::string indents[NUM_GUIDES] = { "┊","|","▏"," ▎", "▍", "▌" }; 92 | std::string out; 93 | for(int i = 0; i < nest; i++) { 94 | out += indents[std::min(i / 4, NUM_GUIDES)]; 95 | out += " "; 96 | } 97 | return out; 98 | } 99 | 100 | std::string indentedNewline(const int nest) { 101 | static int nlines = 0; 102 | nlines++; 103 | return "\n" + getIndentStr(nest, nlines); 104 | } 105 | 106 | void Lambda::print(std::ostream &os, int nest) const { 107 | os << "\\"; 108 | if (freeparams.size() > 0) 109 | printSepBy(os, "(", (ArrayRef)freeparams, " ", ") "); 110 | 111 | printSepBy(os, "(", (ArrayRef)boundparams, " ", ")"); 112 | os << " -> "; 113 | os << returnType << " "; 114 | os << "{"; os << indentedNewline(nest + 1); 115 | expr->print(os, nest+1); 116 | os << indentedNewline(nest); os << "}"; 117 | } 118 | 119 | // Expression 120 | void ExpressionAp::print(std::ostream &os, int nest) const { 121 | os << fn; 122 | printSepBy(os, "(", (ArrayRef)args, " ", ")"); 123 | }; 124 | 125 | void ExpressionConstructor::print(std::ostream &os, int nest) const { 126 | os << name; 127 | printSepBy(os, "(", ArrayRef(args), " ", ")"); 128 | 129 | } 130 | void ExpressionCase::print(std::ostream &os, int nest) const { 131 | os << "(case"; 132 | os << indentedNewline(nest + 1); 133 | scrutinee->print(os, nest + 1); 134 | os << indentedNewline(nest); 135 | os << "of"; 136 | int i = 0; 137 | for(const CaseAlt *alt: alts) { 138 | os << indentedNewline(nest + 1); 139 | alt->print(os, nest + 1); 140 | 141 | i++; 142 | } 143 | os << indentedNewline(nest); 144 | os << ")"; 145 | } 146 | 147 | void ExpressionLet::print(std::ostream &os, int nest) const { 148 | os << "(let"; 149 | 150 | for (const Binding *b : bindings) { 151 | os << indentedNewline(nest + 1); 152 | b->print(os, nest + 1); 153 | } 154 | os << indentedNewline(nest); 155 | os << "in"; 156 | os << indentedNewline(nest + 1); 157 | os << *rhs; 158 | os << indentedNewline(nest); 159 | os << ")"; 160 | } 161 | 162 | void ExpressionIntLiteral::print(std::ostream &os, int nest) const { 163 | os << value; 164 | } 165 | // Case 166 | 167 | std::ostream &stg::operator<<(std::ostream &os, const CaseAlt &a) { 168 | a.print(os); 169 | return os; 170 | }; 171 | void CaseAltInt::print(std::ostream &os, int nest) const { 172 | os << "(casealt " << *lhs << " -> "; 173 | os << indentedNewline(nest + 1); 174 | rhs->print(os, nest + 1); 175 | os << indentedNewline(nest); 176 | os << ")"; 177 | } 178 | 179 | 180 | void CaseAltVariable::print(std::ostream &os, int nest) const { 181 | os << "(caesalt " << lhs << " -> "; 182 | os << indentedNewline(nest + 1); 183 | rhs->print(os, nest + 1); 184 | os << indentedNewline(nest); 185 | os << ")"; 186 | } 187 | 188 | void CaseAltDestructure::print(std::ostream &os, int nest) const { 189 | os << "(casealt "; 190 | os << constructorName; 191 | printSepBy(os, "(", (ArrayRef)vars, " ", ")"); 192 | os << " -> "; 193 | os << indentedNewline(nest + 1); 194 | rhs->print(os, nest + 1); 195 | os << indentedNewline(nest); 196 | os << ")"; 197 | } 198 | 199 | 200 | void CaseAltDefault::print(std::ostream &os, int nest) const { 201 | os << "(casealt default" << "->"; 202 | os << indentedNewline(nest+1); 203 | rhs->print(os, nest + 1); 204 | os << indentedNewline(nest); 205 | os << ")"; 206 | } 207 | 208 | // Binding 209 | std::ostream &stg::operator<<(std::ostream &os, const Binding &b) { 210 | b.print(os); 211 | return os; 212 | } 213 | 214 | void Binding::print(std::ostream &os, int nest) const { 215 | os << "(define " << this->lhs << " = "; 216 | this->rhs->print(os, nest + 1); 217 | os << indentedNewline(nest); 218 | os << ")"; 219 | } 220 | 221 | // Program 222 | 223 | std::ostream &stg::operator<<(std::ostream &os, const Program &p) { 224 | os << "\n"; 225 | for (DataType *dt : p.datatypes){ 226 | os << *dt << "\n"; 227 | } 228 | for(Binding *b : p.bindings) { 229 | os << *b; 230 | os << "\n"; 231 | } 232 | return os; 233 | } 234 | -------------------------------------------------------------------------------- /include/sxhc/RuntimeDebugBuilder.h: -------------------------------------------------------------------------------- 1 | //===--- RuntimeDebugBuilder.h --- Helper to insert prints into LLVM-IR ---===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #ifndef RUNTIME_DEBUG_BUILDER_H 13 | #define RUNTIME_DEBUG_BUILDER_H 14 | 15 | #include "types.h" 16 | #include "llvm/ADT/ArrayRef.h" 17 | #include "llvm/ADT/StringRef.h" 18 | #include 19 | 20 | namespace llvm { 21 | class Value; 22 | class Function; 23 | } // namespace llvm 24 | 25 | namespace sxhc { 26 | 27 | /// Insert function calls that print certain LLVM values at run time. 28 | /// 29 | /// This class inserts libc function calls to print certain LLVM values at 30 | /// run time. 31 | struct RuntimeDebugBuilder { 32 | 33 | /// Print a set of LLVM-IR Values or StringRefs via printf 34 | /// 35 | /// This function emits a call to printf that will print the given arguments. 36 | /// It is useful for debugging CPU programs. All arguments given in this list 37 | /// will be automatically concatenated and the resulting string will be 38 | /// printed atomically. We also support ArrayRef arguments, which can be used 39 | /// to provide of id values. 40 | /// 41 | /// @param Builder The builder used to emit the printer calls. 42 | /// @param Args The list of values to print. 43 | template 44 | static void createCPUPrinter(StgIRBuilder &Builder, Args... args) { 45 | std::vector Vector; 46 | createPrinter(Builder, /* CPU */ false, Vector, args...); 47 | } 48 | 49 | /// Print a set of LLVM-IR Values or StringRefs on an NVIDIA GPU. 50 | /// 51 | /// This function emits a call to vprintf that will print the given 52 | /// arguments from within a kernel thread. It is useful for debugging 53 | /// CUDA program kernels. All arguments given in this list will be 54 | /// automatically concatenated and the resulting string will be printed 55 | /// atomically. We also support ArrayRef arguments, which can be used to 56 | /// provide for example a list of thread-id values. 57 | /// 58 | /// @param Builder The builder used to emit the printer calls. 59 | /// @param Args The list of values to print. 60 | template 61 | static void createGPUPrinter(StgIRBuilder &Builder, Args... args) { 62 | std::vector Vector; 63 | createPrinter(Builder, /* GPU */ true, Vector, args...); 64 | } 65 | 66 | private: 67 | /// Handle Values. 68 | template 69 | static void createPrinter(StgIRBuilder &Builder, bool UseGPU, 70 | std::vector &Values, 71 | llvm::Value *Value, Args... args) { 72 | Values.push_back(Value); 73 | createPrinter(Builder, UseGPU, Values, args...); 74 | } 75 | 76 | /// Handle StringRefs. 77 | template 78 | static void createPrinter(StgIRBuilder &Builder, bool UseGPU, 79 | std::vector &Values, 80 | llvm::StringRef String, Args... args) { 81 | Values.push_back(Builder.CreateGlobalStringPtr(String, "", 4)); 82 | createPrinter(Builder, UseGPU, Values, args...); 83 | } 84 | 85 | /// Handle ArrayRefs. 86 | template 87 | static void createPrinter(StgIRBuilder &Builder, bool UseGPU, 88 | std::vector &Values, 89 | llvm::ArrayRef Array, Args... args) { 90 | if (Array.size() >= 2) 91 | createPrinter(Builder, Values, Array[0], " ", 92 | llvm::ArrayRef(&Array[1], Array.size() - 1), 93 | args...); 94 | else if (Array.size() == 1) 95 | createPrinter(Builder, UseGPU, Values, Array[0], args...); 96 | else 97 | createPrinter(Builder, UseGPU, Values, args...); 98 | } 99 | 100 | /// Print a list of Values. 101 | static void createPrinter(StgIRBuilder &Builder, bool UseGPU, 102 | llvm::ArrayRef Values); 103 | 104 | /// Print a list of Values on a GPU. 105 | static void createGPUPrinterT(StgIRBuilder &Builder, 106 | llvm::ArrayRef Values); 107 | 108 | /// Print a list of Values on a CPU. 109 | static void createCPUPrinterT(StgIRBuilder &Builder, 110 | llvm::ArrayRef Values); 111 | 112 | /// Get a reference to the 'printf' function. 113 | /// 114 | /// If the current module does not yet contain a reference to printf, we 115 | /// insert a reference to it. Otherwise the existing reference is returned. 116 | static llvm::Function *getPrintF(StgIRBuilder &Builder); 117 | 118 | /// Call printf 119 | /// 120 | /// @param Builder The builder used to insert the code. 121 | /// @param Format The format string. 122 | /// @param Values The set of values to print. 123 | static void createPrintF(StgIRBuilder &Builder, std::string Format, 124 | llvm::ArrayRef Values); 125 | 126 | /// Get (and possibly insert) a vprintf declaration into the module. 127 | static llvm::Function *getVPrintF(StgIRBuilder &Builder); 128 | 129 | /// Call fflush 130 | /// 131 | /// @parma Builder The builder used to insert the code. 132 | static void createFlush(StgIRBuilder &Builder); 133 | 134 | /// Get (and possibly insert) a NVIDIA address space cast call. 135 | static llvm::Function *getAddressSpaceCast(StgIRBuilder &Builder, 136 | unsigned Src, unsigned Dst, 137 | unsigned SrcBits = 8, 138 | unsigned DstBits = 8); 139 | 140 | /// Get identifiers that describe the currently executed GPU thread. 141 | /// 142 | /// The result will be a vector that if passed to the GPU printer will result 143 | /// into a string (initialized to values corresponding to the printing 144 | /// thread): 145 | /// 146 | /// "> block-id: bidx bid1y bidz | thread-id: tidx tidy tidz " 147 | static std::vector 148 | getGPUThreadIdentifiers(StgIRBuilder &Builder); 149 | }; 150 | } // namespace polly 151 | 152 | extern bool PollyDebugPrinting; 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /docs/DoxygenLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /src/parser.yy: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "sxhc/stgir.h" 7 | 8 | using namespace std; 9 | 10 | #define YYERROR_VERBOSE 11 | using namespace std; 12 | using namespace stg; 13 | using namespace llvm; 14 | //extern "C" int yylex(void); 15 | //extern "C" int yyparse(void); 16 | int yylex(void); 17 | int yyparse(void); 18 | 19 | extern int g_lexer_success; 20 | extern int g_lexer_line_num; 21 | 22 | using ParamList = Lambda::ParamList; 23 | 24 | void yyerror(const char *s) { 25 | fprintf(stderr, "line %d: %s\n", g_lexer_line_num, s); 26 | g_lexer_success = 0; 27 | } 28 | 29 | std::vector g_toplevel_bindings; 30 | std::vector g_let_bindings; 31 | std::vector g_datatypes; 32 | ParamList g_params; 33 | std::vectorg_types; 34 | std::vectorg_dataconstructors; 35 | stg::Program *g_program; 36 | 37 | 38 | std::stack g_params_stack; 39 | 40 | 41 | std::string *g_datadeclaration_name; 42 | std::vector g_alt_destructure_vars; 43 | 44 | 45 | ParamList g_lambda_freevars; 46 | 47 | 48 | 49 | void add_param_to_list(Parameter *p) { 50 | g_params.push_back(p); 51 | } 52 | 53 | void add_data_constructor_to_list(DataConstructor *b) { 54 | g_dataconstructors.push_back(b); 55 | } 56 | %} 57 | 58 | %union{ 59 | std::vector *atomslist; 60 | std::vector *typeslist; 61 | std::vector *altslist; 62 | stg::Atom *atom; 63 | stg::CaseAlt *alt; 64 | stg::Expression *expr; 65 | stg::Lambda *lambda; 66 | stg::Binding *binding; 67 | stg::DataType *datatype; 68 | stg::Parameter *param; 69 | stg::DataConstructor *dataconstructor; 70 | std::string *constructorName; 71 | stg::TypeRaw *typeraw; 72 | 73 | bool UNDEF; 74 | } 75 | 76 | %token ASSIGN 77 | %token OPENPAREN 78 | %token CLOSEPAREN 79 | %token CASE 80 | %token OF 81 | %token SEMICOLON 82 | %token COLON 83 | %token THINARROW 84 | %token PIPE 85 | %token EOFTOKEN 86 | %token LAMBDA 87 | %token OPENFLOWER 88 | %token CLOSEFLOWER 89 | %token BINDING 90 | %token DATA 91 | %token LET 92 | %token IN 93 | %token DEFAULT 94 | 95 | %start toplevel 96 | %token ATOMINT 97 | %token CONSTRUCTORNAME 98 | %token ATOMSTRING 99 | 100 | %type program 101 | %type binding 102 | %type lambda 103 | %type expr 104 | %type atom 105 | %type datatype 106 | %type dataconstructor 107 | %type typeraw 108 | 109 | %type altlist; 110 | %type alt; 111 | %type altint; 112 | %type altvar; 113 | %type altdefault; 114 | %type altdestructure; 115 | 116 | %type atoms_; 117 | %type atomlist; 118 | 119 | 120 | %type typeraw_typeslist_; 121 | %type typeraw_typeslist; 122 | 123 | %type params; 124 | %type param; 125 | 126 | %type topleveldefn; 127 | 128 | %% 129 | toplevel: 130 | program { 131 | g_program = new stg::Program(g_toplevel_bindings, g_datatypes); } 132 | 133 | binding: 134 | BINDING ATOMSTRING ASSIGN lambda SEMICOLON { $$ = new stg::Binding(cast($2)->getIdent(), $4); }; 135 | 136 | 137 | 138 | // Data declaration 139 | datacons_typeslist_: 140 | datacons_typeslist_ CONSTRUCTORNAME { g_types.push_back($2); } 141 | | CONSTRUCTORNAME { g_types.push_back($1); } 142 | datacons_typeslist: OPENPAREN CLOSEPAREN | OPENPAREN datacons_typeslist_ CLOSEPAREN 143 | 144 | 145 | dataconstructor: 146 | CONSTRUCTORNAME datacons_typeslist { 147 | $$ = new stg::DataConstructor(*$1, g_types); 148 | g_types.clear(); 149 | } 150 | 151 | dataconstructorlist: 152 | dataconstructorlist PIPE dataconstructor 153 | { 154 | add_data_constructor_to_list($3); 155 | } 156 | | dataconstructor { 157 | add_data_constructor_to_list($1); 158 | } 159 | datatype: DATA CONSTRUCTORNAME { g_datadeclaration_name = $2; } ASSIGN dataconstructorlist SEMICOLON { 160 | $$ = new stg::DataType(*g_datadeclaration_name, g_dataconstructors); 161 | g_dataconstructors.clear(); 162 | delete g_datadeclaration_name; 163 | } 164 | 165 | topleveldefn: 166 | binding { g_toplevel_bindings.push_back($1); } 167 | | datatype { g_datatypes.push_back($1); } 168 | 169 | program: 170 | program topleveldefn 171 | | topleveldefn 172 | 173 | atom: 174 | ATOMINT | ATOMSTRING 175 | 176 | atoms_: 177 | atoms_ atom { 178 | $$ = $1; 179 | $$->push_back($2); 180 | } 181 | | atom { $$ = new std::vector(); $$->push_back($1); } 182 | 183 | atomlist: OPENPAREN atoms_ CLOSEPAREN { 184 | $$ = $2; 185 | } 186 | | OPENPAREN CLOSEPAREN { 187 | $$ = new std::vector(); 188 | } 189 | 190 | // Alternates 191 | altlist: altlist alt SEMICOLON { 192 | $1->push_back($2); 193 | $$ = $1; 194 | } | alt SEMICOLON { $$ = new std::vector(); 195 | $$->push_back($1); 196 | } 197 | 198 | 199 | altint: 200 | ATOMINT THINARROW expr { $$ = new stg::CaseAltInt(cast($1), $3); } 201 | 202 | altvar: 203 | ATOMSTRING THINARROW expr { $$ = new stg::CaseAltVariable(cast($1)->getIdent(), $3); } 204 | 205 | altdefault: DEFAULT THINARROW expr { $$ = new stg::CaseAltDefault($3); } 206 | 207 | alt: 208 | altint | altvar | altdestructure | altdefault 209 | // Note that we need a mid rule action so that we reserve the atoms 210 | // consumed so far. Otherwise, expr will take all the atoms. 211 | // case * of (Int (x y) -> print (z)) 212 | // would be parsed as: 213 | // case * of (Int () -> print (x y z) 214 | altdestructure: 215 | CONSTRUCTORNAME atomlist THINARROW expr { 216 | // NOTE: the middle block is considered as a "marker". So, it's $3, NOT 217 | // THINARROW. 218 | // Wish the docs were clearer about this. 219 | std::vector idents; 220 | for(Atom *a : *$2) { 221 | AtomIdent *ident = cast(a); 222 | idents.push_back(ident->getIdent()); 223 | } 224 | $$ = new stg::CaseAltDestructure(*$1, idents, $4); 225 | g_alt_destructure_vars.clear(); 226 | delete $1; 227 | } 228 | 229 | // types: either CONSTRUCTOR | RETTY(TYPES ) 230 | typeraw: CONSTRUCTORNAME 231 | { 232 | $$ = new stg::DataTypeRaw(*$1); 233 | } 234 | | 235 | typeraw_typeslist THINARROW CONSTRUCTORNAME { 236 | $$ = new stg::FunctionTypeRaw(*$3, *$1); 237 | } 238 | | 239 | OPENPAREN CLOSEPAREN THINARROW CONSTRUCTORNAME { 240 | $$ = new stg::FunctionTypeRaw(*$4, {}); 241 | } 242 | 243 | typeraw_typeslist: OPENPAREN typeraw_typeslist_ { 244 | $$ = $2; 245 | } 246 | 247 | typeraw_typeslist_: CONSTRUCTORNAME typeraw_typeslist_ { 248 | $$ = $2; 249 | $$->push_back(*$1); 250 | } | CONSTRUCTORNAME CLOSEPAREN { 251 | $$ = new std::vector(); 252 | $$->push_back(*$1); 253 | } 254 | 255 | // Parameters 256 | param: 257 | ATOMSTRING COLON typeraw { $$ = new stg::Parameter(cast($1)->getIdent(), $3); } 258 | 259 | params_: 260 | params_ param { add_param_to_list($2); } 261 | | param { add_param_to_list($1); } 262 | 263 | params: 264 | OPENPAREN { g_params = {}; } 265 | params_ CLOSEPAREN { 266 | g_params_stack.push(g_params); 267 | g_params = {}; 268 | } | OPENPAREN CLOSEPAREN { 269 | g_params_stack.push(ParamList()); 270 | g_params = {}; 271 | } 272 | 273 | lambda: 274 | LAMBDA params THINARROW CONSTRUCTORNAME 275 | OPENFLOWER expr CLOSEFLOWER 276 | { 277 | $$ = new stg::Lambda(g_params_stack.top(), *$4, $6); 278 | g_params_stack.pop(); 279 | } 280 | | 281 | // syntax for free vars 282 | LAMBDA params params THINARROW CONSTRUCTORNAME OPENFLOWER expr CLOSEFLOWER { 283 | 284 | ParamList boundvars = g_params_stack.top(); 285 | g_params_stack.pop(); 286 | ParamList freevars = g_params_stack.top(); 287 | g_params_stack.pop(); 288 | 289 | $$ = new stg::Lambda(freevars, boundvars, *$5, $7); 290 | g_params.clear(); 291 | } 292 | 293 | 294 | letbindings: binding { g_let_bindings.push_back($1); } | 295 | letbindings binding { g_let_bindings.push_back($2); } 296 | 297 | expr: 298 | // function application 299 | ATOMSTRING atomlist { $$ = new stg::ExpressionAp(cast($1)->getIdent(), *$2); } 300 | | CONSTRUCTORNAME atomlist { 301 | $$ = new stg::ExpressionConstructor(*$1, *$2); 302 | delete $1; 303 | } 304 | | CASE expr OF altlist { $$ = new stg::ExpressionCase($2, *$4); } 305 | | LET letbindings IN expr { 306 | $$ = new stg::ExpressionLet(g_let_bindings, $4); 307 | g_let_bindings.clear(); 308 | } 309 | 310 | | ATOMINT { 311 | $$ = new stg::ExpressionIntLiteral(cast($1)->getVal()); 312 | } 313 | 314 | %% 315 | 316 | -------------------------------------------------------------------------------- /src/RuntimeDebugBuilder.cpp: -------------------------------------------------------------------------------- 1 | //===--- RuntimeDebugBuilder.cpp - Helper to insert prints into LLVM-IR ---===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #include "sxhc/RuntimeDebugBuilder.h" 13 | #include "llvm/IR/Intrinsics.h" 14 | #include "llvm/IR/Module.h" 15 | #include "llvm/Support/Debug.h" 16 | #include 17 | #include 18 | 19 | using namespace llvm; 20 | using namespace sxhc; 21 | 22 | 23 | Function *RuntimeDebugBuilder::getVPrintF(StgIRBuilder &Builder) { 24 | Module *M = Builder.GetInsertBlock()->getParent()->getParent(); 25 | const char *Name = "vprintf"; 26 | Function *F = M->getFunction(Name); 27 | 28 | if (!F) { 29 | GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 30 | FunctionType *Ty = FunctionType::get( 31 | Builder.getInt32Ty(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()}, 32 | false); 33 | F = Function::Create(Ty, Linkage, Name, M); 34 | } 35 | 36 | return F; 37 | } 38 | 39 | Function *RuntimeDebugBuilder::getAddressSpaceCast(StgIRBuilder &Builder, 40 | unsigned Src, unsigned Dst, 41 | unsigned SrcBits, 42 | unsigned DstBits) { 43 | Module *M = Builder.GetInsertBlock()->getParent()->getParent(); 44 | auto Name = std::string("llvm.nvvm.ptr.constant.to.gen.p") + 45 | std::to_string(Dst) + "i" + std::to_string(DstBits) + ".p" + 46 | std::to_string(Src) + "i" + std::to_string(SrcBits); 47 | Function *F = M->getFunction(Name); 48 | 49 | if (!F) { 50 | GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 51 | FunctionType *Ty = FunctionType::get( 52 | PointerType::get(Builder.getIntNTy(DstBits), Dst), 53 | PointerType::get(Builder.getIntNTy(SrcBits), Src), false); 54 | F = Function::Create(Ty, Linkage, Name, M); 55 | } 56 | 57 | return F; 58 | } 59 | 60 | std::vector 61 | RuntimeDebugBuilder::getGPUThreadIdentifiers(StgIRBuilder &Builder) { 62 | std::vector Identifiers; 63 | 64 | auto M = Builder.GetInsertBlock()->getParent()->getParent(); 65 | 66 | std::vector BlockIDs = { 67 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_x), 68 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_y), 69 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_z), 70 | }; 71 | 72 | Identifiers.push_back(Builder.CreateGlobalStringPtr("> block-id: ", "", 4)); 73 | for (auto GetID : BlockIDs) { 74 | Value *Id = Builder.CreateCall(GetID, {}); 75 | Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false); 76 | Identifiers.push_back(Id); 77 | Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4)); 78 | } 79 | 80 | Identifiers.push_back(Builder.CreateGlobalStringPtr("| ", "", 4)); 81 | 82 | std::vector ThreadIDs = { 83 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_x), 84 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_y), 85 | Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_z), 86 | }; 87 | 88 | Identifiers.push_back(Builder.CreateGlobalStringPtr("thread-id: ", "", 4)); 89 | for (auto GetId : ThreadIDs) { 90 | Value *Id = Builder.CreateCall(GetId, {}); 91 | Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false); 92 | Identifiers.push_back(Id); 93 | Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4)); 94 | } 95 | 96 | return Identifiers; 97 | } 98 | 99 | void RuntimeDebugBuilder::createPrinter(StgIRBuilder &Builder, bool IsGPU, 100 | ArrayRef Values) { 101 | if (IsGPU) 102 | createGPUPrinterT(Builder, Values); 103 | else 104 | createCPUPrinterT(Builder, Values); 105 | } 106 | 107 | static std::tuple> 108 | prepareValuesForPrinting(StgIRBuilder &Builder, ArrayRef Values) { 109 | std::string FormatString; 110 | std::vector ValuesToPrint; 111 | 112 | for (auto Val : Values) { 113 | Type *Ty = Val->getType(); 114 | 115 | if (Ty->isFloatingPointTy()) { 116 | if (!Ty->isDoubleTy()) 117 | Val = Builder.CreateFPExt(Val, Builder.getDoubleTy()); 118 | } else if (Ty->isIntegerTy()) { 119 | if (Ty->getIntegerBitWidth() < 64) 120 | Val = Builder.CreateSExt(Val, Builder.getInt64Ty()); 121 | else 122 | assert(Ty->getIntegerBitWidth() && 123 | "Integer types larger 64 bit not supported"); 124 | } else if (isa(Ty)) { 125 | if (Ty->getPointerElementType() == Builder.getInt8Ty() && 126 | Ty->getPointerAddressSpace() == 4) { 127 | Val = Builder.CreateGEP(Val, Builder.getInt64(0)); 128 | } else { 129 | Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty()); 130 | } 131 | } else { 132 | llvm_unreachable("Unknown type"); 133 | } 134 | 135 | Ty = Val->getType(); 136 | 137 | if (Ty->isFloatingPointTy()) 138 | FormatString += "%f"; 139 | else if (Ty->isIntegerTy()) 140 | FormatString += "%ld"; 141 | else 142 | FormatString += "%s"; 143 | 144 | ValuesToPrint.push_back(Val); 145 | } 146 | 147 | return std::make_tuple(FormatString, ValuesToPrint); 148 | } 149 | 150 | void RuntimeDebugBuilder::createCPUPrinterT(StgIRBuilder &Builder, 151 | ArrayRef Values) { 152 | 153 | std::string FormatString; 154 | std::vector ValuesToPrint; 155 | 156 | std::tie(FormatString, ValuesToPrint) = 157 | prepareValuesForPrinting(Builder, Values); 158 | 159 | createPrintF(Builder, FormatString, ValuesToPrint); 160 | createFlush(Builder); 161 | } 162 | 163 | void RuntimeDebugBuilder::createGPUPrinterT(StgIRBuilder &Builder, 164 | ArrayRef Values) { 165 | std::string str; 166 | 167 | auto *Zero = Builder.getInt64(0); 168 | 169 | auto ToPrint = getGPUThreadIdentifiers(Builder); 170 | 171 | ToPrint.push_back(Builder.CreateGlobalStringPtr("\n ", "", 4)); 172 | ToPrint.insert(ToPrint.end(), Values.begin(), Values.end()); 173 | 174 | const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout(); 175 | 176 | // Allocate print buffer (assuming 2*32 bit per element) 177 | auto T = ArrayType::get(Builder.getInt32Ty(), ToPrint.size() * 2); 178 | Value *Data = new AllocaInst( 179 | T, DL.getAllocaAddrSpace(), "polly.vprint.buffer", 180 | &Builder.GetInsertBlock()->getParent()->getEntryBlock().front()); 181 | auto *DataPtr = Builder.CreateGEP(Data, {Zero, Zero}); 182 | 183 | int Offset = 0; 184 | for (auto Val : ToPrint) { 185 | auto Ptr = Builder.CreateGEP(DataPtr, Builder.getInt64(Offset)); 186 | Type *Ty = Val->getType(); 187 | 188 | if (Ty->isFloatingPointTy()) { 189 | if (!Ty->isDoubleTy()) 190 | Val = Builder.CreateFPExt(Val, Builder.getDoubleTy()); 191 | } else if (Ty->isIntegerTy()) { 192 | if (Ty->getIntegerBitWidth() < 64) 193 | Val = Builder.CreateSExt(Val, Builder.getInt64Ty()); 194 | else 195 | assert(Ty->getIntegerBitWidth() && 196 | "Integer types larger 64 bit not supported"); 197 | } else if (auto PtTy = dyn_cast(Ty)) { 198 | if (PtTy->getAddressSpace() == 4) { 199 | // Pointers in constant address space are printed as strings 200 | Val = Builder.CreateGEP(Val, Builder.getInt64(0)); 201 | auto F = RuntimeDebugBuilder::getAddressSpaceCast(Builder, 4, 0); 202 | Val = Builder.CreateCall(F, Val); 203 | } else { 204 | Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty()); 205 | } 206 | } else { 207 | llvm_unreachable("Unknown type"); 208 | } 209 | 210 | Ty = Val->getType(); 211 | Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Ty->getPointerTo(5)); 212 | Builder.CreateAlignedStore(Val, Ptr, 4); 213 | 214 | if (Ty->isFloatingPointTy()) 215 | str += "%f"; 216 | else if (Ty->isIntegerTy()) 217 | str += "%ld"; 218 | else 219 | str += "%s"; 220 | 221 | Offset += 2; 222 | } 223 | 224 | Value *Format = Builder.CreateGlobalStringPtr(str, "polly.vprintf.buffer", 4); 225 | Format = Builder.CreateCall(getAddressSpaceCast(Builder, 4, 0), Format); 226 | 227 | Data = Builder.CreateBitCast(Data, Builder.getInt8PtrTy()); 228 | 229 | Builder.CreateCall(getVPrintF(Builder), {Format, Data}); 230 | } 231 | 232 | Function *RuntimeDebugBuilder::getPrintF(StgIRBuilder &Builder) { 233 | Module *M = Builder.GetInsertBlock()->getParent()->getParent(); 234 | const char *Name = "printf"; 235 | Function *F = M->getFunction(Name); 236 | 237 | if (!F) { 238 | GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 239 | FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), true); 240 | F = Function::Create(Ty, Linkage, Name, M); 241 | } 242 | 243 | return F; 244 | } 245 | 246 | void RuntimeDebugBuilder::createPrintF(StgIRBuilder &Builder, 247 | std::string Format, 248 | ArrayRef Values) { 249 | Value *FormatString = Builder.CreateGlobalStringPtr(Format); 250 | std::vector Arguments; 251 | 252 | Arguments.push_back(FormatString); 253 | Arguments.insert(Arguments.end(), Values.begin(), Values.end()); 254 | Builder.CreateCall(getPrintF(Builder), Arguments); 255 | } 256 | 257 | void RuntimeDebugBuilder::createFlush(StgIRBuilder &Builder) { 258 | Module *M = Builder.GetInsertBlock()->getParent()->getParent(); 259 | const char *Name = "fflush"; 260 | Function *F = M->getFunction(Name); 261 | 262 | if (!F) { 263 | GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 264 | FunctionType *Ty = 265 | FunctionType::get(Builder.getInt32Ty(), Builder.getInt8PtrTy(), false); 266 | F = Function::Create(Ty, Linkage, Name, M); 267 | } 268 | 269 | // fflush(NULL) flushes _all_ open output streams. 270 | // 271 | // fflush is declared as 'int fflush(FILE *stream)'. As we only pass on a NULL 272 | // pointer, the type we point to does conceptually not matter. However, if 273 | // fflush is already declared in this translation unit, we use the very same 274 | // type to ensure that LLVM does not complain about mismatching types. 275 | Builder.CreateCall(F, Constant::getNullValue(F->arg_begin()->getType())); 276 | } 277 | -------------------------------------------------------------------------------- /include/sxhc/optimizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "llvm/ADT/APFloat.h" 6 | #include "llvm/ADT/STLExtras.h" 7 | #include "llvm/ADT/SmallVector.h" 8 | #include "llvm/Bitcode/BitcodeWriter.h" 9 | #include "llvm/IR/BasicBlock.h" 10 | #include "llvm/IR/Constants.h" 11 | #include "llvm/IR/DerivedTypes.h" 12 | #include "llvm/IR/Function.h" 13 | #include "llvm/IR/IRBuilder.h" 14 | #include "llvm/IR/Instructions.h" 15 | #include "llvm/IR/LLVMContext.h" 16 | #include "llvm/IR/LegacyPassManager.h" 17 | #include "llvm/IR/Module.h" 18 | #include "llvm/IR/Type.h" 19 | #include "llvm/IR/Verifier.h" 20 | #include "llvm/Passes/PassBuilder.h" 21 | #include "llvm/Support/CommandLine.h" 22 | #include "llvm/Support/FileSystem.h" 23 | #include "llvm/Support/TargetRegistry.h" 24 | #include "llvm/Support/TargetSelect.h" 25 | #include "llvm/Target/TargetMachine.h" 26 | #include "llvm/Target/TargetOptions.h" 27 | #include "llvm/Transforms/IPO/AlwaysInliner.h" 28 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 29 | #include "llvm/Transforms/Utils/Cloning.h" 30 | #include "llvm/Transforms/Utils/ModuleUtils.h" 31 | #include "sxhc/StackAnalysis.h" 32 | #include "sxhc/types.h" 33 | 34 | #define DEBUG_TYPE "stackMatcher" 35 | bool debug = false; 36 | 37 | class PushPopMatch { 38 | public: 39 | PushPopMatch(StackInstruction push, StackInstruction pop) : pop(pop) { 40 | assert(pop.isPop()); 41 | insertPush(push); 42 | }; 43 | 44 | StackInstruction getPop() { return pop; } 45 | 46 | void insertPush(StackInstruction push) { 47 | assert(push.isPush()); 48 | assert(push.getPushedVal()->getType() == pop.getType()); 49 | pushes.push_back(push); 50 | } 51 | 52 | unsigned getNumPushes() { return pushes.size(); } 53 | 54 | StackInstruction getPush(unsigned n) { 55 | assert(n < pushes.size()); 56 | return pushes[n]; 57 | } 58 | 59 | Type *getType() { 60 | return pop.getType(); 61 | } 62 | 63 | private: 64 | StackInstruction pop; 65 | std::vector pushes; 66 | }; 67 | 68 | using namespace llvm; 69 | // Pass to match abstract stack manipulations and eliminate them. 70 | template 71 | class StackMatcherPass : public PassInfoMixin> { 72 | public: 73 | explicit StackMatcherPass() {}; 74 | static StringRef name() { return "StackMatcher"; } 75 | 76 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { 77 | if (F.isDeclaration()) { 78 | return llvm::PreservedAnalyses::all(); 79 | } 80 | 81 | StackAnalysis &SA = FAM.getResult>(F); 82 | std::vector matches; 83 | 84 | std::vector intraBBMatches = computeIntraBBMatches(SA, F); 85 | matches.insert(matches.begin(), intraBBMatches.begin(), intraBBMatches.end()); 86 | 87 | for(PushPopMatch ppm : matches) { 88 | propogatePushesToPop(ppm); 89 | } 90 | 91 | { 92 | std::set pushes; 93 | for (PushPopMatch ppm : matches) { 94 | for(unsigned i = 0; i < ppm.getNumPushes(); i++) { 95 | pushes.insert(ppm.getPush(i).getInstruction()); 96 | } 97 | } 98 | 99 | for (CallInst *push : pushes) { 100 | push->eraseFromParent(); 101 | } 102 | } 103 | 104 | return llvm::PreservedAnalyses::none(); 105 | } 106 | 107 | /* 108 | void getAnalysisUsage(AnalysisUsage &AU) const { 109 | AU.addRequired(); 110 | }*/ 111 | 112 | private: 113 | std::string stackname; 114 | 115 | static std::vector computeIntraBBMatches(StackAnalysis &SA, Function &F) { 116 | std::vector matches; 117 | for(BasicBlock &BB : F) { 118 | auto it = SA.find(&BB); 119 | assert(it != SA.end()); 120 | // lookup 121 | StackBB SBB = it->second; 122 | std::stack pushes; 123 | for (StackInstruction SI : SBB) { 124 | if (SI.isPush()) { 125 | pushes.push(SI); 126 | continue; 127 | } 128 | 129 | assert(SI.isPop()); 130 | 131 | // we have no push to match with, continue. 132 | if (pushes.size() == 0) continue; 133 | 134 | // grab the topmost push and use this. 135 | StackInstruction push = pushes.top(); 136 | pushes.pop(); 137 | 138 | // add the match. 139 | matches.push_back(PushPopMatch(push, SI)); 140 | 141 | } 142 | 143 | } 144 | 145 | return matches; 146 | } 147 | 148 | static void propogatePushesToPop(PushPopMatch matches) { 149 | llvm::CallInst *PopInst = matches.getPop().getInstruction(); 150 | 151 | // create the PHI node that replaces the pop as a PHI of all the pushes. 152 | errs() << "numPushes: " << matches.getNumPushes() << "\n"; 153 | PHINode *PopReplacement = 154 | PHINode::Create(matches.getType(), matches.getNumPushes(), 155 | PopInst->getName() + ".phi"); 156 | for (unsigned i = 0; i < matches.getNumPushes(); i++) { 157 | Value *pushValue = matches.getPush(i).getPushedVal(); 158 | BasicBlock *pushBB = matches.getPush(i).getInstruction()->getParent(); 159 | PopReplacement->addIncoming(pushValue, pushBB); 160 | } 161 | 162 | // Replace the pop instruction with the PHI node. 163 | BasicBlock::iterator ii(PopInst); 164 | ReplaceInstWithInst(PopInst->getParent()->getInstList(), ii, 165 | PopReplacement); 166 | } 167 | }; 168 | 169 | Instruction *getOwningInst(User *U) { 170 | if (isa(U)) return cast(U); 171 | if (Constant *C = dyn_cast(U)) { 172 | assert(C->getNumUses() == 1 && "constant has more than one use"); 173 | return getOwningInst(*C->users().begin()); 174 | } 175 | 176 | report_fatal_error("unreachable code in getOwningInst"); 177 | } 178 | 179 | // NOTE: this pass is most likely 100% wrong in the presence of loops. Soo.. fix 180 | // that plz :3 181 | template 182 | class SinkPushPass : public PassInfoMixin> { 183 | public: 184 | SinkPushPass() {}; 185 | static StringRef name() { return "SinkPush"; } 186 | 187 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { 188 | if (F.isDeclaration()) { 189 | return llvm::PreservedAnalyses::all(); 190 | } 191 | 192 | if (F.getName() != "main") return llvm::PreservedAnalyses::all(); 193 | 194 | // map from push to pops that accept it. 195 | std::map> pushToAcceptingPops = 196 | [&F, this] { 197 | std::map> matchedPops; 198 | std::set Visited; 199 | BasicBlock *Entry = &F.getEntryBlock(); 200 | 201 | findPushAndMatch(stackname, Entry, Entry->begin(), matchedPops, 202 | Visited); 203 | return matchedPops; 204 | }(); 205 | 206 | for (auto it : pushToAcceptingPops) { 207 | errs() << "Push: " << *it.first; 208 | for (CallInst *CI : it.second) { 209 | errs() << "\tPop: " << *CI << "\n"; 210 | } 211 | } 212 | return llvm::PreservedAnalyses::none(); 213 | } 214 | 215 | void getAnalysisUsage(AnalysisUsage &AU) const { 216 | AU.addRequired(); 217 | } 218 | 219 | private: 220 | const std::string stackname; 221 | 222 | static bool isInstPop(const Instruction *I, const std::string stackname) { 223 | const CallInst *CI = dyn_cast(I); 224 | if (!CI) return false; 225 | if (!CI->getCalledFunction()) return false; 226 | return CI->getCalledFunction()->getName() == "pop" + stackname; 227 | } 228 | 229 | static bool isInstPush(const Instruction *I, const std::string stackname) { 230 | const CallInst *CI = dyn_cast(I); 231 | if (!CI) return false; 232 | if (!CI->getCalledFunction()) return false; 233 | return CI->getCalledFunction()->getName() == "push" + stackname; 234 | } 235 | 236 | static void findPushAndMatch( 237 | const std::string stackname, BasicBlock *CurBB, BasicBlock::iterator it, 238 | std::map> &matchedPops, 239 | std::set &Visited) { 240 | assert(!Visited.count(CurBB)); 241 | for (; it != CurBB->end(); ++it) { 242 | Instruction *I = &*it; 243 | if (!isInstPush(I, stackname)) continue; 244 | CallInst *CI = cast(I); 245 | // we fond a push 246 | getMatchingPops(stackname.c_str(), CurBB, it, CI, matchedPops, Visited); 247 | return; 248 | } 249 | // no pushes found 250 | Visited.insert(CurBB); 251 | 252 | // BB had no pops. 253 | const Instruction *TI = CurBB->getTerminator(); 254 | assert(TI && "BB has no terminator!"); 255 | for (unsigned i = 0; i < TI->getNumSuccessors(); i++) { 256 | BasicBlock *Next = TI->getSuccessor(i); 257 | if (Next->getUniquePredecessor() == CurBB) { 258 | findPushAndMatch(stackname, Next, Next->begin(), matchedPops, 259 | Visited); 260 | } 261 | } 262 | } 263 | 264 | static void getMatchingPops( 265 | const char *stackname, BasicBlock *CurBB, BasicBlock::iterator it, 266 | CallInst *push, 267 | std::map> &matchedPops, 268 | std::set &Visited) { 269 | assert(!Visited.count(CurBB)); 270 | if (Visited.count(CurBB)) return; 271 | 272 | for (; it != CurBB->end(); ++it) { 273 | Instruction *I = &*it; 274 | if (!isInstPop(I, stackname)) continue; 275 | // we found a match. So now, switch to push-hunting mode. 276 | matchedPops[push].push_back(cast(I)); 277 | findPushAndMatch(stackname, CurBB, it, matchedPops, Visited); 278 | return; 279 | } 280 | 281 | // we finished the iterator, add us to `visited`. 282 | Visited.insert(CurBB); 283 | 284 | // BB had no pops. 285 | const Instruction *TI = CurBB->getTerminator(); 286 | assert(TI && "BB has no terminator!"); 287 | for (unsigned i = 0; i < TI->getNumSuccessors(); i++) { 288 | BasicBlock *Next = TI->getSuccessor(i); 289 | if (Next->getUniquePredecessor() == CurBB) { 290 | getMatchingPops(stackname, Next, Next->begin(), push, 291 | matchedPops, Visited); 292 | } 293 | } 294 | } 295 | }; 296 | 297 | class EliminateUnusedAllocPass 298 | : public PassInfoMixin { 299 | public: 300 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { 301 | if (F.isDeclaration()) { 302 | return llvm::PreservedAnalyses::all(); 303 | } 304 | // hack. 305 | if (true) { 306 | return llvm::PreservedAnalyses::all(); 307 | } 308 | AAResults &AA = FAM.getResult(F); 309 | runInternal(F, AA); 310 | 311 | return llvm::PreservedAnalyses::none(); 312 | } 313 | 314 | void getAnalysisUsage(AnalysisUsage &AU) const { 315 | AU.addRequired(); 316 | } 317 | 318 | private: 319 | // This instruction uses a pointer. Return if this instruction `I` allows 320 | // the pointer to escape. 321 | static bool isUserThatCreatesEscape(Instruction *I, 322 | Value *MaybeEscapingVal) { 323 | if (auto CI = dyn_cast(I)) { 324 | if (!CI->getCalledFunction()) { 325 | report_fatal_error( 326 | "indirect function call allows pointer to escape."); 327 | } 328 | if (CI->getName() == "pushReturn") return true; 329 | return false; 330 | } else if (auto SI = dyn_cast(I)) { 331 | // store does NOT escape. 332 | // store DOES escape. 333 | return SI->getOperand(0) == MaybeEscapingVal; 334 | } else if (isa(I)) { 335 | return false; 336 | } else if (isa(I) || isa(I)) { 337 | return doesEscape(I, MaybeEscapingVal); 338 | } 339 | errs() << "unknown instruction pass to " << __PRETTY_FUNCTION__ << ": " 340 | << *I << "\n"; 341 | report_fatal_error("unknown instruction."); 342 | } 343 | 344 | static bool doesEscape(Instruction *I, Value *MaybeEscapingVal) { 345 | for (User *U : I->users()) { 346 | Instruction *User = getOwningInst(U); 347 | if (isUserThatCreatesEscape(User, MaybeEscapingVal)) return true; 348 | } 349 | return false; 350 | } 351 | 352 | void runInternal(Function &F, AAResults &AA) { 353 | errs() << "Eliminate unused alloc running on: " << F.getName() << "\n"; 354 | 355 | for (BasicBlock &BB : F) { 356 | for (Instruction &I : BB) { 357 | if (!isa(I)) continue; 358 | CallInst &CI = cast(I); 359 | // we don't analyse indirect calls. 360 | if (!CI.getCalledFunction()) continue; 361 | 362 | assert(CI.getCalledFunction() && 363 | "should have bailed out by this point at an indirect " 364 | "function"); 365 | if (CI.getCalledFunction()->getName() != "alloc") { 366 | errs() << "* CallInst Escapes:" << CI << "\n"; 367 | continue; 368 | } else { 369 | errs() << "* CallInst DOES NOT Escape, removing:" << CI 370 | << "\n"; 371 | CI.replaceAllUsesWith(UndefValue::get(CI.getType())); 372 | CI.eraseFromParent(); 373 | } 374 | } 375 | } 376 | } 377 | }; 378 | -------------------------------------------------------------------------------- /include/sxhc/stgir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace stg { 9 | 10 | using namespace llvm; 11 | using Identifier = std::string; 12 | using ConstructorName = std::string; 13 | using TypeName = std::string; 14 | 15 | // *** Data declaration 16 | class DataType; 17 | class DataConstructor { 18 | public: 19 | using TypeList = SmallVector; 20 | 21 | private: 22 | friend class DataType; 23 | ConstructorName name; 24 | TypeList types; 25 | 26 | const DataType *parent; 27 | void _setParent(DataType *parent) { 28 | this->parent = parent; 29 | assert(parent); 30 | } 31 | 32 | public: 33 | DataConstructor(ConstructorName name, ArrayRef typesref) 34 | : name(name) { 35 | for (TypeName *t : typesref) { 36 | types.push_back(t); 37 | } 38 | } 39 | ConstructorName getName() const { return name; } 40 | 41 | using iterator = TypeList::iterator; 42 | using const_iterator = TypeList::const_iterator; 43 | 44 | iterator types_begin() { return types.begin(); } 45 | iterator types_end() { return types.end(); } 46 | 47 | const_iterator types_begin() const { return types.begin(); } 48 | const_iterator types_end() const { return types.end(); } 49 | 50 | const TypeName getTypeName(size_t i) const { return *types[i]; } 51 | 52 | iterator_range types_range() { 53 | return make_range(types_begin(), types_end()); 54 | }; 55 | iterator_range types_range() const { 56 | return make_range(types_begin(), types_end()); 57 | }; 58 | 59 | size_t types_size() const { return types.size(); } 60 | bool types_empty() const { return types.empty(); } 61 | 62 | const DataType *getParent() const { 63 | assert(parent); 64 | return this->parent; 65 | } 66 | 67 | void print(std::ostream &os) const; 68 | friend std::ostream &operator<<(std::ostream &os, 69 | const DataConstructor &branch); 70 | }; 71 | 72 | class DataType { 73 | public: 74 | using DataConstructorList = SmallVector; 75 | using iterator = DataConstructorList::iterator; 76 | using const_iterator = DataConstructorList::const_iterator; 77 | 78 | private: 79 | DataConstructorList constructors; 80 | TypeName name; 81 | 82 | public: 83 | DataType(TypeName name, ArrayRef consref) : name(name) { 84 | for (DataConstructor *c : consref) { 85 | c->_setParent(this); 86 | constructors.push_back(c); 87 | } 88 | } 89 | iterator begin() { return constructors.begin(); } 90 | const_iterator begin() const { return constructors.begin(); } 91 | 92 | iterator end() { return constructors.end(); } 93 | const_iterator end() const { return constructors.end(); } 94 | size_t constructors_size() const { return constructors.size(); } 95 | 96 | TypeName getTypeName() const { return name; } 97 | 98 | iterator_range constructors_range() { 99 | return make_range(begin(), end()); 100 | } 101 | iterator_range constructors_range() const { 102 | return make_range(begin(), end()); 103 | } 104 | 105 | void print(std::ostream &os) const; 106 | friend std::ostream &operator<<(std::ostream &os, const DataType &decl); 107 | unsigned getIndexForConstructor(const DataConstructor *needle) const { 108 | for (unsigned i = 0; i < constructors.size(); i++) { 109 | if (constructors[i] == needle) return i; 110 | } 111 | 112 | report_fatal_error( 113 | "unknown data constructor variant asked for data declaration"); 114 | } 115 | }; 116 | 117 | class TypeRaw { 118 | public: 119 | enum TypeKind { TK_Data, TK_Function }; 120 | TypeKind getKind() const { return kind; } 121 | void print(std::ostream &os) const; 122 | void dump() { print(std::cerr); } 123 | 124 | protected: 125 | TypeRaw(TypeKind kind) : kind(kind){}; 126 | TypeKind kind; 127 | }; 128 | 129 | class DataTypeRaw : public TypeRaw { 130 | private: 131 | TypeName name; 132 | 133 | public: 134 | DataTypeRaw(TypeName name) : TypeRaw(TK_Data), name(name){}; 135 | static bool classof(const TypeRaw *T) { 136 | return T->getKind() == TypeRaw::TK_Data; 137 | } 138 | TypeName getName() const { return name; } 139 | }; 140 | 141 | class FunctionTypeRaw : public TypeRaw { 142 | public: 143 | using ParamNamesListTy = SmallVector; 144 | using const_iterator = ParamNamesListTy::const_iterator; 145 | 146 | private: 147 | TypeName returnname; 148 | SmallVector paramnames; 149 | 150 | public: 151 | FunctionTypeRaw(TypeName returnname, ArrayRef paramnamesref) 152 | : TypeRaw(TK_Function), returnname(returnname) { 153 | for (TypeName name : paramnamesref) paramnames.push_back(name); 154 | } 155 | static bool classof(const TypeRaw *T) { 156 | return T->getKind() == TypeRaw::TK_Function; 157 | } 158 | void print(std::ostream &os) const; 159 | 160 | iterator_range params_range() const { 161 | return iterator_range(paramnames.begin(), 162 | paramnames.end()); 163 | } 164 | 165 | TypeName getReturnTypeName() const { return returnname; } 166 | }; 167 | 168 | // *** Atom *** 169 | class Atom { 170 | public: 171 | enum AtomKind { AK_Int, AK_Ident }; 172 | 173 | AtomKind getKind() const { return kind; } 174 | virtual void print(std::ostream &os) const = 0; 175 | 176 | friend std::ostream &operator<<(std::ostream &os, const Atom &a); 177 | 178 | protected: 179 | Atom(AtomKind kind) : kind(kind){}; 180 | 181 | private: 182 | const AtomKind kind; 183 | }; 184 | 185 | class AtomInt : public Atom { 186 | int val; 187 | 188 | public: 189 | AtomInt(int val) : Atom(Atom::AK_Int), val(val) {} 190 | void print(std::ostream &os) const; 191 | static bool classof(const Atom *S) { return S->getKind() == Atom::AK_Int; } 192 | int getVal() const { return val; } 193 | }; 194 | 195 | class AtomIdent : public Atom { 196 | Identifier ident; 197 | 198 | public: 199 | AtomIdent(std::string ident) : Atom(Atom::AK_Ident), ident(ident) {} 200 | void print(std::ostream &os) const; 201 | 202 | Identifier getIdent() const { return ident; }; 203 | 204 | static bool classof(const Atom *S) { 205 | return S->getKind() == Atom::AK_Ident; 206 | } 207 | }; 208 | 209 | // *** Expression **** 210 | class Expression { 211 | public: 212 | enum ExpressionKind { EK_Ap, EK_Cons, EK_Case, EK_Let, EK_IntLiteral }; 213 | virtual void print(std::ostream &os, int nest=0) const = 0; 214 | ExpressionKind getKind() const { return kind; } 215 | 216 | friend std::ostream &operator<<(std::ostream &os, const Expression &e) { 217 | e.print(os); 218 | return os; 219 | } 220 | 221 | 222 | void __attribute__((used)) dump() const { 223 | std::cerr << *this; 224 | } 225 | 226 | protected: 227 | Expression(ExpressionKind kind) : kind(kind){}; 228 | 229 | private: 230 | const ExpressionKind kind; 231 | }; 232 | 233 | class ExpressionAp : public Expression { 234 | public: 235 | using ParamsTy = SmallVector; 236 | using const_iterator = ParamsTy::const_iterator; 237 | using const_reverse_iterator = ParamsTy::const_reverse_iterator; 238 | 239 | const_iterator params_begin() const { return args.begin(); } 240 | const_iterator params_end() const { return args.end(); } 241 | 242 | iterator_range params_range() const { 243 | return make_range(params_begin(), params_end()); 244 | } 245 | 246 | iterator_range params_reverse_range() const { 247 | return make_range(params_end(), params_begin()); 248 | } 249 | ExpressionAp(Identifier fn, std::initializer_list args) 250 | : Expression(Expression::EK_Ap), fn(fn), args(args){}; 251 | 252 | ExpressionAp(Identifier fn, ArrayRef argsref) 253 | : Expression(Expression::EK_Ap), fn(fn) { 254 | for (Atom *arg : argsref) args.push_back(arg); 255 | }; 256 | std::string getFnName() const { return fn; } 257 | 258 | void print(std::ostream &os, int nest=0) const; 259 | 260 | static bool classof(const Expression *E) { 261 | return E->getKind() == Expression::EK_Ap; 262 | } 263 | 264 | unsigned getNumParams() const { 265 | return args.size(); 266 | } 267 | 268 | private: 269 | Identifier fn; 270 | ParamsTy args; 271 | }; 272 | 273 | class ExpressionConstructor : public Expression { 274 | public: 275 | using ArgsTy = SmallVector; 276 | 277 | private: 278 | ConstructorName name; 279 | SmallVector args; 280 | 281 | public: 282 | using iterator = ArgsTy::iterator; 283 | using const_iterator = ArgsTy::const_iterator; 284 | 285 | iterator args_begin() { return args.begin(); } 286 | const_iterator args_begin() const { return args.begin(); } 287 | 288 | iterator args_end() { return args.end(); } 289 | const_iterator args_end() const { return args.end(); } 290 | 291 | iterator_range args_range() { 292 | return make_range(args_begin(), args_end()); 293 | } 294 | iterator_range args_range() const { 295 | return make_range(args_begin(), args_end()); 296 | } 297 | 298 | ExpressionConstructor(ConstructorName name, 299 | std::initializer_list args) 300 | : Expression(Expression::EK_Cons), name(name), args(args){}; 301 | 302 | ExpressionConstructor(ConstructorName name, ArrayRef argsref) 303 | : Expression(Expression::EK_Cons), name(name) { 304 | for (Atom *arg : argsref) args.push_back(arg); 305 | }; 306 | 307 | ConstructorName getName() const { return name; }; 308 | void print(std::ostream &os, int nest=0) const; 309 | static bool classof(const Expression *E) { 310 | return E->getKind() == Expression::EK_Cons; 311 | } 312 | }; 313 | 314 | class Binding; 315 | class ExpressionLet : public Expression { 316 | public: 317 | using BindingListTy = SmallVector; 318 | using iterator = BindingListTy::iterator; 319 | using const_iterator = BindingListTy::const_iterator; 320 | 321 | private: 322 | BindingListTy bindings; 323 | Expression *rhs; 324 | 325 | public: 326 | ExpressionLet(ArrayRef bindingsref, Expression *rhs) 327 | : Expression(Expression::EK_Let), rhs(rhs) { 328 | for (Binding *b : bindingsref) { 329 | bindings.push_back(b); 330 | } 331 | } 332 | void print(std::ostream &os, int nest=0) const; 333 | const Expression *getRHS() const { return rhs; } 334 | const_iterator begin() const { return bindings.begin(); } 335 | const_iterator end() const { return bindings.end(); } 336 | iterator_range bindings_range() const { 337 | return make_range(begin(), end()); 338 | } 339 | static bool classof(const Expression *E) { 340 | return E->getKind() == Expression::EK_Let; 341 | } 342 | }; 343 | class ExpressionIntLiteral : public Expression { 344 | private: 345 | int value; 346 | 347 | public: 348 | ExpressionIntLiteral(int value) 349 | : Expression(Expression::EK_IntLiteral), value(value) {} 350 | 351 | int getValue() const { return value; } 352 | 353 | static bool classof(const Expression *E) { 354 | return E->getKind() == Expression::EK_IntLiteral; 355 | } 356 | void print(std::ostream &os, int nest=0) const; 357 | }; 358 | 359 | // *** Alt *** 360 | class CaseAlt { 361 | public: 362 | Expression *getRHS() { return rhs; } 363 | const Expression *getRHS() const { return rhs; } 364 | 365 | friend std::ostream &operator<<(std::ostream &os, const CaseAlt &a); 366 | virtual void print(std::ostream &os, int nest=0) const = 0; 367 | 368 | enum CaseAltKind { CAK_Int, CAK_Variable, CAK_Destructure, CAK_Default }; 369 | CaseAltKind getKind() const { return kind; } 370 | 371 | private: 372 | CaseAltKind kind; 373 | 374 | protected: 375 | Expression *rhs; 376 | CaseAlt(CaseAltKind kind, Expression *rhs) : kind(kind), rhs(rhs){}; 377 | }; 378 | 379 | class CaseAltInt : public CaseAlt { 380 | AtomInt *lhs; 381 | 382 | public: 383 | CaseAltInt(AtomInt *lhs, Expression *rhs) 384 | : CaseAlt(CAK_Int, rhs), lhs(lhs){}; 385 | static bool classof(const CaseAlt *a) { 386 | return a->getKind() == CaseAlt::CAK_Int; 387 | } 388 | 389 | int getLHS() const { return lhs->getVal(); } 390 | 391 | void print(std::ostream &os, int nest=0) const; 392 | }; 393 | 394 | class CaseAltVariable : public CaseAlt { 395 | Identifier lhs; 396 | 397 | public: 398 | CaseAltVariable(Identifier lhs, Expression *rhs) 399 | : CaseAlt(CAK_Variable, rhs), lhs(lhs){}; 400 | static bool classof(const CaseAlt *a) { 401 | return a->getKind() == CaseAlt::CAK_Variable; 402 | } 403 | Identifier getLHS() const { return lhs; } 404 | void print(std::ostream &os, int nest=0) const; 405 | }; 406 | 407 | // case * of { __Constrcutor x y z -> f(x, y, z) } 408 | class CaseAltDestructure : public CaseAlt { 409 | public: 410 | using VariableList = SmallVector; 411 | using iterator = VariableList::iterator; 412 | using const_iterator = VariableList::const_iterator; 413 | 414 | private: 415 | ConstructorName constructorName; 416 | VariableList vars; 417 | 418 | public: 419 | CaseAltDestructure(ConstructorName constructorName, 420 | ArrayRef varsref, Expression *rhs) 421 | : CaseAlt(CAK_Destructure, rhs), constructorName(constructorName) { 422 | for (Identifier var : varsref) vars.push_back(var); 423 | } 424 | 425 | const_iterator begin() const { return vars.begin(); } 426 | const_iterator end() const { return vars.end(); } 427 | iterator_range variables_range() const { 428 | return make_range(begin(), end()); 429 | } 430 | 431 | size_t variables_size() const { return vars.size(); } 432 | static bool classof(const CaseAlt *a) { 433 | return a->getKind() == CaseAlt::CAK_Destructure; 434 | } 435 | void print(std::ostream &os, int nest=0) const; 436 | 437 | ConstructorName getConstructorName() const { return constructorName; } 438 | }; 439 | 440 | class CaseAltDefault : public CaseAlt { 441 | public: 442 | CaseAltDefault(Expression *rhs) : CaseAlt(CAK_Default, rhs){}; 443 | void print(std::ostream &os, int nest=0) const; 444 | static bool classof(const CaseAlt *a) { 445 | return a->getKind() == CaseAlt::CAK_Default; 446 | } 447 | }; 448 | 449 | // *** Case *** // 450 | class ExpressionCase : public Expression { 451 | public: 452 | using AltsList = SmallVector; 453 | 454 | private: 455 | Expression *scrutinee; 456 | AltsList alts; 457 | 458 | public: 459 | ExpressionCase(Expression *scrutinee, ArrayRef altsref) 460 | : Expression(Expression::EK_Case), scrutinee(scrutinee) { 461 | for (CaseAlt *alt : altsref) { 462 | alts.push_back(alt); 463 | } 464 | 465 | bool hasDefaultAlt = false; 466 | for (const CaseAlt *alt : this->alts) { 467 | if (isa(alt)) { 468 | if (hasDefaultAlt) { 469 | std::cerr << "invalid case expression:\n"; 470 | this->print(std::cerr, 0); 471 | } 472 | 473 | assert(!hasDefaultAlt && 474 | "Case expression has multiple default alts!"); 475 | hasDefaultAlt = true; 476 | } 477 | } 478 | 479 | bool hasVariableAlt = false; 480 | for (const CaseAlt *alt : this->alts) { 481 | if (isa(alt)) { 482 | if (hasVariableAlt) { 483 | std::cerr << "invalid case expression:\n"; 484 | this->print(std::cerr, 0); 485 | } 486 | assert(!hasVariableAlt && 487 | "Case expression has multiple variable alts!"); 488 | hasVariableAlt = true; 489 | } 490 | } 491 | 492 | if (hasDefaultAlt && hasVariableAlt) { 493 | assert( 494 | false && 495 | "case has *both* default *and* variable alt, cannot compile!"); 496 | } 497 | } 498 | 499 | void print(std::ostream &os, int nest=0) const; 500 | static bool classof(const Expression *E) { 501 | return E->getKind() == Expression::EK_Case; 502 | } 503 | 504 | const Expression *getScrutinee() const { return scrutinee; } 505 | 506 | using iterator = AltsList::iterator; 507 | using const_iterator = AltsList::const_iterator; 508 | 509 | const_iterator alts_begin() const { return alts.begin(); } 510 | const_iterator alts_end() const { return alts.end(); } 511 | size_t alts_size() const { return alts.size(); } 512 | 513 | iterator_range alts_range() const { 514 | return make_range(alts_begin(), alts_end()); 515 | } 516 | 517 | const CaseAltDefault *getDefaultAlt() const { 518 | for (const CaseAlt *alt : this->alts) { 519 | if (const CaseAltDefault *d = dyn_cast(alt)) { 520 | return d; 521 | } 522 | } 523 | return nullptr; 524 | } 525 | 526 | const CaseAltVariable *getVariableAlt() const { 527 | for (const CaseAlt *alt : this->alts) { 528 | if (const CaseAltVariable *v = dyn_cast(alt)) { 529 | return v; 530 | } 531 | } 532 | return nullptr; 533 | } 534 | }; 535 | 536 | // *** Parameter *** 537 | class Parameter { 538 | Identifier name; 539 | TypeRaw *type; 540 | 541 | public: 542 | Parameter(Identifier name, TypeRaw *type) : name(name), type(type){}; 543 | void print(std::ostream &os) const; 544 | friend std::ostream &operator<<(std::ostream &os, const Parameter &p); 545 | 546 | const TypeRaw *getTypeRaw() const { return type; } 547 | Identifier getName() const { return name; } 548 | }; 549 | 550 | // *** Lambda *** 551 | class Lambda { 552 | public: 553 | using ParamList = SmallVector; 554 | using iterator = ParamList::iterator; 555 | using const_iterator = ParamList::const_iterator; 556 | using reverse_iterator = ParamList::reverse_iterator; 557 | using const_reverse_iterator = ParamList::const_reverse_iterator; 558 | 559 | private: 560 | ParamList boundparams; 561 | ParamList freeparams; 562 | Expression *expr; 563 | TypeName returnType; 564 | 565 | public: 566 | Lambda(ArrayRef boundparamsref, TypeName returnType, 567 | Expression *expr) 568 | : expr(expr), returnType(returnType) { 569 | for (Parameter *p : boundparamsref) { 570 | boundparams.push_back(p); 571 | } 572 | } 573 | 574 | Lambda(ArrayRef freeparamsref, 575 | ArrayRef boundparamsref, TypeName returnType, 576 | Expression *expr) 577 | : expr(expr), returnType(returnType) { 578 | for (Parameter *p : freeparamsref) { 579 | freeparams.push_back(p); 580 | } 581 | for (Parameter *p : boundparamsref) { 582 | boundparams.push_back(p); 583 | } 584 | } 585 | void __attribute__((used)) dump() const { print(std::cout); }; 586 | void print(std::ostream &os, int nest=0) const; 587 | friend std::ostream &operator<<(std::ostream &os, const Lambda &l); 588 | 589 | const Expression *getRhs() const { return expr; } 590 | 591 | const_iterator bound_params_begin() const { return boundparams.begin(); } 592 | const_iterator bound_params_end() const { return boundparams.end(); } 593 | unsigned bound_params_size() const { return boundparams.size(); } 594 | 595 | iterator_range bound_params_range() const { 596 | return make_range(bound_params_begin(), bound_params_end()); 597 | } 598 | 599 | const_iterator free_params_begin() const { return freeparams.begin(); } 600 | const_iterator free_params_end() const { return freeparams.end(); } 601 | unsigned free_params_size() const { return freeparams.size(); } 602 | 603 | iterator_range free_params_range() const { 604 | return make_range(free_params_begin(), free_params_end()); 605 | } 606 | 607 | ArrayRef free_params_ref() const { return freeparams; } 608 | 609 | iterator_range free_params_reverse_range() const { 610 | return make_range(free_params_end(), free_params_begin()); 611 | } 612 | 613 | std::string getReturnTypeName() const { return this->returnType; } 614 | }; 615 | 616 | // *** Binding *** 617 | class Binding { 618 | Identifier lhs; 619 | Lambda *rhs; 620 | 621 | public: 622 | Binding(Identifier lhs, Lambda *rhs) : lhs(lhs), rhs(rhs){}; 623 | friend std::ostream &operator<<(std::ostream &os, const Binding &b); 624 | Identifier getName() const { return lhs; } 625 | const Lambda *getRhs() const { return rhs; } 626 | void print(std::ostream &os, int nest=0) const; 627 | }; 628 | 629 | // *** Program *** 630 | class Program { 631 | public: 632 | Program(ArrayRef bs, ArrayRef ds) { 633 | for (Binding *b : bs) bindings.push_back(b); 634 | for (DataType *d : ds) datatypes.push_back(d); 635 | }; 636 | 637 | using DataTypeList = SmallVector; 638 | using BindingList = SmallVector; 639 | 640 | using binding_iterator = BindingList::iterator; 641 | using datatype_iterator = DataTypeList::iterator; 642 | 643 | friend std::ostream &operator<<(std::ostream &os, const Program &p); 644 | 645 | binding_iterator bindings_begin() { return bindings.begin(); } 646 | binding_iterator bindings_end() { return bindings.end(); } 647 | 648 | iterator_range bindings_range() { 649 | return make_range(bindings_begin(), bindings_end()); 650 | } 651 | 652 | datatype_iterator datatypes_begin() { return datatypes.begin(); } 653 | datatype_iterator datatypes_end() { return datatypes.end(); } 654 | 655 | iterator_range datatypes_range() { 656 | return make_range(datatypes_begin(), datatypes_end()); 657 | } 658 | 659 | private: 660 | BindingList bindings; 661 | DataTypeList datatypes; 662 | }; 663 | 664 | } // end namespace stg. 665 | -------------------------------------------------------------------------------- /docs/customdoxygen.css: -------------------------------------------------------------------------------- 1 | /* The standard CSS for doxygen 1.8.4 */ 2 | 3 | body, table, div, p, dl { 4 | // font: 400 14px/22px Roboto,sans-serif; 5 | font: 13px "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif; 6 | } 7 | 8 | /* @group Heading Levels */ 9 | 10 | h1.groupheader { 11 | font-size: 28px; 12 | //font-size: 150%; 13 | } 14 | 15 | .title { 16 | //font: 400 14px/28px Roboto,sans-serif; 17 | font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif; 18 | font-size: 24px; 19 | font-weight: normal; 20 | margin: 10px 2px; 21 | } 22 | 23 | h2.groupheader { 24 | //border-bottom: 1px solid #879ECB; 25 | border-bottom: none; 26 | //color: #354C7B; 27 | color: rgb(60, 76, 108); 28 | font-size: 24px; 29 | font-weight: normal; 30 | //margin-top: 1.75em; 31 | margin: 42px 0px 20px 0px; 32 | margin-bottom: 20px; 33 | //padding-top: 8px; 34 | //padding-bottom: 4px; 35 | padding: 0px; 36 | width: 100%; 37 | } 38 | 39 | h3.groupheader { 40 | font-size: 100%; 41 | } 42 | 43 | h1, h2, h3, h4, h5, h6 { 44 | -webkit-transition: text-shadow 0.5s linear; 45 | -moz-transition: text-shadow 0.5s linear; 46 | -ms-transition: text-shadow 0.5s linear; 47 | -o-transition: text-shadow 0.5s linear; 48 | transition: text-shadow 0.5s linear; 49 | //margin-right: 15px; 50 | //margin: 42px 0px 20px 0px; 51 | } 52 | 53 | h1 { 54 | font-size: 28px; 55 | } 56 | 57 | h2 { 58 | color: rgb(60, 76, 108); 59 | font-size: 24px; 60 | font-weight: normal; 61 | margin: 42px 0px 20px 0px; 62 | } 63 | 64 | h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { 65 | text-shadow: 0 0 15px cyan; 66 | } 67 | 68 | dt { 69 | font-weight: bold; 70 | } 71 | 72 | div.multicol { 73 | -moz-column-gap: 1em; 74 | -webkit-column-gap: 1em; 75 | -moz-column-count: 3; 76 | -webkit-column-count: 3; 77 | } 78 | 79 | p.startli, p.startdd, p.starttd { 80 | margin-top: 2px; 81 | } 82 | 83 | p.endli { 84 | margin-bottom: 0px; 85 | } 86 | 87 | p.enddd { 88 | margin-bottom: 4px; 89 | } 90 | 91 | p.endtd { 92 | margin-bottom: 2px; 93 | } 94 | 95 | /* @end */ 96 | 97 | caption { 98 | font-weight: bold; 99 | } 100 | 101 | span.legend { 102 | font-size: 70%; 103 | text-align: center; 104 | } 105 | 106 | h3.version { 107 | font-size: 90%; 108 | text-align: center; 109 | } 110 | 111 | div.qindex, div.navtab{ 112 | background-color: #EBEFF6; 113 | border: 1px solid #A3B4D7; 114 | text-align: center; 115 | } 116 | 117 | div.qindex, div.navpath { 118 | width: 100%; 119 | line-height: 140%; 120 | } 121 | 122 | div.navtab { 123 | margin-right: 15px; 124 | } 125 | 126 | /* @group Link Styling */ 127 | 128 | a { 129 | //color: #3D578C; 130 | color: rgb(51, 102, 204); 131 | font-weight: normal; 132 | text-decoration: none; 133 | } 134 | 135 | .contents a:visited { 136 | //color: #4665A2; 137 | color: rgb(51, 102, 204); 138 | } 139 | 140 | a:hover { 141 | text-decoration: underline; 142 | } 143 | 144 | a.qindex { 145 | font-weight: bold; 146 | } 147 | 148 | a.qindexHL { 149 | font-weight: bold; 150 | background-color: #9CAFD4; 151 | color: #ffffff; 152 | border: 1px double #869DCA; 153 | } 154 | 155 | .contents a.qindexHL:visited { 156 | color: #ffffff; 157 | } 158 | 159 | a.el { 160 | //font-family: "Courier New", courier, monospace; 161 | font-family: Courier, Consolas, monospace; 162 | //font-weight: bold; 163 | font-weight: normal; 164 | } 165 | 166 | a.elRef { 167 | } 168 | 169 | a.code, a.code:visited { 170 | //color: #4665A2; 171 | color: rgb(51, 102, 204); 172 | } 173 | 174 | a.codeRef, a.codeRef:visited { 175 | //color: #4665A2; 176 | color: rgb(51, 102, 204); 177 | } 178 | 179 | /* @end */ 180 | 181 | dl.el { 182 | margin-left: -1cm; 183 | } 184 | 185 | pre.fragment { 186 | //border: 1px solid #C4CFE5; 187 | border: solid 1px rgb(221, 221, 221); 188 | border-radius: 3px; 189 | //background-color: #FBFCFD; 190 | background-color: rgb(248, 248, 248); 191 | //padding: 4px 6px; 192 | padding: 6px 10px; 193 | //margin: 4px 8px 4px 2px; 194 | margin: 15px 0px; 195 | overflow: auto; 196 | word-wrap: break-word; 197 | font-size: 9pt; 198 | line-height: 125%; 199 | //font-family: monospace, fixed; 200 | font-family: Consolas, "Liberation Mono", Courier, monospace; 201 | font-size: 105%; 202 | } 203 | 204 | div.fragment { 205 | //padding: 0px; 206 | //padding: 4px 6px; 207 | //margin: 0px; 208 | //background-color: #FBFCFD; 209 | //border: 1px solid #C4CFE5; 210 | 211 | padding: 6px 10px; 212 | margin: 15px 0px; 213 | border: solid 1px rgb(221, 221, 221); 214 | border-radius: 3px; 215 | 216 | background-color: rgb(248, 248, 248); 217 | } 218 | 219 | div.line { 220 | //font-family: monospace, fixed; 221 | font-family: Consolas, "Liberation Mono", Courier, monospace; 222 | font-size: 13px; 223 | min-height: 13px; 224 | line-height: 1.0; 225 | text-wrap: unrestricted; 226 | white-space: -moz-pre-wrap; /* Moz */ 227 | white-space: -pre-wrap; /* Opera 4-6 */ 228 | white-space: -o-pre-wrap; /* Opera 7 */ 229 | white-space: pre-wrap; /* CSS3 */ 230 | word-wrap: break-word; /* IE 5.5+ */ 231 | text-indent: -53px; 232 | padding-left: 53px; 233 | padding-bottom: 0px; 234 | margin: 0px; 235 | -webkit-transition-property: background-color, box-shadow; 236 | -webkit-transition-duration: 0.5s; 237 | -moz-transition-property: background-color, box-shadow; 238 | -moz-transition-duration: 0.5s; 239 | -ms-transition-property: background-color, box-shadow; 240 | -ms-transition-duration: 0.5s; 241 | -o-transition-property: background-color, box-shadow; 242 | -o-transition-duration: 0.5s; 243 | transition-property: background-color, box-shadow; 244 | transition-duration: 0.5s; 245 | } 246 | 247 | div.line.glow { 248 | background-color: cyan; 249 | box-shadow: 0 0 10px cyan; 250 | } 251 | 252 | 253 | span.lineno { 254 | padding-right: 4px; 255 | text-align: right; 256 | border-right: 2px solid #0F0; 257 | background-color: #E8E8E8; 258 | white-space: pre; 259 | } 260 | span.lineno a { 261 | background-color: #D8D8D8; 262 | } 263 | 264 | span.lineno a:hover { 265 | background-color: #C8C8C8; 266 | } 267 | 268 | div.ah { 269 | background-color: black; 270 | font-weight: bold; 271 | color: #ffffff; 272 | margin-bottom: 3px; 273 | margin-top: 3px; 274 | padding: 0.2em; 275 | border: solid thin #333; 276 | border-radius: 0.5em; 277 | -webkit-border-radius: .5em; 278 | -moz-border-radius: .5em; 279 | box-shadow: 2px 2px 3px #999; 280 | -webkit-box-shadow: 2px 2px 3px #999; 281 | -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; 282 | background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); 283 | background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); 284 | } 285 | 286 | div.groupHeader { 287 | //margin-left: 16px; 288 | //margin-top: 12px; 289 | margin-left: 0px; 290 | margin-top: 9px; 291 | margin-bottom: 4.7px; 292 | 293 | font-size: 19px; 294 | //font-weight: bold; 295 | font-weight: normal; 296 | } 297 | 298 | div.groupText { 299 | margin-left: 16px; 300 | font-style: italic; 301 | } 302 | 303 | body { 304 | background-color: white; 305 | color: black; 306 | margin: 0; 307 | } 308 | 309 | div.contents { 310 | margin-top: 10px; 311 | margin-left: 12px; 312 | margin-right: 8px; 313 | } 314 | 315 | td.indexkey { 316 | background-color: #EBEFF6; 317 | font-weight: bold; 318 | border: 1px solid #C4CFE5; 319 | margin: 2px 0px 2px 0; 320 | padding: 2px 10px; 321 | white-space: nowrap; 322 | vertical-align: top; 323 | } 324 | 325 | td.indexvalue { 326 | background-color: #EBEFF6; 327 | border: 1px solid #C4CFE5; 328 | padding: 2px 10px; 329 | margin: 2px 0px; 330 | } 331 | 332 | tr.memlist { 333 | background-color: #EEF1F7; 334 | } 335 | 336 | p.formulaDsp { 337 | text-align: center; 338 | } 339 | 340 | img.formulaDsp { 341 | 342 | } 343 | 344 | img.formulaInl { 345 | vertical-align: middle; 346 | } 347 | 348 | div.center { 349 | text-align: center; 350 | margin-top: 0px; 351 | margin-bottom: 0px; 352 | padding: 0px; 353 | } 354 | 355 | div.center img { 356 | border: 0px; 357 | } 358 | 359 | address.footer { 360 | text-align: right; 361 | padding-right: 12px; 362 | } 363 | 364 | img.footer { 365 | border: 0px; 366 | vertical-align: middle; 367 | } 368 | 369 | /* @group Code Colorization */ 370 | 371 | span.keyword { 372 | color: #008000 373 | } 374 | 375 | span.keywordtype { 376 | color: #604020 377 | } 378 | 379 | span.keywordflow { 380 | color: #e08000 381 | } 382 | 383 | span.comment { 384 | color: #800000 385 | } 386 | 387 | span.preprocessor { 388 | color: #806020 389 | } 390 | 391 | span.stringliteral { 392 | color: #002080 393 | } 394 | 395 | span.charliteral { 396 | color: #008080 397 | } 398 | 399 | span.vhdldigit { 400 | color: #ff00ff 401 | } 402 | 403 | span.vhdlchar { 404 | color: #000000 405 | } 406 | 407 | span.vhdlkeyword { 408 | color: #700070 409 | } 410 | 411 | span.vhdllogic { 412 | color: #ff0000 413 | } 414 | 415 | blockquote { 416 | background-color: #F7F8FB; 417 | border-left: 2px solid #9CAFD4; 418 | margin: 0 24px 0 4px; 419 | padding: 0 12px 0 16px; 420 | } 421 | 422 | /* @end */ 423 | 424 | /* 425 | .search { 426 | color: #003399; 427 | font-weight: bold; 428 | } 429 | 430 | form.search { 431 | margin-bottom: 0px; 432 | margin-top: 0px; 433 | } 434 | 435 | input.search { 436 | font-size: 75%; 437 | color: #000080; 438 | font-weight: normal; 439 | background-color: #e8eef2; 440 | } 441 | */ 442 | 443 | td.tiny { 444 | font-size: 75%; 445 | } 446 | 447 | .dirtab { 448 | padding: 4px; 449 | border-collapse: collapse; 450 | border: 1px solid #A3B4D7; 451 | } 452 | 453 | th.dirtab { 454 | background: #EBEFF6; 455 | font-weight: bold; 456 | } 457 | 458 | hr { 459 | height: 0px; 460 | border: none; 461 | //border-top: 1px solid #4A6AAA; 462 | border-top: 1px solid #444; 463 | } 464 | 465 | hr.footer { 466 | height: 0px; 467 | border-top: 3px solid #444; 468 | } 469 | 470 | /* @group Member Descriptions */ 471 | 472 | table.memberdecls { 473 | border-spacing: 0px; 474 | padding: 0px; 475 | } 476 | 477 | .memberdecls td, .fieldtable tr { 478 | -webkit-transition-property: background-color, box-shadow; 479 | -webkit-transition-duration: 0.5s; 480 | -moz-transition-property: background-color, box-shadow; 481 | -moz-transition-duration: 0.5s; 482 | -ms-transition-property: background-color, box-shadow; 483 | -ms-transition-duration: 0.5s; 484 | -o-transition-property: background-color, box-shadow; 485 | -o-transition-duration: 0.5s; 486 | transition-property: background-color, box-shadow; 487 | transition-duration: 0.5s; 488 | } 489 | 490 | .memberdecls td.glow, .fieldtable tr.glow { 491 | background-color: cyan; 492 | box-shadow: 0 0 15px cyan; 493 | } 494 | 495 | .mdescLeft, .mdescRight, 496 | .memItemLeft, .memItemRight, 497 | .memTemplItemLeft, .memTemplItemRight, .memTemplParams { 498 | //background-color: #F9FAFC; 499 | background-color: white; 500 | border: none; 501 | margin: 4px; 502 | padding: 1px 0 0 8px; 503 | 504 | //font-family: "Courier New", courier, monospace; 505 | font-family: Courier, Consolas, monospace; 506 | } 507 | 508 | .mdescLeft, .mdescRight { 509 | //padding: 0px 8px 4px 8px; 510 | padding: 0px 8px 4px 24px; 511 | //color: #555; 512 | color: black; 513 | 514 | font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif; 515 | //font-style: italic; 516 | font-style: normal; 517 | } 518 | 519 | .memSeparator { 520 | //border-bottom: 1px solid #DEE4F0; 521 | border-bottom: none; 522 | line-height: 8px; 523 | margin: 0px; 524 | padding: 0px; 525 | } 526 | 527 | .memItemLeft, .memTemplItemLeft { 528 | white-space: nowrap; 529 | } 530 | 531 | .memItemRight { 532 | width: 100%; 533 | } 534 | 535 | .memTemplParams { 536 | color: #4665A2; 537 | white-space: nowrap; 538 | font-size: 80%; 539 | } 540 | 541 | /* @end */ 542 | 543 | /* @group Member Details */ 544 | 545 | /* Styles for detailed member documentation */ 546 | 547 | .memtemplate { 548 | font-family: Courier, Consolas, monospace; 549 | font-size: 100%; 550 | //font-size: 80%; 551 | //color: #4665A2; 552 | color: black; 553 | font-weight: normal; 554 | //margin-left: 9px; 555 | margin-left: 1px; 556 | } 557 | 558 | .memnav { 559 | background-color: #EBEFF6; 560 | border: 1px solid #A3B4D7; 561 | text-align: center; 562 | margin: 2px; 563 | margin-right: 15px; 564 | padding: 2px; 565 | } 566 | 567 | .mempage { 568 | width: 100%; 569 | } 570 | 571 | .memitem { 572 | padding: 0; 573 | margin-bottom: 10px; 574 | margin-right: 5px; 575 | -webkit-transition: box-shadow 0.5s linear; 576 | -moz-transition: box-shadow 0.5s linear; 577 | -ms-transition: box-shadow 0.5s linear; 578 | -o-transition: box-shadow 0.5s linear; 579 | transition: box-shadow 0.5s linear; 580 | display: table !important; 581 | width: 100%; 582 | } 583 | 584 | .memitem.glow { 585 | box-shadow: 0 0 15px cyan; 586 | } 587 | 588 | .memname { 589 | //font-family: "Courier New", courier, monospace; 590 | //font-family: Courier, Consolas, monospace; 591 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif; 592 | font-weight: 400; 593 | //font-size: 15px; 594 | font-size: 19px; 595 | //font-weight: bold; 596 | //margin-left: 6px; 597 | margin-left: 0px; 598 | } 599 | 600 | .memname td { 601 | vertical-align: bottom; 602 | } 603 | 604 | .memproto, dl.reflist dt { 605 | margin-top: 1.5em; 606 | border: none; 607 | //border-top: 1px solid #ccc; 608 | //border-top: 1px solid #A8B8D9; 609 | //border-left: 1px solid #A8B8D9; 610 | //border-right: 1px solid #A8B8D9; 611 | border-bottom: 1px solid rgb(172, 172, 172); 612 | //padding: 6px 0px 6px 0px; 613 | //padding: 6px; 614 | padding: 0px; 615 | //color: #253555; 616 | color: black; 617 | //font-family: Courier, Consolas, monospace; 618 | font-weight: bold; 619 | //text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); 620 | //text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 621 | text-shadow: none; 622 | //background-image:url('nav_f.png'); 623 | background-image: none; 624 | //background-repeat:repeat-x; 625 | //background-color: #E2E8F2; 626 | background-color: rgb(235, 238, 241); 627 | //background-color: #ccc; 628 | //background-color: white; 629 | /* opera specific markup */ 630 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); 631 | //border-top-right-radius: 4px; 632 | //border-top-left-radius: 4px; 633 | border-top-right-radius: 0px; 634 | border-top-left-radius: 0px; 635 | /* firefox specific markup */ 636 | //-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; 637 | //-moz-border-radius-topright: 4px; 638 | //-moz-border-radius-topleft: 4px; 639 | -moz-box-shadow: none; 640 | -moz-border-radius-topright: 0px; 641 | -moz-border-radius-topleft: 0px; 642 | /* webkit specific markup */ 643 | //-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); 644 | //-webkit-border-top-right-radius: 4px; 645 | //-webkit-border-top-left-radius: 4px; 646 | -webkit-box-shadow: none; 647 | -webkit-border-top-right-radius: 0px; 648 | -webkit-border-top-left-radius: 0px; 649 | } 650 | 651 | .memdoc, dl.reflist dd { 652 | border: none; 653 | //border-bottom: 1px solid #A8B8D9; 654 | //border-left: 1px solid #A8B8D9; 655 | //border-right: 1px solid #A8B8D9; 656 | padding: 6px; 657 | background-color: #FBFCFD; 658 | border-top-width: 0; 659 | //background-image:url('nav_g.png'); 660 | background-image: none; 661 | //background-repeat:repeat-x; 662 | background-color: #FFFFFF; 663 | /* opera specific markup */ 664 | //border-bottom-left-radius: 4px; 665 | //border-bottom-right-radius: 4px; 666 | //box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); 667 | border-bottom-left-radius: 0px; 668 | border-bottom-right-radius: 0px; 669 | box-shadow: none; 670 | /* firefox specific markup */ 671 | //-moz-border-radius-bottomleft: 4px; 672 | //-moz-border-radius-bottomright: 4px; 673 | //-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; 674 | -moz-border-radius-bottomleft: 0px; 675 | -moz-border-radius-bottomright: 0px; 676 | -moz-box-shadow: none; 677 | /* webkit specific markup */ 678 | //-webkit-border-bottom-left-radius: 4px; 679 | //-webkit-border-bottom-right-radius: 4px; 680 | //-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); 681 | -webkit-border-bottom-left-radius: 0px; 682 | -webkit-border-bottom-right-radius: 0px; 683 | -webkit-box-shadow: none; 684 | } 685 | 686 | dl.reflist dt { 687 | padding: 5px; 688 | } 689 | 690 | dl.reflist dd { 691 | margin: 0px 0px 10px 0px; 692 | padding: 5px; 693 | } 694 | 695 | .paramkey { 696 | text-align: right; 697 | } 698 | 699 | .paramtype { 700 | white-space: nowrap; 701 | } 702 | 703 | .paramname { 704 | //color: #602020; 705 | color: rgb(37, 53, 85); 706 | white-space: nowrap; 707 | } 708 | .paramname em { 709 | font-style: italic; 710 | font-weight: normal; 711 | //font-style: normal; 712 | } 713 | .paramname code { 714 | line-height: 14px; 715 | } 716 | 717 | .params, .retval, .exception, .tparams { 718 | margin-left: 0px; 719 | padding-left: 0px; 720 | } 721 | 722 | .params .paramname, .retval .paramname { 723 | //font-weight: bold; 724 | //font-family: "Courier New", courier, monospace; 725 | font-family: Courier, Consolas, monospace; 726 | font-style: italic; 727 | font-weight: normal; 728 | text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 1px; 729 | } 730 | 731 | .params .paramtype { 732 | font-style: italic; 733 | vertical-align: top; 734 | } 735 | 736 | .params .paramdir { 737 | font-family: "courier new",courier,monospace; 738 | vertical-align: top; 739 | } 740 | 741 | table.mlabels { 742 | border-spacing: 0px; 743 | } 744 | 745 | td.mlabels-left { 746 | width: 100%; 747 | padding: 0px; 748 | } 749 | 750 | td.mlabels-right { 751 | vertical-align: middle; 752 | padding: 0px; 753 | white-space: nowrap; 754 | } 755 | 756 | span.mlabels { 757 | margin-left: 8px; 758 | } 759 | 760 | span.mlabel { 761 | //background-color: #728DC1; 762 | background-color: rgb(172, 172, 172);; 763 | //border-top:1px solid #5373B4; 764 | //border-left:1px solid #5373B4; 765 | //border-right:1px solid #C4CFE5; 766 | //border-bottom:1px solid #C4CFE5; 767 | border: none; 768 | text-shadow: none; 769 | color: white; 770 | margin-right: 4px; 771 | padding: 2px 3px; 772 | //padding: 4px 8px; 773 | //border-radius: 3px; 774 | border-radius: 4px; 775 | //font-size: 7pt; 776 | font-size: 9pt; 777 | white-space: nowrap; 778 | vertical-align: middle; 779 | } 780 | 781 | 782 | 783 | /* @end */ 784 | 785 | /* these are for tree view when not used as main index */ 786 | 787 | div.directory { 788 | margin: 10px 0px; 789 | border-top: 1px solid #A8B8D9; 790 | border-bottom: 1px solid #A8B8D9; 791 | width: 100%; 792 | } 793 | 794 | .directory table { 795 | border-collapse:collapse; 796 | } 797 | 798 | .directory td { 799 | margin: 0px; 800 | padding: 0px; 801 | vertical-align: top; 802 | } 803 | 804 | .directory td.entry { 805 | white-space: nowrap; 806 | padding-right: 6px; 807 | padding-top: 3px; 808 | } 809 | 810 | .directory td.entry a { 811 | outline:none; 812 | } 813 | 814 | .directory td.entry a img { 815 | border: none; 816 | } 817 | 818 | .directory td.desc { 819 | width: 100%; 820 | padding-left: 6px; 821 | padding-right: 6px; 822 | padding-top: 3px; 823 | border-left: 1px solid rgba(0,0,0,0.05); 824 | } 825 | 826 | .directory tr.even { 827 | padding-left: 6px; 828 | background-color: #F7F8FB; 829 | } 830 | 831 | .directory img { 832 | vertical-align: -30%; 833 | } 834 | 835 | .directory .levels { 836 | white-space: nowrap; 837 | width: 100%; 838 | text-align: right; 839 | font-size: 9pt; 840 | } 841 | 842 | .directory .levels span { 843 | cursor: pointer; 844 | padding-left: 2px; 845 | padding-right: 2px; 846 | color: #3D578C; 847 | } 848 | 849 | div.dynheader { 850 | margin-top: 8px; 851 | -webkit-touch-callout: none; 852 | -webkit-user-select: none; 853 | -khtml-user-select: none; 854 | -moz-user-select: none; 855 | -ms-user-select: none; 856 | user-select: none; 857 | } 858 | 859 | address { 860 | font-style: normal; 861 | color: #2A3D61; 862 | } 863 | 864 | table.doxtable { 865 | border-collapse:collapse; 866 | margin-top: 4px; 867 | margin-bottom: 4px; 868 | } 869 | 870 | table.doxtable td, table.doxtable th { 871 | border: 1px solid #2D4068; 872 | padding: 3px 7px 2px; 873 | } 874 | 875 | table.doxtable th { 876 | background-color: #374F7F; 877 | color: #FFFFFF; 878 | font-size: 110%; 879 | padding-bottom: 4px; 880 | padding-top: 5px; 881 | } 882 | 883 | table.fieldtable { 884 | /*width: 100%;*/ 885 | margin-bottom: 10px; 886 | border: 1px solid #A8B8D9; 887 | border-spacing: 0px; 888 | -moz-border-radius: 4px; 889 | -webkit-border-radius: 4px; 890 | border-radius: 4px; 891 | -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; 892 | -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); 893 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); 894 | } 895 | 896 | .fieldtable td, .fieldtable th { 897 | padding: 3px 7px 2px; 898 | } 899 | 900 | .fieldtable td.fieldtype, .fieldtable td.fieldname { 901 | white-space: nowrap; 902 | border-right: 1px solid #A8B8D9; 903 | border-bottom: 1px solid #A8B8D9; 904 | vertical-align: top; 905 | } 906 | 907 | .fieldtable td.fieldname { 908 | padding-top: 3px; 909 | } 910 | 911 | .fieldtable td.fielddoc { 912 | border-bottom: 1px solid #A8B8D9; 913 | /*width: 100%;*/ 914 | } 915 | 916 | .fieldtable td.fielddoc p:first-child { 917 | margin-top: 0px; 918 | } 919 | 920 | .fieldtable td.fielddoc p:last-child { 921 | margin-bottom: 2px; 922 | } 923 | 924 | .fieldtable tr:last-child td { 925 | border-bottom: none; 926 | } 927 | 928 | .fieldtable th { 929 | background-image:url('nav_f.png'); 930 | background-repeat:repeat-x; 931 | background-color: #E2E8F2; 932 | font-size: 90%; 933 | color: #253555; 934 | padding-bottom: 4px; 935 | padding-top: 5px; 936 | text-align:left; 937 | -moz-border-radius-topleft: 4px; 938 | -moz-border-radius-topright: 4px; 939 | -webkit-border-top-left-radius: 4px; 940 | -webkit-border-top-right-radius: 4px; 941 | border-top-left-radius: 4px; 942 | border-top-right-radius: 4px; 943 | border-bottom: 1px solid #A8B8D9; 944 | } 945 | 946 | 947 | .tabsearch { 948 | top: 0px; 949 | left: 10px; 950 | height: 36px; 951 | background-image: url('tab_b.png'); 952 | z-index: 101; 953 | overflow: hidden; 954 | font-size: 13px; 955 | } 956 | 957 | .navpath ul 958 | { 959 | font-size: 11px; 960 | background-image:url('tab_b.png'); 961 | background-repeat:repeat-x; 962 | background-position: 0 -5px; 963 | height:30px; 964 | line-height:30px; 965 | color:#8AA0CC; 966 | border:solid 1px #C2CDE4; 967 | overflow:hidden; 968 | margin:0px; 969 | padding:0px; 970 | } 971 | 972 | .navpath li 973 | { 974 | list-style-type:none; 975 | float:left; 976 | padding-left:10px; 977 | padding-right:15px; 978 | background-image:url('bc_s.png'); 979 | background-repeat:no-repeat; 980 | background-position:right; 981 | color:#364D7C; 982 | } 983 | 984 | .navpath li.navelem a 985 | { 986 | height:32px; 987 | display:block; 988 | text-decoration: none; 989 | outline: none; 990 | color: #283A5D; 991 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 992 | text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); 993 | text-decoration: none; 994 | } 995 | 996 | .navpath li.navelem a:hover 997 | { 998 | color:#6884BD; 999 | } 1000 | 1001 | .navpath li.footer 1002 | { 1003 | list-style-type:none; 1004 | float:right; 1005 | padding-left:10px; 1006 | padding-right:15px; 1007 | background-image:none; 1008 | background-repeat:no-repeat; 1009 | background-position:right; 1010 | color:#364D7C; 1011 | font-size: 8pt; 1012 | } 1013 | 1014 | 1015 | div.summary 1016 | { 1017 | float: right; 1018 | font-size: 8pt; 1019 | padding-right: 5px; 1020 | width: 50%; 1021 | text-align: right; 1022 | } 1023 | 1024 | div.summary a 1025 | { 1026 | white-space: nowrap; 1027 | } 1028 | 1029 | div.ingroups 1030 | { 1031 | font-size: 8pt; 1032 | width: 50%; 1033 | text-align: left; 1034 | } 1035 | 1036 | div.ingroups a 1037 | { 1038 | white-space: nowrap; 1039 | } 1040 | 1041 | div.header 1042 | { 1043 | //background-image:url('nav_h.png'); 1044 | //background-repeat:repeat-x; 1045 | //background-color: #F9FAFC; 1046 | background-image: none; 1047 | background-color: white; 1048 | margin: 0px; 1049 | //border-bottom: 1px solid #C4CFE5; 1050 | border: none; 1051 | } 1052 | 1053 | div.headertitle 1054 | { 1055 | padding: 5px 5px 5px 10px; 1056 | } 1057 | 1058 | dl 1059 | { 1060 | padding: 0 0 0 10px; 1061 | } 1062 | 1063 | /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ 1064 | dl.section 1065 | { 1066 | margin-left: 0px; 1067 | padding-left: 0px; 1068 | } 1069 | 1070 | dl.note 1071 | { 1072 | //margin-left:-7px; 1073 | margin-left: 0px; 1074 | padding: 6px 0px 3px 8px; 1075 | //padding-left: 8px; 1076 | border-left: 6px solid; 1077 | border-color: #D0C000; 1078 | background-color: #fff799 1079 | } 1080 | 1081 | dl.warning, dl.attention 1082 | { 1083 | //margin-left:-7px; 1084 | //padding-left: 3px; 1085 | margin-left: 0px; 1086 | padding: 6px 0px 3px 8px; 1087 | 1088 | //border-left:4px solid; 1089 | border-left: 6px solid; 1090 | border-color: #FF0000; 1091 | } 1092 | 1093 | dl.pre, dl.post, dl.invariant 1094 | { 1095 | margin-left:-7px; 1096 | padding-left: 3px; 1097 | border-left:4px solid; 1098 | border-color: #00D000; 1099 | } 1100 | 1101 | dl.deprecated 1102 | { 1103 | //margin-left:-7px; 1104 | //padding-left: 3px; 1105 | margin-left: 0px; 1106 | padding: 6px 0px 3px 8px; 1107 | //border-left: 4px solid; 1108 | border-left: 6px solid; 1109 | border-color: #505050; 1110 | } 1111 | 1112 | dl.deprecated dt a.el 1113 | { 1114 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 1115 | } 1116 | 1117 | dl.todo 1118 | { 1119 | //margin-left:-7px; 1120 | //padding-left: 3px; 1121 | margin-left: 0px; 1122 | padding: 6px 0px 3px 8px; 1123 | border-left:4px solid; 1124 | border-color: #00C0E0; 1125 | } 1126 | 1127 | dl.test 1128 | { 1129 | margin-left:-7px; 1130 | padding-left: 3px; 1131 | border-left:4px solid; 1132 | border-color: #3030E0; 1133 | } 1134 | 1135 | dl.bug 1136 | { 1137 | margin-left:-7px; 1138 | padding-left: 3px; 1139 | border-left:4px solid; 1140 | border-color: #C08050; 1141 | } 1142 | 1143 | dl.section dd { 1144 | margin-bottom: 6px; 1145 | } 1146 | 1147 | 1148 | #projectlogo 1149 | { 1150 | text-align: center; 1151 | vertical-align: bottom; 1152 | border-collapse: separate; 1153 | } 1154 | 1155 | #projectlogo img 1156 | { 1157 | border: 0px none; 1158 | } 1159 | 1160 | #projectname 1161 | { 1162 | font: 300% Tahoma, Arial,sans-serif; 1163 | margin: 0px; 1164 | padding: 2px 0px; 1165 | } 1166 | 1167 | #projectbrief 1168 | { 1169 | font: 120% Tahoma, Arial,sans-serif; 1170 | margin: 0px; 1171 | padding: 0px; 1172 | } 1173 | 1174 | #projectnumber 1175 | { 1176 | font: 50% Tahoma, Arial,sans-serif; 1177 | margin: 0px; 1178 | padding: 0px; 1179 | } 1180 | 1181 | #titlearea 1182 | { 1183 | padding: 0px; 1184 | margin: 0px; 1185 | width: 100%; 1186 | border-bottom: 1px solid #5373B4; 1187 | } 1188 | 1189 | .image 1190 | { 1191 | text-align: center; 1192 | } 1193 | 1194 | .dotgraph 1195 | { 1196 | text-align: center; 1197 | } 1198 | 1199 | .mscgraph 1200 | { 1201 | text-align: center; 1202 | } 1203 | 1204 | .caption 1205 | { 1206 | font-weight: bold; 1207 | } 1208 | 1209 | div.zoom 1210 | { 1211 | border: 1px solid #90A5CE; 1212 | } 1213 | 1214 | dl.citelist { 1215 | margin-bottom:50px; 1216 | } 1217 | 1218 | dl.citelist dt { 1219 | color:#334975; 1220 | float:left; 1221 | font-weight:bold; 1222 | margin-right:10px; 1223 | padding:5px; 1224 | } 1225 | 1226 | dl.citelist dd { 1227 | margin:2px 0; 1228 | padding:5px 0; 1229 | } 1230 | 1231 | div.toc { 1232 | padding: 14px 25px; 1233 | background-color: #F4F6FA; 1234 | border: 1px solid #D8DFEE; 1235 | border-radius: 7px 7px 7px 7px; 1236 | float: right; 1237 | height: auto; 1238 | margin: 0 20px 10px 10px; 1239 | width: 200px; 1240 | } 1241 | 1242 | div.toc li { 1243 | background: url("bdwn.png") no-repeat scroll 0 5px transparent; 1244 | font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; 1245 | margin-top: 5px; 1246 | padding-left: 10px; 1247 | padding-top: 2px; 1248 | } 1249 | 1250 | div.toc h3 { 1251 | font: bold 12px/1.2 Arial,FreeSans,sans-serif; 1252 | color: #4665A2; 1253 | border-bottom: 0 none; 1254 | margin: 0; 1255 | } 1256 | 1257 | div.toc ul { 1258 | list-style: none outside none; 1259 | border: medium none; 1260 | padding: 0px; 1261 | } 1262 | 1263 | div.toc li.level1 { 1264 | margin-left: 0px; 1265 | } 1266 | 1267 | div.toc li.level2 { 1268 | margin-left: 15px; 1269 | } 1270 | 1271 | div.toc li.level3 { 1272 | margin-left: 30px; 1273 | } 1274 | 1275 | div.toc li.level4 { 1276 | margin-left: 45px; 1277 | } 1278 | 1279 | .inherit_header { 1280 | font-weight: bold; 1281 | color: gray; 1282 | cursor: pointer; 1283 | -webkit-touch-callout: none; 1284 | -webkit-user-select: none; 1285 | -khtml-user-select: none; 1286 | -moz-user-select: none; 1287 | -ms-user-select: none; 1288 | user-select: none; 1289 | } 1290 | 1291 | .inherit_header td { 1292 | padding: 6px 0px 2px 5px; 1293 | } 1294 | 1295 | .inherit { 1296 | display: none; 1297 | } 1298 | 1299 | tr.heading h2 { 1300 | margin-top: 42px; 1301 | margin-bottom: 20px; 1302 | } 1303 | 1304 | @media print 1305 | { 1306 | #top { display: none; } 1307 | #side-nav { display: none; } 1308 | #nav-path { display: none; } 1309 | body { overflow:visible; } 1310 | h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } 1311 | .summary { display: none; } 1312 | .memitem { page-break-inside: avoid; } 1313 | #doc-content 1314 | { 1315 | margin-left:0 !important; 1316 | height:auto !important; 1317 | width:auto !important; 1318 | overflow:inherit; 1319 | display:inline; 1320 | } 1321 | } 1322 | 1323 | 1324 | .tabs, .tabs2, .tabs3 { 1325 | background-image: none; 1326 | background-color: #2f3a48; 1327 | color: white; 1328 | 1329 | } 1330 | 1331 | 1332 | .tabs2 { 1333 | background-color: #51637b; 1334 | } 1335 | 1336 | 1337 | .tablist li { 1338 | background-image: none; 1339 | } 1340 | 1341 | 1342 | .tablist a { 1343 | background-image: none; 1344 | color: white; 1345 | //text-shadow: none; 1346 | text-shadow: rgba(0, 0, 0, 0.6) 0px 2px 2px; 1347 | } 1348 | 1349 | 1350 | .tablist a:hover { 1351 | background-image: none; 1352 | text-shadow: none; 1353 | } 1354 | 1355 | 1356 | .tablist li.current a { 1357 | background-image: none; 1358 | //color: #ccc; 1359 | text-shadow: none; 1360 | } 1361 | 1362 | 1363 | .tabs li.current { 1364 | background-color: #51637b; 1365 | } 1366 | 1367 | .tabs2 li.current { 1368 | background-color: #888; 1369 | } 1370 | 1371 | 1372 | .navpath { 1373 | border: none; 1374 | } 1375 | 1376 | 1377 | .navpath ul { 1378 | background-image: none; 1379 | background-color: #888; 1380 | border: none; 1381 | } 1382 | 1383 | 1384 | .navpath li { 1385 | background-image: none; 1386 | } 1387 | 1388 | 1389 | .navpath li.navelem a { 1390 | background-image: none; 1391 | color: white; 1392 | text-shadow: none; 1393 | } 1394 | 1395 | 1396 | .navpath li.navelem a:hover { 1397 | background-image: none; 1398 | color: white; 1399 | text-shadow: none; 1400 | } 1401 | 1402 | -------------------------------------------------------------------------------- /src/cxxopts.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef CXX_OPTS_HPP 26 | #define CXX_OPTS_HPP 27 | 28 | #if defined(__GNUC__) 29 | #pragma GCC diagnostic push 30 | #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" 31 | #endif 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | //when we ask cxxopts to use Unicode, help strings are processed using ICU, 46 | //which results in the correct lengths being computed for strings when they 47 | //are formatted for the help output 48 | //it is necessary to make sure that can be found by the 49 | //compiler, and that icu-uc is linked in to the binary. 50 | 51 | #ifdef CXXOPTS_USE_UNICODE 52 | #include 53 | 54 | namespace cxxopts 55 | { 56 | typedef icu::UnicodeString String; 57 | 58 | inline 59 | String 60 | toLocalString(std::string s) 61 | { 62 | return icu::UnicodeString::fromUTF8(std::move(s)); 63 | } 64 | 65 | class UnicodeStringIterator : public 66 | std::iterator 67 | { 68 | public: 69 | 70 | UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) 71 | : s(string) 72 | , i(pos) 73 | { 74 | } 75 | 76 | value_type 77 | operator*() const 78 | { 79 | return s->char32At(i); 80 | } 81 | 82 | bool 83 | operator==(const UnicodeStringIterator& rhs) const 84 | { 85 | return s == rhs.s && i == rhs.i; 86 | } 87 | 88 | bool 89 | operator!=(const UnicodeStringIterator& rhs) const 90 | { 91 | return !(*this == rhs); 92 | } 93 | 94 | UnicodeStringIterator& 95 | operator++() 96 | { 97 | ++i; 98 | return *this; 99 | } 100 | 101 | UnicodeStringIterator 102 | operator+(int32_t v) 103 | { 104 | return UnicodeStringIterator(s, i + v); 105 | } 106 | 107 | private: 108 | const icu::UnicodeString* s; 109 | int32_t i; 110 | }; 111 | 112 | inline 113 | String& 114 | stringAppend(String&s, String a) 115 | { 116 | return s.append(std::move(a)); 117 | } 118 | 119 | inline 120 | String& 121 | stringAppend(String& s, int n, UChar32 c) 122 | { 123 | for (int i = 0; i != n; ++i) 124 | { 125 | s.append(c); 126 | } 127 | 128 | return s; 129 | } 130 | 131 | template 132 | String& 133 | stringAppend(String& s, Iterator begin, Iterator end) 134 | { 135 | while (begin != end) 136 | { 137 | s.append(*begin); 138 | ++begin; 139 | } 140 | 141 | return s; 142 | } 143 | 144 | inline 145 | size_t 146 | stringLength(const String& s) 147 | { 148 | return s.length(); 149 | } 150 | 151 | inline 152 | std::string 153 | toUTF8String(const String& s) 154 | { 155 | std::string result; 156 | s.toUTF8String(result); 157 | 158 | return result; 159 | } 160 | 161 | inline 162 | bool 163 | empty(const String& s) 164 | { 165 | return s.isEmpty(); 166 | } 167 | } 168 | 169 | namespace std 170 | { 171 | cxxopts::UnicodeStringIterator 172 | begin(const icu::UnicodeString& s) 173 | { 174 | return cxxopts::UnicodeStringIterator(&s, 0); 175 | } 176 | 177 | cxxopts::UnicodeStringIterator 178 | end(const icu::UnicodeString& s) 179 | { 180 | return cxxopts::UnicodeStringIterator(&s, s.length()); 181 | } 182 | } 183 | 184 | //ifdef CXXOPTS_USE_UNICODE 185 | #else 186 | 187 | namespace cxxopts 188 | { 189 | typedef std::string String; 190 | 191 | template 192 | T 193 | toLocalString(T&& t) 194 | { 195 | return t; 196 | } 197 | 198 | inline 199 | size_t 200 | stringLength(const String& s) 201 | { 202 | return s.length(); 203 | } 204 | 205 | inline 206 | String& 207 | stringAppend(String&s, String a) 208 | { 209 | return s.append(std::move(a)); 210 | } 211 | 212 | inline 213 | String& 214 | stringAppend(String& s, size_t n, char c) 215 | { 216 | return s.append(n, c); 217 | } 218 | 219 | template 220 | String& 221 | stringAppend(String& s, Iterator begin, Iterator end) 222 | { 223 | return s.append(begin, end); 224 | } 225 | 226 | template 227 | std::string 228 | toUTF8String(T&& t) 229 | { 230 | return std::forward(t); 231 | } 232 | 233 | inline 234 | bool 235 | empty(const std::string& s) 236 | { 237 | return s.empty(); 238 | } 239 | } 240 | 241 | //ifdef CXXOPTS_USE_UNICODE 242 | #endif 243 | 244 | namespace cxxopts 245 | { 246 | namespace 247 | { 248 | #ifdef _WIN32 249 | const std::string LQUOTE("\'"); 250 | const std::string RQUOTE("\'"); 251 | #else 252 | const std::string LQUOTE("‘"); 253 | const std::string RQUOTE("’"); 254 | #endif 255 | } 256 | 257 | class Value : public std::enable_shared_from_this 258 | { 259 | public: 260 | 261 | virtual void 262 | parse(const std::string& text) const = 0; 263 | 264 | virtual void 265 | parse() const = 0; 266 | 267 | virtual bool 268 | has_arg() const = 0; 269 | 270 | virtual bool 271 | has_default() const = 0; 272 | 273 | virtual bool 274 | is_container() const = 0; 275 | 276 | virtual bool 277 | has_implicit() const = 0; 278 | 279 | virtual std::string 280 | get_default_value() const = 0; 281 | 282 | virtual std::string 283 | get_implicit_value() const = 0; 284 | 285 | virtual std::shared_ptr 286 | default_value(const std::string& value) = 0; 287 | 288 | virtual std::shared_ptr 289 | implicit_value(const std::string& value) = 0; 290 | }; 291 | 292 | class OptionException : public std::exception 293 | { 294 | public: 295 | OptionException(const std::string& message) 296 | : m_message(message) 297 | { 298 | } 299 | 300 | virtual const char* 301 | what() const noexcept 302 | { 303 | return m_message.c_str(); 304 | } 305 | 306 | private: 307 | std::string m_message; 308 | }; 309 | 310 | class OptionSpecException : public OptionException 311 | { 312 | public: 313 | 314 | OptionSpecException(const std::string& message) 315 | : OptionException(message) 316 | { 317 | } 318 | }; 319 | 320 | class OptionParseException : public OptionException 321 | { 322 | public: 323 | OptionParseException(const std::string& message) 324 | : OptionException(message) 325 | { 326 | } 327 | }; 328 | 329 | class option_exists_error : public OptionSpecException 330 | { 331 | public: 332 | option_exists_error(const std::string& option) 333 | : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + u8" already exists") 334 | { 335 | } 336 | }; 337 | 338 | class invalid_option_format_error : public OptionSpecException 339 | { 340 | public: 341 | invalid_option_format_error(const std::string& format) 342 | : OptionSpecException(u8"Invalid option format " + LQUOTE + format + RQUOTE) 343 | { 344 | } 345 | }; 346 | 347 | class option_not_exists_exception : public OptionParseException 348 | { 349 | public: 350 | option_not_exists_exception(const std::string& option) 351 | : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" does not exist") 352 | { 353 | } 354 | }; 355 | 356 | class missing_argument_exception : public OptionParseException 357 | { 358 | public: 359 | missing_argument_exception(const std::string& option) 360 | : OptionParseException( 361 | u8"Option " + LQUOTE + option + RQUOTE + u8" is missing an argument" 362 | ) 363 | { 364 | } 365 | }; 366 | 367 | class option_requires_argument_exception : public OptionParseException 368 | { 369 | public: 370 | option_requires_argument_exception(const std::string& option) 371 | : OptionParseException( 372 | u8"Option " + LQUOTE + option + RQUOTE + u8" requires an argument" 373 | ) 374 | { 375 | } 376 | }; 377 | 378 | class option_not_has_argument_exception : public OptionParseException 379 | { 380 | public: 381 | option_not_has_argument_exception 382 | ( 383 | const std::string& option, 384 | const std::string& arg 385 | ) 386 | : OptionParseException( 387 | u8"Option " + LQUOTE + option + RQUOTE + 388 | u8" does not take an argument, but argument" + 389 | LQUOTE + arg + RQUOTE + " given" 390 | ) 391 | { 392 | } 393 | }; 394 | 395 | class option_not_present_exception : public OptionParseException 396 | { 397 | public: 398 | option_not_present_exception(const std::string& option) 399 | : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" not present") 400 | { 401 | } 402 | }; 403 | 404 | class argument_incorrect_type : public OptionParseException 405 | { 406 | public: 407 | argument_incorrect_type 408 | ( 409 | const std::string& arg 410 | ) 411 | : OptionParseException( 412 | u8"Argument " + LQUOTE + arg + RQUOTE + u8" failed to parse" 413 | ) 414 | { 415 | } 416 | }; 417 | 418 | class option_required_exception : public OptionParseException 419 | { 420 | public: 421 | option_required_exception(const std::string& option) 422 | : OptionParseException( 423 | u8"Option " + LQUOTE + option + RQUOTE + u8" is required but not present" 424 | ) 425 | { 426 | } 427 | }; 428 | 429 | namespace values 430 | { 431 | namespace 432 | { 433 | std::basic_regex integer_pattern 434 | ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|(0)"); 435 | } 436 | 437 | namespace detail 438 | { 439 | template 440 | struct SignedCheck; 441 | 442 | template 443 | struct SignedCheck 444 | { 445 | template 446 | void 447 | operator()(bool negative, U u, const std::string& text) 448 | { 449 | if (negative) 450 | { 451 | if (u > static_cast(-std::numeric_limits::min())) 452 | { 453 | throw argument_incorrect_type(text); 454 | } 455 | } 456 | else 457 | { 458 | if (u > static_cast(std::numeric_limits::max())) 459 | { 460 | throw argument_incorrect_type(text); 461 | } 462 | } 463 | } 464 | }; 465 | 466 | template 467 | struct SignedCheck 468 | { 469 | template 470 | void 471 | operator()(bool, U, const std::string&) {} 472 | }; 473 | 474 | template 475 | void 476 | check_signed_range(bool negative, U value, const std::string& text) 477 | { 478 | SignedCheck::is_signed>()(negative, value, text); 479 | } 480 | } 481 | 482 | template 483 | R 484 | checked_negate(T&& t, const std::string&, std::true_type) 485 | { 486 | // if we got to here, then `t` is a positive number that fits into 487 | // `R`. So to avoid MSVC C4146, we first cast it to `R`. 488 | // See https://github.com/jarro2783/cxxopts/issues/62 for more details. 489 | return -static_cast(t); 490 | } 491 | 492 | template 493 | T 494 | checked_negate(T&&, const std::string& text, std::false_type) 495 | { 496 | throw argument_incorrect_type(text); 497 | } 498 | 499 | template 500 | void 501 | integer_parser(const std::string& text, T& value) 502 | { 503 | std::smatch match; 504 | std::regex_match(text, match, integer_pattern); 505 | 506 | if (match.length() == 0) 507 | { 508 | throw argument_incorrect_type(text); 509 | } 510 | 511 | if (match.length(4) > 0) 512 | { 513 | value = 0; 514 | return; 515 | } 516 | 517 | using US = typename std::make_unsigned::type; 518 | 519 | constexpr auto umax = std::numeric_limits::max(); 520 | constexpr bool is_signed = std::numeric_limits::is_signed; 521 | const bool negative = match.length(1) > 0; 522 | const auto base = match.length(2) > 0 ? 16 : 10; 523 | 524 | auto value_match = match[3]; 525 | 526 | US result = 0; 527 | 528 | for (auto iter = value_match.first; iter != value_match.second; ++iter) 529 | { 530 | int digit = 0; 531 | 532 | if (*iter >= '0' && *iter <= '9') 533 | { 534 | digit = *iter - '0'; 535 | } 536 | else if (*iter >= 'a' && *iter <= 'f') 537 | { 538 | digit = *iter - 'a' + 10; 539 | } 540 | else if (*iter >= 'A' && *iter <= 'F') 541 | { 542 | digit = *iter - 'A' + 10; 543 | } 544 | 545 | if (umax - digit < result * base) 546 | { 547 | throw argument_incorrect_type(text); 548 | } 549 | 550 | result = result * base + digit; 551 | } 552 | 553 | detail::check_signed_range(negative, result, text); 554 | 555 | if (negative) 556 | { 557 | value = checked_negate(result, 558 | text, 559 | std::integral_constant()); 560 | //if (!is_signed) 561 | //{ 562 | // throw argument_incorrect_type(text); 563 | //} 564 | //value = -result; 565 | } 566 | else 567 | { 568 | value = result; 569 | } 570 | } 571 | 572 | template 573 | void stringstream_parser(const std::string& text, T& value) 574 | { 575 | std::stringstream in(text); 576 | in >> value; 577 | if (!in) { 578 | throw argument_incorrect_type(text); 579 | } 580 | } 581 | 582 | inline 583 | void 584 | parse_value(const std::string& text, uint8_t& value) 585 | { 586 | integer_parser(text, value); 587 | } 588 | 589 | inline 590 | void 591 | parse_value(const std::string& text, int8_t& value) 592 | { 593 | integer_parser(text, value); 594 | } 595 | 596 | inline 597 | void 598 | parse_value(const std::string& text, uint16_t& value) 599 | { 600 | integer_parser(text, value); 601 | } 602 | 603 | inline 604 | void 605 | parse_value(const std::string& text, int16_t& value) 606 | { 607 | integer_parser(text, value); 608 | } 609 | 610 | inline 611 | void 612 | parse_value(const std::string& text, uint32_t& value) 613 | { 614 | integer_parser(text, value); 615 | } 616 | 617 | inline 618 | void 619 | parse_value(const std::string& text, int32_t& value) 620 | { 621 | integer_parser(text, value); 622 | } 623 | 624 | inline 625 | void 626 | parse_value(const std::string& text, uint64_t& value) 627 | { 628 | integer_parser(text, value); 629 | } 630 | 631 | inline 632 | void 633 | parse_value(const std::string& text, int64_t& value) 634 | { 635 | integer_parser(text, value); 636 | } 637 | 638 | inline 639 | void 640 | parse_value(const std::string& /*text*/, bool& value) 641 | { 642 | //TODO recognise on, off, yes, no, enable, disable 643 | //so that we can write --long=yes explicitly 644 | value = true; 645 | } 646 | 647 | inline 648 | void 649 | parse_value(const std::string& text, std::string& value) 650 | { 651 | value = text; 652 | } 653 | 654 | // The fallback parser. It uses the stringstream parser to parse all types 655 | // that have not been overloaded explicitly. It has to be placed in the 656 | // source code before all other more specialized templates. 657 | template 658 | void 659 | parse_value(const std::string& text, T& value) { 660 | stringstream_parser(text, value); 661 | } 662 | 663 | template 664 | void 665 | parse_value(const std::string& text, std::vector& value) 666 | { 667 | T v; 668 | parse_value(text, v); 669 | value.push_back(v); 670 | } 671 | 672 | template 673 | struct value_has_arg 674 | { 675 | static constexpr bool value = true; 676 | }; 677 | 678 | template <> 679 | struct value_has_arg 680 | { 681 | static constexpr bool value = false; 682 | }; 683 | 684 | template 685 | struct type_is_container 686 | { 687 | static constexpr bool value = false; 688 | }; 689 | 690 | template 691 | struct type_is_container> 692 | { 693 | static constexpr bool value = true; 694 | }; 695 | 696 | template 697 | class standard_value final : public Value 698 | { 699 | public: 700 | standard_value() 701 | : m_result(std::make_shared()) 702 | , m_store(m_result.get()) 703 | { 704 | } 705 | 706 | standard_value(T* t) 707 | : m_store(t) 708 | { 709 | } 710 | 711 | void 712 | parse(const std::string& text) const 713 | { 714 | parse_value(text, *m_store); 715 | } 716 | 717 | bool 718 | is_container() const 719 | { 720 | return type_is_container::value; 721 | } 722 | 723 | void 724 | parse() const 725 | { 726 | parse_value(m_default_value, *m_store); 727 | } 728 | 729 | bool 730 | has_arg() const 731 | { 732 | return value_has_arg::value; 733 | } 734 | 735 | bool 736 | has_default() const 737 | { 738 | return m_default; 739 | } 740 | 741 | bool 742 | has_implicit() const 743 | { 744 | return m_implicit; 745 | } 746 | 747 | virtual std::shared_ptr 748 | default_value(const std::string& value){ 749 | m_default = true; 750 | m_default_value = value; 751 | return shared_from_this(); 752 | } 753 | 754 | virtual std::shared_ptr 755 | implicit_value(const std::string& value){ 756 | m_implicit = true; 757 | m_implicit_value = value; 758 | return shared_from_this(); 759 | } 760 | 761 | std::string 762 | get_default_value() const 763 | { 764 | return m_default_value; 765 | } 766 | 767 | std::string 768 | get_implicit_value() const 769 | { 770 | return m_implicit_value; 771 | } 772 | 773 | const T& 774 | get() const 775 | { 776 | if (m_store == nullptr) 777 | { 778 | return *m_result; 779 | } 780 | else 781 | { 782 | return *m_store; 783 | } 784 | } 785 | 786 | protected: 787 | std::shared_ptr m_result; 788 | T* m_store; 789 | bool m_default = false; 790 | std::string m_default_value; 791 | bool m_implicit = false; 792 | std::string m_implicit_value; 793 | }; 794 | } 795 | 796 | template 797 | std::shared_ptr 798 | value() 799 | { 800 | return std::make_shared>(); 801 | } 802 | 803 | template 804 | std::shared_ptr 805 | value(T& t) 806 | { 807 | return std::make_shared>(&t); 808 | } 809 | 810 | class OptionAdder; 811 | 812 | class OptionDetails 813 | { 814 | public: 815 | OptionDetails 816 | ( 817 | const String& desc, 818 | std::shared_ptr val 819 | ) 820 | : m_desc(desc) 821 | , m_value(val) 822 | , m_count(0) 823 | { 824 | } 825 | 826 | const String& 827 | description() const 828 | { 829 | return m_desc; 830 | } 831 | 832 | bool 833 | has_arg() const 834 | { 835 | return m_value->has_arg(); 836 | } 837 | 838 | void 839 | parse(const std::string& text) 840 | { 841 | m_value->parse(text); 842 | ++m_count; 843 | } 844 | 845 | void 846 | parse_default() 847 | { 848 | m_value->parse(); 849 | } 850 | 851 | int 852 | count() const 853 | { 854 | return m_count; 855 | } 856 | 857 | const Value& value() const { 858 | return *m_value; 859 | } 860 | 861 | template 862 | const T& 863 | as() const 864 | { 865 | #ifdef CXXOPTS_NO_RTTI 866 | return static_cast&>(*m_value).get(); 867 | #else 868 | return static_cast&>(*m_value).get(); 869 | #endif 870 | } 871 | 872 | private: 873 | String m_desc; 874 | std::shared_ptr m_value; 875 | int m_count; 876 | }; 877 | 878 | struct HelpOptionDetails 879 | { 880 | std::string s; 881 | std::string l; 882 | String desc; 883 | bool has_arg; 884 | bool has_default; 885 | std::string default_value; 886 | bool has_implicit; 887 | std::string implicit_value; 888 | std::string arg_help; 889 | bool is_container; 890 | }; 891 | 892 | struct HelpGroupDetails 893 | { 894 | std::string name; 895 | std::string description; 896 | std::vector options; 897 | }; 898 | 899 | class Options 900 | { 901 | public: 902 | 903 | Options(std::string program, std::string help_string = "") 904 | : m_program(std::move(program)) 905 | , m_help_string(toLocalString(std::move(help_string))) 906 | , m_positional_help("positional parameters") 907 | , m_next_positional(m_positional.end()) 908 | { 909 | } 910 | 911 | inline 912 | Options& 913 | positional_help(std::string help_text) 914 | { 915 | m_positional_help = std::move(help_text); 916 | return *this; 917 | } 918 | 919 | inline 920 | void 921 | parse(int& argc, char**& argv); 922 | 923 | inline 924 | OptionAdder 925 | add_options(std::string group = ""); 926 | 927 | inline 928 | void 929 | add_option 930 | ( 931 | const std::string& group, 932 | const std::string& s, 933 | const std::string& l, 934 | std::string desc, 935 | std::shared_ptr value, 936 | std::string arg_help 937 | ); 938 | 939 | int 940 | count(const std::string& o) const 941 | { 942 | auto iter = m_options.find(o); 943 | if (iter == m_options.end()) 944 | { 945 | return 0; 946 | } 947 | 948 | return iter->second->count(); 949 | } 950 | 951 | const OptionDetails& 952 | operator[](const std::string& option) const 953 | { 954 | auto iter = m_options.find(option); 955 | 956 | if (iter == m_options.end()) 957 | { 958 | throw option_not_present_exception(option); 959 | } 960 | 961 | return *iter->second; 962 | } 963 | 964 | //parse positional arguments into the given option 965 | inline 966 | void 967 | parse_positional(std::string option); 968 | 969 | inline 970 | void 971 | parse_positional(std::vector options); 972 | 973 | inline 974 | std::string 975 | help(const std::vector& groups = {""}) const; 976 | 977 | inline 978 | const std::vector 979 | groups() const; 980 | 981 | inline 982 | const HelpGroupDetails& 983 | group_help(const std::string& group) const; 984 | 985 | private: 986 | 987 | inline 988 | void 989 | add_one_option 990 | ( 991 | const std::string& option, 992 | std::shared_ptr details 993 | ); 994 | 995 | inline 996 | bool 997 | consume_positional(std::string a); 998 | 999 | inline 1000 | void 1001 | add_to_option(const std::string& option, const std::string& arg); 1002 | 1003 | inline 1004 | void 1005 | parse_option 1006 | ( 1007 | std::shared_ptr value, 1008 | const std::string& name, 1009 | const std::string& arg = "" 1010 | ); 1011 | 1012 | inline 1013 | void 1014 | checked_parse_arg 1015 | ( 1016 | int argc, 1017 | char* argv[], 1018 | int& current, 1019 | std::shared_ptr value, 1020 | const std::string& name 1021 | ); 1022 | 1023 | inline 1024 | String 1025 | help_one_group(const std::string& group) const; 1026 | 1027 | inline 1028 | void 1029 | generate_group_help 1030 | ( 1031 | String& result, 1032 | const std::vector& groups 1033 | ) const; 1034 | 1035 | inline 1036 | void 1037 | generate_all_groups_help(String& result) const; 1038 | 1039 | std::string m_program; 1040 | String m_help_string; 1041 | std::string m_positional_help; 1042 | 1043 | std::map> m_options; 1044 | std::vector m_positional; 1045 | std::vector::iterator m_next_positional; 1046 | std::unordered_set m_positional_set; 1047 | 1048 | //mapping from groups to help options 1049 | std::map m_help; 1050 | }; 1051 | 1052 | class OptionAdder 1053 | { 1054 | public: 1055 | 1056 | OptionAdder(Options& options, std::string group) 1057 | : m_options(options), m_group(std::move(group)) 1058 | { 1059 | } 1060 | 1061 | inline 1062 | OptionAdder& 1063 | operator() 1064 | ( 1065 | const std::string& opts, 1066 | const std::string& desc, 1067 | std::shared_ptr value 1068 | = ::cxxopts::value(), 1069 | std::string arg_help = "" 1070 | ); 1071 | 1072 | private: 1073 | Options& m_options; 1074 | std::string m_group; 1075 | }; 1076 | 1077 | // A helper function for setting required arguments 1078 | inline 1079 | void 1080 | check_required 1081 | ( 1082 | const Options& options, 1083 | const std::vector& required 1084 | ) 1085 | { 1086 | for (auto& r : required) 1087 | { 1088 | if (options.count(r) == 0) 1089 | { 1090 | throw option_required_exception(r); 1091 | } 1092 | } 1093 | } 1094 | 1095 | namespace 1096 | { 1097 | constexpr int OPTION_LONGEST = 30; 1098 | constexpr int OPTION_DESC_GAP = 2; 1099 | 1100 | std::basic_regex option_matcher 1101 | ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); 1102 | 1103 | std::basic_regex option_specifier 1104 | ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); 1105 | 1106 | String 1107 | format_option 1108 | ( 1109 | const HelpOptionDetails& o 1110 | ) 1111 | { 1112 | auto& s = o.s; 1113 | auto& l = o.l; 1114 | 1115 | String result = " "; 1116 | 1117 | if (s.size() > 0) 1118 | { 1119 | result += "-" + toLocalString(s) + ","; 1120 | } 1121 | else 1122 | { 1123 | result += " "; 1124 | } 1125 | 1126 | if (l.size() > 0) 1127 | { 1128 | result += " --" + toLocalString(l); 1129 | } 1130 | 1131 | if (o.has_arg) 1132 | { 1133 | auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; 1134 | 1135 | if (o.has_implicit) 1136 | { 1137 | result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; 1138 | } 1139 | else 1140 | { 1141 | result += " " + arg; 1142 | } 1143 | } 1144 | 1145 | return result; 1146 | } 1147 | 1148 | String 1149 | format_description 1150 | ( 1151 | const HelpOptionDetails& o, 1152 | size_t start, 1153 | size_t width 1154 | ) 1155 | { 1156 | auto desc = o.desc; 1157 | 1158 | if (o.has_default) 1159 | { 1160 | desc += toLocalString(" (default: " + o.default_value + ")"); 1161 | } 1162 | 1163 | String result; 1164 | 1165 | auto current = std::begin(desc); 1166 | auto startLine = current; 1167 | auto lastSpace = current; 1168 | 1169 | auto size = size_t{}; 1170 | 1171 | while (current != std::end(desc)) 1172 | { 1173 | if (*current == ' ') 1174 | { 1175 | lastSpace = current; 1176 | } 1177 | 1178 | if (size > width) 1179 | { 1180 | if (lastSpace == startLine) 1181 | { 1182 | stringAppend(result, startLine, current + 1); 1183 | stringAppend(result, "\n"); 1184 | stringAppend(result, start, ' '); 1185 | startLine = current + 1; 1186 | lastSpace = startLine; 1187 | } 1188 | else 1189 | { 1190 | stringAppend(result, startLine, lastSpace); 1191 | stringAppend(result, "\n"); 1192 | stringAppend(result, start, ' '); 1193 | startLine = lastSpace + 1; 1194 | } 1195 | size = 0; 1196 | } 1197 | else 1198 | { 1199 | ++size; 1200 | } 1201 | 1202 | ++current; 1203 | } 1204 | 1205 | //append whatever is left 1206 | stringAppend(result, startLine, current); 1207 | 1208 | return result; 1209 | } 1210 | } 1211 | 1212 | OptionAdder 1213 | Options::add_options(std::string group) 1214 | { 1215 | return OptionAdder(*this, std::move(group)); 1216 | } 1217 | 1218 | OptionAdder& 1219 | OptionAdder::operator() 1220 | ( 1221 | const std::string& opts, 1222 | const std::string& desc, 1223 | std::shared_ptr value, 1224 | std::string arg_help 1225 | ) 1226 | { 1227 | std::match_results result; 1228 | std::regex_match(opts.c_str(), result, option_specifier); 1229 | 1230 | if (result.empty()) 1231 | { 1232 | throw invalid_option_format_error(opts); 1233 | } 1234 | 1235 | const auto& short_match = result[2]; 1236 | const auto& long_match = result[3]; 1237 | 1238 | if (!short_match.length() && !long_match.length()) 1239 | { 1240 | throw invalid_option_format_error(opts); 1241 | } else if (long_match.length() == 1 && short_match.length()) 1242 | { 1243 | throw invalid_option_format_error(opts); 1244 | } 1245 | 1246 | auto option_names = [] 1247 | ( 1248 | const std::sub_match& short_, 1249 | const std::sub_match& long_ 1250 | ) 1251 | { 1252 | if (long_.length() == 1) 1253 | { 1254 | return std::make_tuple(long_.str(), short_.str()); 1255 | } 1256 | else 1257 | { 1258 | return std::make_tuple(short_.str(), long_.str()); 1259 | } 1260 | }(short_match, long_match); 1261 | 1262 | m_options.add_option 1263 | ( 1264 | m_group, 1265 | std::get<0>(option_names), 1266 | std::get<1>(option_names), 1267 | desc, 1268 | value, 1269 | std::move(arg_help) 1270 | ); 1271 | 1272 | return *this; 1273 | } 1274 | 1275 | void 1276 | Options::parse_option 1277 | ( 1278 | std::shared_ptr value, 1279 | const std::string& /*name*/, 1280 | const std::string& arg 1281 | ) 1282 | { 1283 | value->parse(arg); 1284 | } 1285 | 1286 | void 1287 | Options::checked_parse_arg 1288 | ( 1289 | int argc, 1290 | char* argv[], 1291 | int& current, 1292 | std::shared_ptr value, 1293 | const std::string& name 1294 | ) 1295 | { 1296 | if (current + 1 >= argc) 1297 | { 1298 | if (value->value().has_implicit()) 1299 | { 1300 | parse_option(value, name, value->value().get_implicit_value()); 1301 | } 1302 | else 1303 | { 1304 | throw missing_argument_exception(name); 1305 | } 1306 | } 1307 | else 1308 | { 1309 | if (argv[current + 1][0] == '-' && value->value().has_implicit()) 1310 | { 1311 | parse_option(value, name, value->value().get_implicit_value()); 1312 | } 1313 | else 1314 | { 1315 | parse_option(value, name, argv[current + 1]); 1316 | ++current; 1317 | } 1318 | } 1319 | } 1320 | 1321 | void 1322 | Options::add_to_option(const std::string& option, const std::string& arg) 1323 | { 1324 | auto iter = m_options.find(option); 1325 | 1326 | if (iter == m_options.end()) 1327 | { 1328 | throw option_not_exists_exception(option); 1329 | } 1330 | 1331 | parse_option(iter->second, option, arg); 1332 | } 1333 | 1334 | bool 1335 | Options::consume_positional(std::string a) 1336 | { 1337 | while (m_next_positional != m_positional.end()) 1338 | { 1339 | auto iter = m_options.find(*m_next_positional); 1340 | if (iter != m_options.end()) 1341 | { 1342 | if (!iter->second->value().is_container()) 1343 | { 1344 | if (iter->second->count() == 0) 1345 | { 1346 | add_to_option(*m_next_positional, a); 1347 | ++m_next_positional; 1348 | return true; 1349 | } 1350 | else 1351 | { 1352 | ++m_next_positional; 1353 | continue; 1354 | } 1355 | } 1356 | else 1357 | { 1358 | add_to_option(*m_next_positional, a); 1359 | return true; 1360 | } 1361 | } 1362 | ++m_next_positional; 1363 | } 1364 | 1365 | return false; 1366 | } 1367 | 1368 | void 1369 | Options::parse_positional(std::string option) 1370 | { 1371 | parse_positional(std::vector{option}); 1372 | } 1373 | 1374 | void 1375 | Options::parse_positional(std::vector options) 1376 | { 1377 | m_positional = std::move(options); 1378 | m_next_positional = m_positional.begin(); 1379 | 1380 | m_positional_set.insert(m_positional.begin(), m_positional.end()); 1381 | } 1382 | 1383 | void 1384 | Options::parse(int& argc, char**& argv) 1385 | { 1386 | int current = 1; 1387 | 1388 | int nextKeep = 1; 1389 | 1390 | bool consume_remaining = false; 1391 | 1392 | while (current != argc) 1393 | { 1394 | if (strcmp(argv[current], "--") == 0) 1395 | { 1396 | consume_remaining = true; 1397 | ++current; 1398 | break; 1399 | } 1400 | 1401 | std::match_results result; 1402 | std::regex_match(argv[current], result, option_matcher); 1403 | 1404 | if (result.empty()) 1405 | { 1406 | //not a flag 1407 | 1408 | //if true is returned here then it was consumed, otherwise it is 1409 | //ignored 1410 | if (consume_positional(argv[current])) 1411 | { 1412 | } 1413 | else 1414 | { 1415 | argv[nextKeep] = argv[current]; 1416 | ++nextKeep; 1417 | } 1418 | //if we return from here then it was parsed successfully, so continue 1419 | } 1420 | else 1421 | { 1422 | //short or long option? 1423 | if (result[4].length() != 0) 1424 | { 1425 | const std::string& s = result[4]; 1426 | 1427 | for (std::size_t i = 0; i != s.size(); ++i) 1428 | { 1429 | std::string name(1, s[i]); 1430 | auto iter = m_options.find(name); 1431 | 1432 | if (iter == m_options.end()) 1433 | { 1434 | throw option_not_exists_exception(name); 1435 | } 1436 | 1437 | auto value = iter->second; 1438 | 1439 | //if no argument then just add it 1440 | if (!value->has_arg()) 1441 | { 1442 | parse_option(value, name); 1443 | } 1444 | else 1445 | { 1446 | //it must be the last argument 1447 | if (i + 1 == s.size()) 1448 | { 1449 | checked_parse_arg(argc, argv, current, value, name); 1450 | } 1451 | else if (value->value().has_implicit()) 1452 | { 1453 | parse_option(value, name, value->value().get_implicit_value()); 1454 | } 1455 | else 1456 | { 1457 | //error 1458 | throw option_requires_argument_exception(name); 1459 | } 1460 | } 1461 | } 1462 | } 1463 | else if (result[1].length() != 0) 1464 | { 1465 | const std::string& name = result[1]; 1466 | 1467 | auto iter = m_options.find(name); 1468 | 1469 | if (iter == m_options.end()) 1470 | { 1471 | throw option_not_exists_exception(name); 1472 | } 1473 | 1474 | auto opt = iter->second; 1475 | 1476 | //equals provided for long option? 1477 | if (result[3].length() != 0) 1478 | { 1479 | //parse the option given 1480 | 1481 | //but if it doesn't take an argument, this is an error 1482 | if (!opt->has_arg()) 1483 | { 1484 | throw option_not_has_argument_exception(name, result[3]); 1485 | } 1486 | 1487 | parse_option(opt, name, result[3]); 1488 | } 1489 | else 1490 | { 1491 | if (opt->has_arg()) 1492 | { 1493 | //parse the next argument 1494 | checked_parse_arg(argc, argv, current, opt, name); 1495 | } 1496 | else 1497 | { 1498 | //parse with empty argument 1499 | parse_option(opt, name); 1500 | } 1501 | } 1502 | } 1503 | 1504 | } 1505 | 1506 | ++current; 1507 | } 1508 | 1509 | for (auto& opt : m_options) 1510 | { 1511 | auto& detail = opt.second; 1512 | auto& value = detail->value(); 1513 | 1514 | if(!detail->count() && value.has_default()){ 1515 | detail->parse_default(); 1516 | } 1517 | } 1518 | 1519 | if (consume_remaining) 1520 | { 1521 | while (current < argc) 1522 | { 1523 | if (!consume_positional(argv[current])) { 1524 | break; 1525 | } 1526 | ++current; 1527 | } 1528 | 1529 | //adjust argv for any that couldn't be swallowed 1530 | while (current != argc) { 1531 | argv[nextKeep] = argv[current]; 1532 | ++nextKeep; 1533 | ++current; 1534 | } 1535 | } 1536 | 1537 | argc = nextKeep; 1538 | 1539 | } 1540 | 1541 | void 1542 | Options::add_option 1543 | ( 1544 | const std::string& group, 1545 | const std::string& s, 1546 | const std::string& l, 1547 | std::string desc, 1548 | std::shared_ptr value, 1549 | std::string arg_help 1550 | ) 1551 | { 1552 | auto stringDesc = toLocalString(std::move(desc)); 1553 | auto option = std::make_shared(stringDesc, value); 1554 | 1555 | if (s.size() > 0) 1556 | { 1557 | add_one_option(s, option); 1558 | } 1559 | 1560 | if (l.size() > 0) 1561 | { 1562 | add_one_option(l, option); 1563 | } 1564 | 1565 | //add the help details 1566 | auto& options = m_help[group]; 1567 | 1568 | options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, 1569 | value->has_arg(), 1570 | value->has_default(), value->get_default_value(), 1571 | value->has_implicit(), value->get_implicit_value(), 1572 | std::move(arg_help), 1573 | value->is_container()}); 1574 | } 1575 | 1576 | void 1577 | Options::add_one_option 1578 | ( 1579 | const std::string& option, 1580 | std::shared_ptr details 1581 | ) 1582 | { 1583 | auto in = m_options.emplace(option, details); 1584 | 1585 | if (!in.second) 1586 | { 1587 | throw option_exists_error(option); 1588 | } 1589 | } 1590 | 1591 | String 1592 | Options::help_one_group(const std::string& g) const 1593 | { 1594 | typedef std::vector> OptionHelp; 1595 | 1596 | auto group = m_help.find(g); 1597 | if (group == m_help.end()) 1598 | { 1599 | return ""; 1600 | } 1601 | 1602 | OptionHelp format; 1603 | 1604 | size_t longest = 0; 1605 | 1606 | String result; 1607 | 1608 | if (!g.empty()) 1609 | { 1610 | result += toLocalString(" " + g + " options:\n"); 1611 | } 1612 | 1613 | for (const auto& o : group->second.options) 1614 | { 1615 | if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) 1616 | { 1617 | continue; 1618 | } 1619 | 1620 | auto s = format_option(o); 1621 | longest = std::max(longest, stringLength(s)); 1622 | format.push_back(std::make_pair(s, String())); 1623 | } 1624 | 1625 | longest = std::min(longest, static_cast(OPTION_LONGEST)); 1626 | 1627 | //widest allowed description 1628 | auto allowed = size_t{76} - longest - OPTION_DESC_GAP; 1629 | 1630 | auto fiter = format.begin(); 1631 | for (const auto& o : group->second.options) 1632 | { 1633 | if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) 1634 | { 1635 | continue; 1636 | } 1637 | 1638 | auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); 1639 | 1640 | result += fiter->first; 1641 | if (stringLength(fiter->first) > longest) 1642 | { 1643 | result += '\n'; 1644 | result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); 1645 | } 1646 | else 1647 | { 1648 | result += toLocalString(std::string(longest + OPTION_DESC_GAP - 1649 | stringLength(fiter->first), 1650 | ' ')); 1651 | } 1652 | result += d; 1653 | result += '\n'; 1654 | 1655 | ++fiter; 1656 | } 1657 | 1658 | return result; 1659 | } 1660 | 1661 | void 1662 | Options::generate_group_help 1663 | ( 1664 | String& result, 1665 | const std::vector& print_groups 1666 | ) const 1667 | { 1668 | for (size_t i = 0; i != print_groups.size(); ++i) 1669 | { 1670 | const String& group_help_text = help_one_group(print_groups[i]); 1671 | if (empty(group_help_text)) 1672 | { 1673 | continue; 1674 | } 1675 | result += group_help_text; 1676 | if (i < print_groups.size() - 1) 1677 | { 1678 | result += '\n'; 1679 | } 1680 | } 1681 | } 1682 | 1683 | void 1684 | Options::generate_all_groups_help(String& result) const 1685 | { 1686 | std::vector all_groups; 1687 | all_groups.reserve(m_help.size()); 1688 | 1689 | for (auto& group : m_help) 1690 | { 1691 | all_groups.push_back(group.first); 1692 | } 1693 | 1694 | generate_group_help(result, all_groups); 1695 | } 1696 | 1697 | std::string 1698 | Options::help(const std::vector& help_groups) const 1699 | { 1700 | String result = m_help_string + "\nUsage:\n " + 1701 | toLocalString(m_program) + " [OPTION...]"; 1702 | 1703 | if (m_positional.size() > 0) { 1704 | result += " " + toLocalString(m_positional_help); 1705 | } 1706 | 1707 | result += "\n\n"; 1708 | 1709 | if (help_groups.size() == 0) 1710 | { 1711 | generate_all_groups_help(result); 1712 | } 1713 | else 1714 | { 1715 | generate_group_help(result, help_groups); 1716 | } 1717 | 1718 | return toUTF8String(result); 1719 | } 1720 | 1721 | const std::vector 1722 | Options::groups() const 1723 | { 1724 | std::vector g; 1725 | 1726 | std::transform( 1727 | m_help.begin(), 1728 | m_help.end(), 1729 | std::back_inserter(g), 1730 | [] (const std::map::value_type& pair) 1731 | { 1732 | return pair.first; 1733 | } 1734 | ); 1735 | 1736 | return g; 1737 | } 1738 | 1739 | const HelpGroupDetails& 1740 | Options::group_help(const std::string& group) const 1741 | { 1742 | return m_help.at(group); 1743 | } 1744 | 1745 | } 1746 | 1747 | #if defined(__GNU__) 1748 | #pragma GCC diagnostic pop 1749 | #endif 1750 | 1751 | #endif //CXX_OPTS_HPP 1752 | --------------------------------------------------------------------------------