diff --git a/README.md b/README.md index b561ab2a..9f1ea02c 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ A few early examples to get an idea: A PSON decoder implemented in AssemblyScript. * **[TLSF memory allocator](./examples/tlsf)**
- An port of TLSF to AssemblyScript. + A port of TLSF to AssemblyScript. * **[μgc garbage collector](./examples/ugc)**
- An port of μgc to AssemblyScript. + A port of μgc to AssemblyScript. Or browse the [compiler tests](./tests/compiler) for a more in-depth overview of what's supported already. One of them is a [showcase](./tests/compiler/showcase.ts). diff --git a/bin/asc.js b/bin/asc.js index 7f150156..6ab7a97c 100644 --- a/bin/asc.js +++ b/bin/asc.js @@ -32,7 +32,7 @@ Object.keys(conf).forEach(key => { var args = minimist(process.argv.slice(2), opts); var version = require("../package.json").version; -var indent = 20; +var indent = 24; if (isDev) version += "-dev"; if (args.version) { @@ -47,9 +47,9 @@ if (args.help || args._.length < 1) { Object.keys(conf).forEach(name => { var option = conf[name]; var text = " "; - if (option.aliases && option.aliases[0].length === 1) - text += "-" + option.aliases[0] + ", "; text += "--" + name; + if (option.aliases && option.aliases[0].length === 1) + text += ", -" + option.aliases[0]; while (text.length < indent) text += " "; if (Array.isArray(option.desc)) { @@ -164,15 +164,63 @@ else if (args.trapMode !== "allow") { process.exit(1); } -if (args.optimize) - module.optimize(); +var optimizeLevel = 0; +var shrinkLevel = 0; +var debugInfo = !args.noDebug; +var runPasses = []; +if (args["O"]) { + if (typeof args["O"] === "number") + optimizeLevel = args["O"]; + else if (args["O"] === true) { + optimizeLevel = 2; + shrinkLevel = 1; + } else if (args["0"]) + optimizeLevel = 0; + else if (args["1"]) + optimizeLevel = 1; + else if (args["2"]) + optimizeLevel = 2; + else if (args["3"]) + optimizeLevel = 3; + else + optimizeLevel = 2; +} +if (args["s"]) + shrinkLevel = 1; +else if (args["z"]) + shrinkLevel = 2; + +// Check explicit levels +if (typeof args.optimizeLevel === "number") + optimizeLevel = args.optimizeLevel; +if (typeof args.shrinkLevel === "number") + shrinkLevel = args.shrinkLevel; + +// Workaround for inlining not being performed (42.0.0) +if ((optimizeLevel >= 2 || shrinkLevel >= 2) && !debugInfo) + runPasses = [ "inlining", "inlining-optimizing" ]; + +// Check additional passes if (args.runPasses) { if (typeof args.runPasses === "string") args.runPasses = args.runPasses.split(","); - module.runPasses(args.runPasses.map(pass => pass.trim())); + if (args.runPasses.length) + args.runPasses.forEach(pass => { + if (runPasses.indexOf(pass) < 0) + runPasses.push(pass); + }); } +module.setOptimizeLevel(optimizeLevel); +module.setShrinkLevel(shrinkLevel); +module.setDebugInfo(debugInfo); + +if (optimizeLevel || shrinkLevel) + module.optimize(); +if (runPasses.length) + module.runPasses(runPasses.map(pass => pass.trim())); + var hasOutput = false; if (args.outFile != null) { diff --git a/bin/asc.json b/bin/asc.json index b165abfe..236c1f5c 100644 --- a/bin/asc.json +++ b/bin/asc.json @@ -10,10 +10,27 @@ "aliases": [ "h" ] }, "optimize": { - "desc": "Optimizes the module.", - "type": "boolean", + "desc": [ + "Optimizes the module. Also accepts the optimize level:", + " -O Equivalent to -O2s", + " -O0 Runs no optimization passes", + " -O1 Runs fast optimization passes", + " -O2 Runs default optimization passes", + " -O3 Runs all optimization passes", + " -O2s Specifies optimize level 2 with shrink level 1", + " -O3z etc." + ], + "type": "number", "aliases": [ "O" ] }, + "optimizeLevel": { + "desc": "How much to focus on optimizing code.", + "type": "number" + }, + "shrinkLevel": { + "desc": "How much to focus on shrinking code size.", + "type": "number" + }, "validate": { "desc": "Validates the module.", "type": "boolean", @@ -43,6 +60,10 @@ "desc": "Disables tree-shaking.", "type": "boolean" }, + "noDebug": { + "desc": "Disables debug information in binaries.", + "type": "boolean" + }, "noAssert": { "desc": "Disables assertions.", "type": "boolean" diff --git a/examples/tlsf/package.json b/examples/tlsf/package.json index cea56016..eab049eb 100644 --- a/examples/tlsf/package.json +++ b/examples/tlsf/package.json @@ -5,7 +5,7 @@ "scripts": { "build": "npm run build:untouched && npm run build:optimized", "build:untouched": "asc assembly/tlsf.ts -t tlsf.untouched.wast -b tlsf.untouched.wasm --validate", - "build:optimized": "asc -O assembly/tlsf.ts -b tlsf.optimized.wasm -t tlsf.optimized.wast --validate --noAssert --runPasses inlining", + "build:optimized": "asc -O3 assembly/tlsf.ts -b tlsf.optimized.wasm -t tlsf.optimized.wast --validate --noDebug --noAssert", "test": "node tests" } } diff --git a/examples/ugc/assembly/ugc.ts b/examples/ugc/assembly/ugc.ts index 90696948..ee4ed065 100644 --- a/examples/ugc/assembly/ugc.ts +++ b/examples/ugc/assembly/ugc.ts @@ -20,6 +20,8 @@ class ObjectHeader { ///////////////////////////////// Fields //////////////////////////////////// + // the next and prev pointer with tags in the least significant two bits that + // would otherwise be zero (blocks are guaranteed to be aligned to 4/8 bytes) tagged_next: usize; tagged_prev: usize; @@ -173,13 +175,13 @@ class Control { gc_scan_fn(this, obj); } else { gc_scan_fn(this, null); - obj = this.iterator.next; + obj = this.iterator.next; // already strips tags, see * if (obj == this.to) { var from = this.from; this.from = this.to; this.to = from; this.white = white ^ 1; - this.iterator = from.next; + this.iterator = changetype(from.tagged_next); // * this.state = SWEEP; } } @@ -233,10 +235,10 @@ class Control { } } +// TODO: should happen dynamically so it DCE's if all objects are unmanaged var GC = Control.create(HEAP_BASE); -var GC_BASE = HEAP_BASE + Control.SIZE; - -GC.register(changetype(GC_BASE)); +// var someObject = allocate_memory(64); +// GC.register(changetype(someObject)); // Exported interface @@ -259,7 +261,7 @@ export function gc_collect(): void { } // TODO: these functions must be generated by the compiler and combined by -// any potential linker. They live here for now to document their structure. +// a potential linker. They live here for now to document their structure. function gc_scan_fn(control: Control, header: ObjectHeader | null): void { if (!header) { diff --git a/examples/ugc/package.json b/examples/ugc/package.json index 138aef2a..a48ce201 100644 --- a/examples/ugc/package.json +++ b/examples/ugc/package.json @@ -5,7 +5,7 @@ "scripts": { "build": "npm run build:untouched && npm run build:optimized", "build:untouched": "asc assembly/ugc.ts -t ugc.untouched.wast -b ugc.untouched.wasm --validate", - "build:optimized": "asc -O assembly/ugc.ts -b ugc.optimized.wasm -t ugc.optimized.wast --validate --noAssert --runPasses inlining", + "build:optimized": "asc -O3 assembly/ugc.ts -b ugc.optimized.wasm -t ugc.optimized.wast --validate --noDebug --noAssert", "test": "node tests" } } diff --git a/package-lock.json b/package-lock.json index ff41be5a..923af15a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -274,9 +274,9 @@ "dev": true }, "binaryen": { - "version": "40.0.0-nightly.20171229", - "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-40.0.0-nightly.20171229.tgz", - "integrity": "sha512-P9VXMphJKRZbdr0AAmkpgRPGVWbnDmcqt8NPuZ+W0eSeC1igGDLdreJteCdtHA7Z+qRQ4BWqtmKPZEJKRZk47w==" + "version": "42.0.0", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-42.0.0.tgz", + "integrity": "sha512-1JkYPfxkkjkTrG1QekDeMyNdwbA/RIvlkpio+BJ41po9X6d7qZnlQHM/CNVhgXCtmGzlw2hbkAyYxEfnE001vw==" }, "bn.js": { "version": "4.11.8", diff --git a/package.json b/package.json index da6b76ad..7e5ba40c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "https://github.com/AssemblyScript/assemblyscript/issues" }, "dependencies": { - "binaryen": "40.0.0-nightly.20171229", + "binaryen": "42.0.0", "glob": "^7.1.2", "minimist": "^1.2.0" }, diff --git a/src/module.ts b/src/module.ts index 4626c1d8..8e537aad 100644 --- a/src/module.ts +++ b/src/module.ts @@ -727,9 +727,19 @@ export class Module { _BinaryenSetStart(this.ref, func); } + setOptimizeLevel(level: i32 = 2): void { + _BinaryenSetOptimizeLevel(level); + } + + setShrinkLevel(level: i32 = 1): void { + _BinaryenSetShrinkLevel(level); + } + + setDebugInfo(on: bool = false): void { + _BinaryenSetDebugInfo(on); + } + optimize(func: FunctionRef = 0): void { - // see: https://github.com/WebAssembly/binaryen/issues/1331#issuecomment-350328175 - // this.runPasses([ "flatten", "ssa" ], func); if (func) { _BinaryenFunctionOptimize(func, this.ref); } else {