Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.
Micha Reiser edited this page May 28, 2017 · 4 revisions

The compiler CLI allows compiling a single or multiple TypeScript files.

node packages/compiler/cli.js
Pass either a config file or the files to use
  Usage: cli [options] [files ...]

  Options:

    -h, --help                    output usage information
    -V, --version                 output the version number
    -c --config <configFile>      The path to the tsconfig.json
    --unsafe                      Use the unsafe runtime system
    --emit-llvm                   Emit LLVM Assembly Code instead of WASM files
    --save-wast                   Saves the WAST file in the output directory if compiling all the way to WebAssembly
    --save-bc                     Saves a copy of the bitcode to the output directory if compiling all the way to WebAssembly. The file includes the linked and optimized code.
    --binaryen-opt                Optimize using Binaryen opt
    --expose-gc                   Exposes the speedy js garbage collector in the module as speedyJsGc
    --export-gc                   Exposes and exports the speedy js garbage collector as the symbol speedyJsGc
    --disable-heap-nuke-on-exit   Disables nuking of the heap before the exit of the entry function (it's your responsible for calling the gc in this case!)
    --optimization-level [value]  The optimization level to use. One of the following values: '0, 1, 2, 3, s or z'
    -s --settings [value]         additional settings

A single file can be compiled using:

node packages/compiler/cli.js packages/benchmark/cases/tspInt-spdy.ts

The compiler emits the JavaScript file next to the TypeScript file. Multiple files can either be compiled by passing each file name to the compiler or by calling the compiler from a directory that contains a tsconfig.json.

Inspecting the Generated Code

The compiler offers various options that allow examining the generated code. This is useful for curious people or to analyze performance issues. The files containing the intermediate representations are emitted to the output directory --- by default side by side with the TypeScript file.

The example use the following TypeScript file as input:

export async function fib(value: int): Promise<int> {
    "use speedyjs";

    return fibSync(value);
}

function fibSync(value: int): int {
    "use speedyjs";

    if (value <= 2) {
        return 1;
    }

    return fibSync(value - 2) + fibSync(value - 1);
}

emit-llvm

The --emit-llvm option emits the LLVM-IR code generated by the Speedy.js compiler before any optimizations are applied. The output file has the file ending .ll.

node packages/compiler/cli.js packages/benchmark/cases/fib-spdy.ts --emit-llvm
cat packages/benchmark/cases/fib-spdy.ll
; ModuleID = 'fib-spdy.ts'
source_filename = "fib-spdy.ts"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

%class.Math = type { { [5 x i8]* }* }

@Array_name = private unnamed_addr constant [6 x i8] c"Array\00"
@Array_type_descriptor = private constant { [6 x i8]* } { [6 x i8]* @Array_name }
@Math_name = private unnamed_addr constant [5 x i8] c"Math\00"
@Math_type_descriptor = private constant { [5 x i8]* } { [5 x i8]* @Math_name }
@Math_object = private constant %class.Math { { [5 x i8]* }* @Math_type_descriptor }
@Math_ptr = private constant %class.Math* @Math_object

define i32 @_fib(i32 %value) {
entry:
  %value.addr = alloca i32, align 4
  %return = alloca i32, align 4
  store i32 %value, i32* %value.addr, align 4
  %value.addr1 = load i32, i32* %value.addr, align 4
  %fibSyncReturnValue = call i32 @"fib_spdy.ts$$7fibSynci"(i32 %value.addr1)
  store i32 %fibSyncReturnValue, i32* %return, align 4
  br label %returnBlock

returnBlock:                                      ; preds = %entry
  %return2 = load i32, i32* %return, align 4
  ret i32 %return2
}

define linkonce_odr hidden i32 @"fib_spdy.ts$$7fibSynci"(i32 %value) {
entry:
  %value.addr = alloca i32, align 4
  %return = alloca i32, align 4
  store i32 %value, i32* %value.addr, align 4
  %value.addr1 = load i32, i32* %value.addr, align 4
  %cmpLE = icmp sle i32 %value.addr1, 2
  br i1 %cmpLE, label %if.then, label %if.end

if.then:                                          ; preds = %entry
  store i32 1, i32* %return, align 4
  br label %returnBlock

if.end:                                           ; preds = %entry
  %value.addr2 = load i32, i32* %value.addr, align 4
  %sub = sub i32 %value.addr2, 2
  %fibSyncReturnValue = call i32 @"fib_spdy.ts$$7fibSynci"(i32 %sub)
  %value.addr3 = load i32, i32* %value.addr, align 4
  %sub4 = sub i32 %value.addr3, 1
  %fibSyncReturnValue5 = call i32 @"fib_spdy.ts$$7fibSynci"(i32 %sub4)
  %add = add i32 %fibSyncReturnValue, %fibSyncReturnValue5
  store i32 %add, i32* %return, align 4
  br label %returnBlock

returnBlock:                                      ; preds = %if.end, %if.then
  %return6 = load i32, i32* %return, align 4
  ret i32 %return6
}

save-bc

The --save-bc option saves the optimized and linked program as LLVM bitcode file. The bitcode file is used as input to the LLVM WebAssembly backend. The bitcode file is useful to inspect the applied optimizations. The bitcode file has the file ending .bc. It is needed to disassemble the file using llvm-dis to get a human readable representation. llvm-dis saves the disassembled output with the file ending .ll (as is --emit-llvm).

node packages/compiler/cli.js packages/benchmark/cases/fib-spdy.ts --save-bc
packages/compiler/tools/llvm/bin/llvm-dis packages/benchmark/cases/fib-spdy.bc
cat packages/benchmark/cases/fib-spdy.ll
; ModuleID = 'packages/benchmark/cases/fib-spdy.bc'
source_filename = "llvm-link"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

define i32 @_fib(i32 %value) local_unnamed_addr {
entry:
  %fibSyncReturnValue = tail call i32 @"fib_spdy.ts$$7fibSynci"(i32 %value)
  ret i32 %fibSyncReturnValue
}

define internal i32 @"fib_spdy.ts$$7fibSynci"(i32 %value) local_unnamed_addr {
entry:
  %cmpLE3 = icmp slt i32 %value, 3
  br i1 %cmpLE3, label %returnBlock, label %if.end

if.end:                                           ; preds = %entry, %if.end
  %value.tr5 = phi i32 [ %sub4, %if.end ], [ %value, %entry ]
  %accumulator.tr4 = phi i32 [ %add, %if.end ], [ 1, %entry ]
  %sub = add i32 %value.tr5, -2
  %fibSyncReturnValue = tail call i32 @"fib_spdy.ts$$7fibSynci"(i32 %sub)
  %sub4 = add nsw i32 %value.tr5, -1
  %add = add i32 %fibSyncReturnValue, %accumulator.tr4
  %cmpLE = icmp slt i32 %value.tr5, 4
  br i1 %cmpLE, label %returnBlock, label %if.end

returnBlock:                                      ; preds = %if.end, %entry
  %accumulator.tr.lcssa = phi i32 [ 1, %entry ], [ %add, %if.end ]
  ret i32 %accumulator.tr.lcssa
}

save-wast

The ---save-wast option saves the final WebAssembly module in its textual representation (using s-expressions). The compiler emits the file with the .wast file ending.

node packages/compiler/cli.js packages/benchmark/cases/fib-spdy.ts --save-wast
cat packages/benchmark/cases/fib-spdy.wast
(module
 (import "env" "memory" (memory $0 256))
 (table 0 anyfunc)
 (export "_fib" (func $_fib))
 (func $_fib (param $0 i32) (result i32)
  (call $fib_spdy.ts$$7fibSynci
   (get_local $0)
  )
 )
 (func $fib_spdy.ts$$7fibSynci (param $0 i32) (result i32)
  (local $1 i32)
  (set_local $1
   (i32.const 1)
  )
  (block $label$0
   (br_if $label$0
    (i32.lt_s
     (get_local $0)
     (i32.const 3)
    )
   )
   (set_local $1
    (i32.const 1)
   )
   (set_local $0
    (i32.add
     (get_local $0)
     (i32.const 1)
    )
   )
   (loop $label$1
    (set_local $1
     (i32.add
      (call $fib_spdy.ts$$7fibSynci
       (i32.add
        (get_local $0)
        (i32.const -3)
       )
      )
      (get_local $1)
     )
    )
    (br_if $label$1
     (i32.gt_s
      (tee_local $0
       (i32.add
        (get_local $0)
        (i32.const -1)
       )
      )
      (i32.const 3)
     )
    )
   )
  )
  (get_local $1)
 )
)
;; METADATA: { "asmConsts": {},"staticBump": 8, "initializers": [] }

Settings

The following additional settings are supported.

INITIAL_MEMORY: The initial size of the WebAssembly memory in bytes. The default value is 16 MB. The initially allocated memory grows if the application requires more. However, growing is potentially an expensive operation. Therefore, it is preferred to initialize the value with a reasonable value. The initial memory needs to be a multiple of the WebAssembly page size of 64KB.

TOTAL_STACK: The size of a stack in bytes. The default value is 5 MB.

GLOBAL_BASE: Where global data begins; the start of static memory.

Environment Variables

The following environment variables are respected by the CLI:

DEBUG: Enables the debug output. By setting DEBUG=* all debug output is logged to the console. For more details see debug. For example, on Unix systems use DEBUG=* node cli.js ....