From df3e34f2aad404d6a70a5b476168f4b91cc79ade Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Mon, 4 Dec 2017 19:26:50 +0100 Subject: [PATCH] More options for asc --- bin/asc.json | 8 ++ bin/asc.ts | 16 ++- src/compiler.ts | 40 ++++-- src/glue/js.ts | 20 +++ src/module.ts | 12 +- src/program.ts | 5 + tests/binaryen/export-import.js | 13 ++ tests/compiler.ts | 11 -- tests/compiler/builtins.optimized.wast | 31 ++++- tests/compiler/builtins.ts | 10 +- tests/compiler/builtins.wast | 31 ++++- tests/compiler/declare.optimized.wast | 5 + tests/compiler/declare.ts | 4 + tests/compiler/declare.wast | 37 ++++++ tests/compiler/logical.optimized.wast | 152 +++++++++++++++++++++++ tests/compiler/logical.ts | 40 ++++++ tests/compiler/logical.wast | 162 +++++++++++++++++++++++++ 17 files changed, 565 insertions(+), 32 deletions(-) create mode 100644 tests/binaryen/export-import.js create mode 100644 tests/compiler/declare.optimized.wast create mode 100644 tests/compiler/declare.ts create mode 100644 tests/compiler/declare.wast diff --git a/bin/asc.json b/bin/asc.json index 04f86cb3..9009fbf5 100644 --- a/bin/asc.json +++ b/bin/asc.json @@ -17,5 +17,13 @@ "validate": { "desc": "Validate the module.", "type": "boolean" + }, + "outFile": { + "desc": "Specify the output file (binary format).", + "type": "string" + }, + "textFile": { + "desc": "Specify the output file (text format).", + "type": "string" } } diff --git a/bin/asc.ts b/bin/asc.ts index 0770c375..f92e0a39 100644 --- a/bin/asc.ts +++ b/bin/asc.ts @@ -1,5 +1,3 @@ -/// - import * as fs from "fs"; import * as path from "path"; import * as minimist from "minimist"; @@ -112,5 +110,17 @@ if (args["validate"]) if (args["optimize"]) module.optimize(); -_BinaryenModulePrint(module.ref); +let hasOutput = false; + +if (args["outFile"]) { + fs.writeFileSync(args["outFile"], module.toBinary()); + hasOutput = true; +} +if (args["textFile"]) { + fs.writeFileSync(args["textFile"], module.toText(), { encoding: "utf8" }); + hasOutput = true; +} +if (!hasOutput) + module.print(); + module.dispose(); diff --git a/src/compiler.ts b/src/compiler.ts index 4db27486..2d0509f6 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -412,7 +412,9 @@ export class Compiler extends DiagnosticEmitter { if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE) throw new Error("unexpected missing function"); const instance: Function | null = this.compileFunctionUsingTypeArguments(element, typeArguments, contextualTypeArguments, alternativeReportNode); - if (instance && declaration.range.source.isEntry && declaration.parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers)) + if (!instance) + return; + if (declaration.range.source.isEntry && declaration.parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers)) this.module.addExport(instance.internalName, declaration.identifier.name); } @@ -431,19 +433,29 @@ export class Compiler extends DiagnosticEmitter { if (!declaration) throw new Error("unexpected missing declaration"); - if (!declaration.statements) { - this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.identifier.range); - return false; + if (instance.isDeclare) { + if (declaration.statements) { + this.error(DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts, declaration.identifier.range); + return false; + } + } else { + if (!declaration.statements) { + this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.identifier.range); + return false; + } } instance.isCompiled = true; // compile statements - const previousFunction: Function = this.currentFunction; - this.currentFunction = instance; - const stmts: ExpressionRef[] = this.compileStatements(declaration.statements); - this.currentFunction = previousFunction; + let stmts: ExpressionRef[] | null = null; + if (!instance.isDeclare) { + const previousFunction: Function = this.currentFunction; + this.currentFunction = instance; + stmts = this.compileStatements(declaration.statements); + this.currentFunction = previousFunction; + } - // create the function + // create the function type let k: i32 = instance.parameters.length; const binaryenResultType: NativeType = typeToNativeType(instance.returnType); const binaryenParamTypes: NativeType[] = new Array(k); @@ -456,8 +468,14 @@ export class Compiler extends DiagnosticEmitter { let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes); if (!typeRef) typeRef = this.module.addFunctionType(signatureNameParts.join(""), binaryenResultType, binaryenParamTypes); + + // create the function const internalName: string = instance.internalName; - this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, stmts, NativeType.None)); + if (instance.isDeclare) { + this.module.addImport(internalName, "env", declaration.identifier.name, typeRef); + } else { + this.module.addFunction(internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, stmts, NativeType.None)); + } return true; } @@ -1732,7 +1750,7 @@ export class Compiler extends DiagnosticEmitter { this.compileFunction(functionInstance); // imported function - if (functionInstance.isImport) + if (functionInstance.isDeclare) return this.module.createCallImport(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType)); // internal function diff --git a/src/glue/js.ts b/src/glue/js.ts index dec17c2a..c9ca9096 100644 --- a/src/glue/js.ts +++ b/src/glue/js.ts @@ -12,3 +12,23 @@ const binaryen = require("binaryen"); for (const key in binaryen) if (/^_(?:Binaryen|Relooper|malloc$|free$)/.test(key)) globalScope[key] = binaryen[key]; + +import { Module } from "../module"; + +Module.prototype.toBinary = function(bufferSize = 1048576): Uint8Array { + const ptr = _malloc(bufferSize); + const len = this.write(ptr, bufferSize); + const ret = new Uint8Array(len); + ret.set(binaryen.HEAPU8.subarray(ptr, ptr + len)); + _free(ptr); + return ret; +} + +Module.prototype.toText = function(): string { + let previousPrint: any = (binaryen)["print"]; + let ret: string = ""; + binaryen["print"] = function(x: string): void { ret += x + "\n" }; + this.print(); + binaryen["print"] = previousPrint; + return ret; +} diff --git a/src/module.ts b/src/module.ts index 1a5d5440..89036161 100644 --- a/src/module.ts +++ b/src/module.ts @@ -617,11 +617,21 @@ export class Module { return _BinaryenModuleInterpret(this.ref); } - toBinary(): Uint8Array { + write(output: usize, outputSize: usize = 1048576): usize { + return _BinaryenModuleWrite(this.ref, output, outputSize); + } + + print(): void { + return _BinaryenModulePrint(this.ref); + } + + toBinary(bufferSize: usize = 1048576): Uint8Array { + // FIXME: target specific / JS glue overrides this throw new Error("not implemented"); } toText(): string { + // FIXME: target specific / JS glue overrides this throw new Error("not implemented"); } diff --git a/src/program.ts b/src/program.ts index c2bf4c7f..5a0530c1 100644 --- a/src/program.ts +++ b/src/program.ts @@ -380,6 +380,9 @@ export class Program extends DiagnosticEmitter { else this.exports.set(internalName, prototype); } + if (hasModifier(ModifierKind.DECLARE, declaration.modifiers)) { + prototype.isDeclare = true; + } } private initializeImports(statement: ImportStatement, queuedExports: Map, queuedImports: QueuedImport[]): void { @@ -689,6 +692,7 @@ export abstract class Element { isCompiled: bool = false; isImport: bool = false; isBuiltin: bool = false; + isDeclare: bool = false; constructor(program: Program, internalName: string) { this.program = program; @@ -922,6 +926,7 @@ export class Function extends Element { this.returnType = returnType; this.instanceMethodOf = instanceMethodOf; this.isBuiltin = prototype.isBuiltin; + this.isDeclare = prototype.isDeclare; let localIndex: i32 = 0; if (instanceMethodOf) { this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type)); diff --git a/tests/binaryen/export-import.js b/tests/binaryen/export-import.js new file mode 100644 index 00000000..fa40596f --- /dev/null +++ b/tests/binaryen/export-import.js @@ -0,0 +1,13 @@ +var binaryen = require("binaryen"); + +// "unexpected false: module function exports must be found" + +var mod = new binaryen.Module(); + +var funcType = mod.addFunctionType("v", binaryen.none, []); +var func = mod.addImport("test", "env", "test", funcType); +mod.addExport("test", "test"); + +console.log(mod.emitText()); +if (!mod.validate()) + console.log("-> does not validate"); diff --git a/tests/compiler.ts b/tests/compiler.ts index 4c15bdeb..64e78d26 100644 --- a/tests/compiler.ts +++ b/tests/compiler.ts @@ -11,17 +11,6 @@ import { Module } from "../src/module"; import { Parser } from "../src/parser"; import { diff } from "./util/diff"; -// TODO: implement properly in module.ts -import * as binaryen from "binaryen"; -Module.prototype.toText = function(): string { - let old: any = (binaryen)["print"]; - let ret: string = ""; - (binaryen)["print"] = function(x: string): void { ret += x + "\n" }; - _BinaryenModulePrint(this.ref); - (binaryen)["print"] = old; - return ret; -} - const isCreate = process.argv[2] === "--create"; const filter = process.argv.length > 2 && !isCreate ? "*" + process.argv[2] + "*.ts" : "*.ts"; diff --git a/tests/compiler/builtins.optimized.wast b/tests/compiler/builtins.optimized.wast index 4d12c32b..0f9786de 100644 --- a/tests/compiler/builtins.optimized.wast +++ b/tests/compiler/builtins.optimized.wast @@ -177,13 +177,40 @@ ) (set_global $builtins/i (i32.load - (i32.const 4) + (i32.const 8) ) ) (i32.store - (i32.const 4) + (i32.const 8) (get_global $builtins/i) ) + (set_global $builtins/I + (i64.load + (i32.const 8) + ) + ) + (i64.store + (i32.const 8) + (get_global $builtins/I) + ) + (set_global $builtins/f + (f32.load + (i32.const 8) + ) + ) + (f32.store + (i32.const 8) + (get_global $builtins/f) + ) + (set_global $builtins/F + (f64.load + (i32.const 8) + ) + ) + (f64.store + (i32.const 8) + (get_global $builtins/F) + ) (if (f32.eq (tee_local $0 diff --git a/tests/compiler/builtins.ts b/tests/compiler/builtins.ts index 2fa18144..5b66c599 100644 --- a/tests/compiler/builtins.ts +++ b/tests/compiler/builtins.ts @@ -119,8 +119,14 @@ sizeof(); sizeof(); sizeof(); -i = load(4); -store(4, i); +i = load(8); +store(8, i); +I = load(8); +store(8, I); +f = load(8); +store(8, f); +F = load(8); +store(8, F); if (NaN == NaN) unreachable(); diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index 236a3024..d7b940d4 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -520,13 +520,40 @@ ) (set_global $builtins/i (i32.load - (i32.const 4) + (i32.const 8) ) ) (i32.store - (i32.const 4) + (i32.const 8) (get_global $builtins/i) ) + (set_global $builtins/I + (i64.load + (i32.const 8) + ) + ) + (i64.store + (i32.const 8) + (get_global $builtins/I) + ) + (set_global $builtins/f + (f32.load + (i32.const 8) + ) + ) + (f32.store + (i32.const 8) + (get_global $builtins/f) + ) + (set_global $builtins/F + (f64.load + (i32.const 8) + ) + ) + (f64.store + (i32.const 8) + (get_global $builtins/F) + ) (if (f64.eq (f64.const nan:0x8000000000000) diff --git a/tests/compiler/declare.optimized.wast b/tests/compiler/declare.optimized.wast new file mode 100644 index 00000000..e84d4561 --- /dev/null +++ b/tests/compiler/declare.optimized.wast @@ -0,0 +1,5 @@ +(module + (memory $0 1) + (data (i32.const 4) "\08") + (export "memory" (memory $0)) +) diff --git a/tests/compiler/declare.ts b/tests/compiler/declare.ts new file mode 100644 index 00000000..f0c8f543 --- /dev/null +++ b/tests/compiler/declare.ts @@ -0,0 +1,4 @@ +declare function external(): void; + +// FIXME: "unexpected false: module function exports must be found" +export { external }; diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast new file mode 100644 index 00000000..1b3c89cd --- /dev/null +++ b/tests/compiler/declare.wast @@ -0,0 +1,37 @@ +(module + (type $v (func)) + (import "env" "external" (func $declare/external)) + (memory $0 1) + (data (i32.const 4) "\08\00\00\00") + (export "external" (func $declare/external)) + (export "memory" (memory $0)) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + current_memory + grow_memory + unreachable + isNaN + isFinite + assert + sizeof + load + store + declare/external +[program.exports] + declare/external +;) diff --git a/tests/compiler/logical.optimized.wast b/tests/compiler/logical.optimized.wast index e67c8205..18a40836 100644 --- a/tests/compiler/logical.optimized.wast +++ b/tests/compiler/logical.optimized.wast @@ -1,5 +1,9 @@ (module (type $v (func)) + (global $logical/i (mut i32) (i32.const 0)) + (global $logical/I (mut i64) (i64.const 0)) + (global $logical/f (mut f32) (f32.const 0)) + (global $logical/F (mut f64) (f64.const 0)) (memory $0 1) (data (i32.const 4) "\08") (export "memory" (memory $0)) @@ -7,6 +11,8 @@ (func $start (; 0 ;) (type $v) (local $0 i32) (local $1 f64) + (local $2 i64) + (local $3 f32) (if (tee_local $0 (i32.const 0) @@ -71,5 +77,151 @@ ) (unreachable) ) + (set_global $logical/i + (if (result i32) + (tee_local $0 + (i32.const 1) + ) + (i32.const 2) + (get_local $0) + ) + ) + (if + (i32.ne + (get_global $logical/i) + (i32.const 2) + ) + (unreachable) + ) + (set_global $logical/i + (if (result i32) + (tee_local $0 + (i32.const 0) + ) + (get_local $0) + (i32.const 1) + ) + ) + (if + (i32.ne + (get_global $logical/i) + (i32.const 1) + ) + (unreachable) + ) + (set_global $logical/I + (if (result i64) + (i64.ne + (tee_local $2 + (i64.const 1) + ) + (i64.const 0) + ) + (i64.const 2) + (get_local $2) + ) + ) + (if + (i64.ne + (get_global $logical/I) + (i64.const 2) + ) + (unreachable) + ) + (set_global $logical/I + (if (result i64) + (i64.ne + (tee_local $2 + (i64.const 0) + ) + (i64.const 0) + ) + (get_local $2) + (i64.const 1) + ) + ) + (if + (i64.ne + (get_global $logical/I) + (i64.const 1) + ) + (unreachable) + ) + (set_global $logical/f + (if (result f32) + (f32.ne + (tee_local $3 + (f32.const 1) + ) + (f32.const 0) + ) + (f32.const 2) + (get_local $3) + ) + ) + (if + (f32.ne + (get_global $logical/f) + (f32.const 2) + ) + (unreachable) + ) + (set_global $logical/f + (if (result f32) + (f32.ne + (tee_local $3 + (f32.const 0) + ) + (f32.const 0) + ) + (get_local $3) + (f32.const 1) + ) + ) + (if + (f32.ne + (get_global $logical/f) + (f32.const 1) + ) + (unreachable) + ) + (set_global $logical/F + (if (result f64) + (f64.ne + (tee_local $1 + (f64.const 1) + ) + (f64.const 0) + ) + (f64.const 2) + (get_local $1) + ) + ) + (if + (f64.ne + (get_global $logical/F) + (f64.const 2) + ) + (unreachable) + ) + (set_global $logical/F + (if (result f64) + (f64.ne + (tee_local $1 + (f64.const 0) + ) + (f64.const 0) + ) + (get_local $1) + (f64.const 1) + ) + ) + (if + (f64.ne + (get_global $logical/F) + (f64.const 1) + ) + (unreachable) + ) ) ) diff --git a/tests/compiler/logical.ts b/tests/compiler/logical.ts index 01f0928c..68dc4f89 100644 --- a/tests/compiler/logical.ts +++ b/tests/compiler/logical.ts @@ -5,3 +5,43 @@ 1 && 2 || unreachable(); 1.0 && 2.0 || unreachable(); + +let i: i32; + +i = 1 && 2; +if (i != 2) + unreachable(); + +i = 0 || 1; +if (i != 1) + unreachable(); + +let I: i64; + +I = 1 && 2; +if (I != 2) + unreachable(); + +I = 0 || 1; +if (I != 1) + unreachable(); + +let f: f32; + +f = 1.0 && 2.0; +if (f != 2.0) + unreachable(); + +f = 0.0 || 1.0; +if (f != 1.0) + unreachable(); + +let F: f64; + +F = 1.0 && 2.0; +if (F != 2.0) + unreachable(); + +F = 0.0 || 1.0; +if (F != 1.0) + unreachable(); diff --git a/tests/compiler/logical.wast b/tests/compiler/logical.wast index 8dca9c4b..d2b8f5e4 100644 --- a/tests/compiler/logical.wast +++ b/tests/compiler/logical.wast @@ -1,5 +1,9 @@ (module (type $v (func)) + (global $logical/i (mut i32) (i32.const 0)) + (global $logical/I (mut i64) (i64.const 0)) + (global $logical/f (mut f32) (f32.const 0)) + (global $logical/F (mut f64) (f64.const 0)) (memory $0 1) (data (i32.const 4) "\08\00\00\00") (export "memory" (memory $0)) @@ -13,6 +17,14 @@ (local $5 i32) (local $6 f64) (local $7 f64) + (local $8 i32) + (local $9 i32) + (local $10 i64) + (local $11 i64) + (local $12 f32) + (local $13 f32) + (local $14 f64) + (local $15 f64) (drop (if (result i32) (tee_local $0 @@ -91,6 +103,152 @@ (unreachable) ) ) + (set_global $logical/i + (if (result i32) + (tee_local $8 + (i32.const 1) + ) + (i32.const 2) + (get_local $8) + ) + ) + (if + (i32.ne + (get_global $logical/i) + (i32.const 2) + ) + (unreachable) + ) + (set_global $logical/i + (if (result i32) + (tee_local $9 + (i32.const 0) + ) + (get_local $9) + (i32.const 1) + ) + ) + (if + (i32.ne + (get_global $logical/i) + (i32.const 1) + ) + (unreachable) + ) + (set_global $logical/I + (if (result i64) + (i64.ne + (tee_local $10 + (i64.const 1) + ) + (i64.const 0) + ) + (i64.const 2) + (get_local $10) + ) + ) + (if + (i64.ne + (get_global $logical/I) + (i64.const 2) + ) + (unreachable) + ) + (set_global $logical/I + (if (result i64) + (i64.ne + (tee_local $11 + (i64.const 0) + ) + (i64.const 0) + ) + (get_local $11) + (i64.const 1) + ) + ) + (if + (i64.ne + (get_global $logical/I) + (i64.const 1) + ) + (unreachable) + ) + (set_global $logical/f + (if (result f32) + (f32.ne + (tee_local $12 + (f32.const 1) + ) + (f32.const 0) + ) + (f32.const 2) + (get_local $12) + ) + ) + (if + (f32.ne + (get_global $logical/f) + (f32.const 2) + ) + (unreachable) + ) + (set_global $logical/f + (if (result f32) + (f32.ne + (tee_local $13 + (f32.const 0) + ) + (f32.const 0) + ) + (get_local $13) + (f32.const 1) + ) + ) + (if + (f32.ne + (get_global $logical/f) + (f32.const 1) + ) + (unreachable) + ) + (set_global $logical/F + (if (result f64) + (f64.ne + (tee_local $14 + (f64.const 1) + ) + (f64.const 0) + ) + (f64.const 2) + (get_local $14) + ) + ) + (if + (f64.ne + (get_global $logical/F) + (f64.const 2) + ) + (unreachable) + ) + (set_global $logical/F + (if (result f64) + (f64.ne + (tee_local $15 + (f64.const 0) + ) + (f64.const 0) + ) + (get_local $15) + (f64.const 1) + ) + ) + (if + (f64.ne + (get_global $logical/F) + (f64.const 1) + ) + (unreachable) + ) ) ) (; @@ -118,6 +276,10 @@ sizeof load store + logical/i + logical/I + logical/f + logical/F [program.exports] ;)