A first take on imported built-ins

This commit is contained in:
dcodeIO 2017-12-12 01:35:48 +01:00
parent 09cbad6ede
commit ad1fbcf5b2
29 changed files with 127 additions and 37 deletions

View File

@ -3,7 +3,7 @@ AssemblyScript NEXT
[![Build Status](https://travis-ci.org/AssemblyScript/next.svg?branch=master)](https://travis-ci.org/AssemblyScript/next) [![Build Status](https://travis-ci.org/AssemblyScript/next.svg?branch=master)](https://travis-ci.org/AssemblyScript/next)
**AssemblyScript** is a new compiler targeting WebAssembly while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser. **AssemblyScript** is a new compiler targeting [WebAssembly](http://webassembly.org) while utilizing [TypeScript](http://www.typescriptlang.org)'s syntax and [node](https://nodejs.org)'s vibrant ecosystem. Instead of requiring complex toolchains to set up, you can simply `npm install` it - or run it in a browser.
By compiling a variant of TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill. By compiling a variant of TypeScript to [Binaryen](https://github.com/WebAssembly/binaryen) IR, the resulting module can be validated, optimized, emitted in WebAssembly text or binary format and converted to [asm.js](http://asmjs.org) as a polyfill.

4
assembly.d.ts vendored
View File

@ -90,6 +90,10 @@ declare function isNaN<T = f32 | f64>(value: T): bool;
declare function isFinite<T = f32 | f64>(value: T): bool; declare function isFinite<T = f32 | f64>(value: T): bool;
/** Traps if the specified value evaluates to `false`. */ /** Traps if the specified value evaluates to `false`. */
declare function assert(isTrue: bool): void; declare function assert(isTrue: bool): void;
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
// Internal decorators (not yet implemented) // Internal decorators (not yet implemented)

7
portable.d.ts vendored
View File

@ -52,6 +52,10 @@ declare function unreachable(): any; // sic
declare function changetype<T1,T2>(value: T1): T2; declare function changetype<T1,T2>(value: T1): T2;
/** Traps if the specified value evaluates to `false`. */ /** Traps if the specified value evaluates to `false`. */
declare function assert(isTrue: bool): void; declare function assert(isTrue: bool): void;
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses a floating point string to a 64-bit float. */
declare function parseFloat(str: string): f64;
// Portable standard library // Portable standard library
// Everything marked @deprecated is a temporary filler. Do not use. // Everything marked @deprecated is a temporary filler. Do not use.
@ -143,6 +147,3 @@ declare namespace console {
/** @deprecated */ /** @deprecated */
function log(message: string): void; function log(message: string): void;
} }
/** @deprecated */
declare function parseFloat(str: string): f64;

View File

@ -2,7 +2,7 @@ import { Compiler, Target, typeToNativeType, typeToNativeOne } from "./compiler"
import { DiagnosticCode } from "./diagnostics"; import { DiagnosticCode } from "./diagnostics";
import { Node, Expression } from "./ast"; import { Node, Expression } from "./ast";
import { Type } from "./types"; import { Type } from "./types";
import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType } from "./module"; import { Module, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module";
import { Program, FunctionPrototype, Local } from "./program"; import { Program, FunctionPrototype, Local } from "./program";
/** Initializes the specified program with built-in functions. */ /** Initializes the specified program with built-in functions. */
@ -33,23 +33,23 @@ export function initialize(program: Program): void {
addFunction(program, "isNaN", true); addFunction(program, "isNaN", true);
addFunction(program, "isFinite", true); addFunction(program, "isFinite", true);
addFunction(program, "assert"); addFunction(program, "assert");
// addFunction(program, "fmod", false, true); addFunction(program, "parseInt");
// addFunction(program, "pow", true, true); addFunction(program, "parseFloat");
} }
/** Adds a built-in function to the specified program. */ /** Adds a built-in function to the specified program. */
function addFunction(program: Program, name: string, isGeneric: bool = false, isImport: bool = false): void { function addFunction(program: Program, name: string, isGeneric: bool = false): void {
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null); let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
prototype.isBuiltIn = true; prototype.isBuiltIn = true;
prototype.isGeneric = isGeneric; prototype.isGeneric = isGeneric;
prototype.isImport = isImport;
program.elements.set(name, prototype); program.elements.set(name, prototype);
} }
/** Compiles a call to a built-in function. */ /** Compiles a call to a built-in function. */
export function compileCall(compiler: Compiler, internalName: string, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef { export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[], operands: Expression[], reportNode: Node): ExpressionRef {
const module: Module = compiler.module; const module: Module = compiler.module;
const usizeType: Type = select<Type>(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64); const usizeType: Type = select<Type>(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64);
const nativeUsizeType: NativeType = select<NativeType>(NativeType.I64, NativeType.I32, compiler.options.target == Target.WASM64);
let arg0: ExpressionRef, let arg0: ExpressionRef,
arg1: ExpressionRef, arg1: ExpressionRef,
@ -59,8 +59,9 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume
let tempLocal1: Local; let tempLocal1: Local;
let type: Type; let type: Type;
var ftype: FunctionTypeRef;
switch (internalName) { switch (prototype.internalName) {
case "clz": // clz<T>(value: T) -> T case "clz": // clz<T>(value: T) -> T
if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode))
@ -498,8 +499,42 @@ export function compileCall(compiler: Compiler, internalName: string, typeArgume
module.createUnreachable() module.createUnreachable()
); );
// case "fmod": case "parseInt": // takes a pointer to the string
// case "pow": compiler.currentType = Type.f64;
if (typeArguments.length != 0) {
compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10));
return module.createUnreachable();
}
if (operands.length < 1) {
compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10));
return module.createUnreachable();
}
if (operands.length > 2) {
compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10));
return module.createUnreachable();
}
if (!prototype.isCompiled) {
if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType, NativeType.I32 ])))
ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FIi" : "Fii", NativeType.F64, [ nativeUsizeType, NativeType.I32 ]);
module.addFunctionImport("parseInt", "env", "parseInt", ftype); // FIXME: can't call with i64 pointers (WASM64)
prototype.isCompiled = true;
}
arg0 = compiler.compileExpression(operands[0], usizeType); // reports
arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], Type.i32) : module.createI32(-1); // -1 marks omitted
return module.createCallImport("parseInt", [ arg0, arg1 ], NativeType.F64);
case "parseFloat": // takes a pointer to the string
compiler.currentType = Type.f64;
if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode))
return module.createUnreachable();
if (!prototype.isCompiled) {
if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType ])))
ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FI" : "Fi", NativeType.F64, [ nativeUsizeType ]);
module.addFunctionImport("parseFloat", "env", "parseFloat", ftype); // FIXME: can't call with i64 pointers (WASM64)
prototype.isCompiled = true;
}
arg0 = compiler.compileExpression(operands[0], usizeType); // reports
return module.createCallImport("parseFloat", [ arg0 ], NativeType.F64);
} }
return 0; return 0;
} }

View File

@ -1673,7 +1673,7 @@ export class Compiler extends DiagnosticEmitter {
functionInstance = <Function | null>functionPrototype.instances.get(sb.join(",")); functionInstance = <Function | null>functionPrototype.instances.get(sb.join(","));
if (!functionInstance) { if (!functionInstance) {
this.currentType = contextualType; this.currentType = contextualType;
let expr: ExpressionRef = compileBuiltinCall(this, functionPrototype.internalName, resolvedTypeArguments, expression.arguments, expression); let expr: ExpressionRef = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, expression);
if (!expr) { if (!expr) {
this.error(DiagnosticCode.Operation_not_supported, expression.range); this.error(DiagnosticCode.Operation_not_supported, expression.range);
return this.module.createUnreachable(); return this.module.createUnreachable();

View File

@ -41,6 +41,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
[program.exports] [program.exports]
;) ;)

View File

@ -844,6 +844,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
binary/b binary/b
binary/i binary/i
binary/I binary/I

View File

@ -1,6 +1,6 @@
let b: bool; let b: bool;
// integer builtins // integers
let i: i32; let i: i32;
@ -40,7 +40,7 @@ I = abs<i64>(-42); assert(I == 42);
I = max<i64>(1, 2); assert(I == 2); I = max<i64>(1, 2); assert(I == 2);
I = min<i64>(1, 2); assert(i == 1); I = min<i64>(1, 2); assert(i == 1);
// floating point builtins // floats
let f: f32; let f: f32;
@ -104,7 +104,7 @@ F = trunc<f64>(1.25);
b = isNaN<f64>(1.25); b = isNaN<f64>(1.25);
b = isFinite<f64>(1.25); b = isFinite<f64>(1.25);
// load and store builtins // load and store
i = load<i32>(8); store<i32>(8, i); i = load<i32>(8); store<i32>(8, i);
store<i32>(8, load<i32>(8)); store<i32>(8, load<i32>(8));
@ -115,7 +115,7 @@ store<f32>(8, load<f32>(8));
F = load<f64>(8); store<f64>(8, F); F = load<f64>(8); store<f64>(8, F);
store<f64>(8, load<f64>(8)); store<f64>(8, load<f64>(8));
// reinterpretation builtins // reinterpretation
reinterpret<f32,i32>(1.25); reinterpret<f32,i32>(1.25);
reinterpret<i32,f32>(25); reinterpret<i32,f32>(25);
@ -127,7 +127,7 @@ f = reinterpret<i32,f32>(25);
I = reinterpret<f64,i64>(1.25); I = reinterpret<f64,i64>(1.25);
F = reinterpret<i64,f64>(25); F = reinterpret<i64,f64>(25);
// host builtins // host
let s: usize; let s: usize;
@ -137,7 +137,7 @@ grow_memory(1);
s = current_memory(); s = current_memory();
s = grow_memory(1); s = grow_memory(1);
// other builtins // other
select<i32>(10, 20, true); select<i32>(10, 20, true);
select<i64>(100, 200, false); select<i64>(100, 200, false);
@ -151,7 +151,7 @@ F = select<f64>(12.5, 25.0, false);
if (0) unreachable(); if (0) unreachable();
// AS specific builtins // AS specific
sizeof<u8>(); sizeof<u8>();
sizeof<u16>(); sizeof<u16>();
@ -176,3 +176,11 @@ assert(!isFinite<f64>(NaN));
assert(!isFinite<f64>(Infinity)); assert(!isFinite<f64>(Infinity));
assert(isFinite<f32>(0)); assert(isFinite<f32>(0));
assert(isFinite<f64>(0)); assert(isFinite<f64>(0));
// imported
// TODO: Can't be interpreted due to 'Fatal: callImport: unknown import: env.parseInt'
// parseInt(0);
// parseInt(0, 10);
// parseFloat(0);

View File

@ -1087,6 +1087,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
builtins/b builtins/b
builtins/i builtins/i
builtins/I builtins/I

View File

@ -34,6 +34,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
declare/external declare/external
[program.exports] [program.exports]
declare/external declare/external

View File

@ -79,6 +79,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
do/loopDo do/loopDo
do/loopDoInDo do/loopDoInDo
[program.exports] [program.exports]

View File

@ -64,6 +64,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
enum/Implicit enum/Implicit
enum/Explicit enum/Explicit
enum/Mixed enum/Mixed

View File

@ -54,6 +54,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
export/add export/add
export/sub export/sub
export/a export/a

View File

@ -175,6 +175,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
for/i for/i
[program.exports] [program.exports]

View File

@ -336,6 +336,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
game-of-life/w game-of-life/w
game-of-life/h game-of-life/h
game-of-life/s game-of-life/s

View File

@ -22,8 +22,8 @@
(export "or" (func $i64/or)) (export "or" (func $i64/or))
(export "xor" (func $i64/xor)) (export "xor" (func $i64/xor))
(export "shl" (func $i64/shl)) (export "shl" (func $i64/shl))
(export "shr_u" (func $i64/shr_u))
(export "shr_s" (func $i64/shr_s)) (export "shr_s" (func $i64/shr_s))
(export "shr_u" (func $i64/shr_u))
(export "rotl" (func $i64/rotl_)) (export "rotl" (func $i64/rotl_))
(export "rotr" (func $i64/rotr_)) (export "rotr" (func $i64/rotr_))
(export "eq" (func $i64/eq)) (export "eq" (func $i64/eq))
@ -580,12 +580,12 @@
) )
) )
) )
(func $i64/shr_u (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (func $i64/shr_s (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 i64) (local $4 i64)
(set_global $i64/lo (set_global $i64/lo
(i32.wrap/i64 (i32.wrap/i64
(tee_local $4 (tee_local $4
(i64.shr_u (i64.shr_s
(i64.or (i64.or
(i64.extend_u/i32 (i64.extend_u/i32
(get_local $0) (get_local $0)
@ -621,12 +621,12 @@
) )
) )
) )
(func $i64/shr_s (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (func $i64/shr_u (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 i64) (local $4 i64)
(set_global $i64/lo (set_global $i64/lo
(i32.wrap/i64 (i32.wrap/i64
(tee_local $4 (tee_local $4
(i64.shr_s (i64.shr_u
(i64.or (i64.or
(i64.extend_u/i32 (i64.extend_u/i32
(get_local $0) (get_local $0)

View File

@ -102,14 +102,14 @@ export function shl(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void
hi = <u32>(ret >>> 32); hi = <u32>(ret >>> 32);
} }
export function shr_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { export function shr_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void {
const ret: u64 = (<u64>loLeft | <u64>hiLeft << 32) >> (<u64>loRight | <u64>hiRight << 32); const ret: u64 = <u64>(<i64>(<u64>loLeft | <u64>hiLeft << 32) >> <i64>(<u64>loRight | <u64>hiRight << 32));
lo = <u32>ret; lo = <u32>ret;
hi = <u32>(ret >>> 32); hi = <u32>(ret >>> 32);
} }
export function shr_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { export function shr_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void {
const ret: u64 = <u64>(<i64>(<u64>loLeft | <u64>hiLeft << 32) >> <i64>(<u64>loRight | <u64>hiRight << 32)); const ret: u64 = (<u64>loLeft | <u64>hiLeft << 32) >> (<u64>loRight | <u64>hiRight << 32);
lo = <u32>ret; lo = <u32>ret;
hi = <u32>(ret >>> 32); hi = <u32>(ret >>> 32);
} }

View File

@ -23,8 +23,8 @@
(export "or" (func $i64/or)) (export "or" (func $i64/or))
(export "xor" (func $i64/xor)) (export "xor" (func $i64/xor))
(export "shl" (func $i64/shl)) (export "shl" (func $i64/shl))
(export "shr_u" (func $i64/shr_u))
(export "shr_s" (func $i64/shr_s)) (export "shr_s" (func $i64/shr_s))
(export "shr_u" (func $i64/shr_u))
(export "rotl" (func $i64/rotl_)) (export "rotl" (func $i64/rotl_))
(export "rotr" (func $i64/rotr_)) (export "rotr" (func $i64/rotr_))
(export "eq" (func $i64/eq)) (export "eq" (func $i64/eq))
@ -642,11 +642,11 @@
) )
) )
) )
(func $i64/shr_u (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (func $i64/shr_s (; 17 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 i64) (local $4 i64)
(block (block
(set_local $4 (set_local $4
(i64.shr_u (i64.shr_s
(i64.or (i64.or
(i64.extend_u/i32 (i64.extend_u/i32
(get_local $0) (get_local $0)
@ -686,11 +686,11 @@
) )
) )
) )
(func $i64/shr_s (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (func $i64/shr_u (; 18 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 i64) (local $4 i64)
(block (block
(set_local $4 (set_local $4
(i64.shr_s (i64.shr_u
(i64.or (i64.or
(i64.extend_u/i32 (i64.extend_u/i32
(get_local $0) (get_local $0)
@ -1217,6 +1217,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
i64/lo i64/lo
i64/hi i64/hi
i64/getLo i64/getLo
@ -1236,8 +1238,8 @@
i64/or i64/or
i64/xor i64/xor
i64/shl i64/shl
i64/shr_u
i64/shr_s i64/shr_s
i64/shr_u
i64/rotl_ i64/rotl_
i64/rotr_ i64/rotr_
i64/eq i64/eq
@ -1268,8 +1270,8 @@
i64/or i64/or
i64/xor i64/xor
i64/shl i64/shl
i64/shr_u
i64/shr_s i64/shr_s
i64/shr_u
i64/rotl i64/rotl
i64/rotr i64/rotr
i64/eq i64/eq

View File

@ -74,6 +74,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
if/ifThenElse if/ifThenElse
if/ifThen if/ifThen
if/ifThenElseBlock if/ifThenElseBlock

View File

@ -66,6 +66,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
export/add export/add
export/sub export/sub
export/a export/a

View File

@ -167,6 +167,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
[program.exports] [program.exports]
;) ;)

View File

@ -295,6 +295,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
logical/i logical/i
logical/I logical/I
logical/f logical/f

View File

@ -2161,6 +2161,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
memcpy/memcpy memcpy/memcpy
memcpy/base memcpy/base
memcpy/dest memcpy/dest

View File

@ -72,6 +72,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
export/add export/add
export/sub export/sub
export/a export/a

View File

@ -173,6 +173,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
switch/doSwitch switch/doSwitch
switch/doSwitchDefaultFirst switch/doSwitchDefaultFirst
switch/doSwitchDefaultOmitted switch/doSwitchDefaultOmitted

View File

@ -61,6 +61,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
ternary/a ternary/a
[program.exports] [program.exports]

View File

@ -356,6 +356,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
tlsf/fls tlsf/fls
tlsf/ffs tlsf/ffs
tlsf/ALIGN_SIZE_LOG2 tlsf/ALIGN_SIZE_LOG2

View File

@ -661,6 +661,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
unary/i unary/i
unary/I unary/I
unary/f unary/f

View File

@ -88,6 +88,8 @@
isNaN isNaN
isFinite isFinite
assert assert
parseInt
parseFloat
while/loopWhile while/loopWhile
while/loopWhileInWhile while/loopWhileInWhile
[program.exports] [program.exports]