Minor refactoring; Fix n-body TS build

This commit is contained in:
dcodeIO 2018-04-28 18:07:20 +02:00
parent 2aea14b518
commit 8b5d1d7f74
13 changed files with 296 additions and 310 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -175,7 +175,7 @@ function asmFunc(global, env, buffer) {
function assembly_index_NBodySystem_constructor($0, $1) { function assembly_index_NBodySystem_constructor($0, $1) {
$0 = $0 | 0; $0 = $0 | 0;
$1 = $1 | 0; $1 = $1 | 0;
var $2 = 0, $3 = 0, $4 = 0.0, $5 = 0.0, $6 = 0.0, $7 = 0.0, $8 = 0, $8 = 0, $10 = 0; var $2 = 0, $3 = 0, $4 = 0.0, $5 = 0.0, $6 = 0.0, $7 = 0.0, $8 = 0, $9 = 0;
$2 = $1; $2 = $1;
$8 = HEAP32[($2 + 4 | 0) >> 2] | 0; $8 = HEAP32[($2 + 4 | 0) >> 2] | 0;
continue_0 : do { continue_0 : do {
@ -193,17 +193,16 @@ function asmFunc(global, env, buffer) {
} while (1); } while (1);
$2 = $1; $2 = $1;
$2 = HEAPU32[$2 >> 2] | 0; $2 = HEAPU32[$2 >> 2] | 0;
if (0 >>> 0 < ((HEAP32[$2 >> 2] | 0) >>> 2 | 0) >>> 0) $10 = HEAPU32[(($2 + 0 | 0) + 8 | 0) >> 2] | 0; else abort(); if (0 >>> 0 < ((HEAP32[$2 >> 2] | 0) >>> 2 | 0) >>> 0) $9 = HEAPU32[(($2 + 0 | 0) + 8 | 0) >> 2] | 0; else abort();
$2 = $10; $2 = $9;
HEAPF64[($2 + 24 | 0) >> 3] = -$5 / 39.47841760435743; HEAPF64[($2 + 24 | 0) >> 3] = -$5 / 39.47841760435743;
HEAPF64[($2 + 32 | 0) >> 3] = -$6 / 39.47841760435743; HEAPF64[($2 + 32 | 0) >> 3] = -$6 / 39.47841760435743;
HEAPF64[($2 + 40 | 0) >> 3] = -$7 / 39.47841760435743; HEAPF64[($2 + 40 | 0) >> 3] = -$7 / 39.47841760435743;
if ($0) $8 = $0; else { if (($0 | 0) == (0 | 0)) {
$0 = $lib_allocator_arena_allocate_memory(4 | 0) | 0; $0 = $lib_allocator_arena_allocate_memory(4 | 0) | 0;
HEAP32[$0 >> 2] = $1; HEAP32[$0 >> 2] = $1;
$8 = $0;
} }
return $8 | 0; return $0 | 0;
} }
function assembly_index_init() { function assembly_index_init() {

View File

@ -1,6 +1,8 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
require("allocator/arena"); require("allocator/arena");
// From The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org
const SOLAR_MASS = 4.0 * Math.PI * Math.PI; const SOLAR_MASS = 4.0 * Math.PI * Math.PI;
const DAYS_PER_YEAR = 365.24; const DAYS_PER_YEAR = 365.24;
class Body { class Body {
@ -37,25 +39,27 @@ function Neptune() {
} }
class NBodySystem { class NBodySystem {
constructor(bodies) { constructor(bodies) {
this.bodies = bodies;
var px = 0.0; var px = 0.0;
var py = 0.0; var py = 0.0;
var pz = 0.0; var pz = 0.0;
var size = bodies.length; var size = bodies.length;
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
let b = bodies[i]; let b = unchecked(bodies[i]);
let m = b.mass; let m = b.mass;
px += b.vx * m; px += b.vx * m;
py += b.vy * m; py += b.vy * m;
pz += b.vz * m; pz += b.vz * m;
} }
this.bodies = bodies; bodies[0].offsetMomentum(px, py, pz);
this.bodies[0].offsetMomentum(px, py, pz);
} }
advance(dt) { advance(dt) {
var bodies = this.bodies; var bodies = this.bodies;
var size = bodies.length; var size = bodies.length;
// var buffer = changetype<usize>(bodies.buffer_);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
let bodyi = bodies[i]; let bodyi = unchecked(bodies[i]);
// let bodyi = load<Body>(buffer + i * sizeof<Body>(), 8);
let ix = bodyi.x; let ix = bodyi.x;
let iy = bodyi.y; let iy = bodyi.y;
let iz = bodyi.z; let iz = bodyi.z;
@ -64,7 +68,8 @@ class NBodySystem {
let bivz = bodyi.vz; let bivz = bodyi.vz;
let bodyim = bodyi.mass; let bodyim = bodyi.mass;
for (let j = i + 1; j < size; ++j) { for (let j = i + 1; j < size; ++j) {
let bodyj = bodies[j]; let bodyj = unchecked(bodies[j]);
// let bodyj = load<Body>(buffer + j * sizeof<Body>(), 8);
let dx = ix - bodyj.x; let dx = ix - bodyj.x;
let dy = iy - bodyj.y; let dy = iy - bodyj.y;
let dz = iz - bodyj.z; let dz = iz - bodyj.z;
@ -91,9 +96,8 @@ class NBodySystem {
energy() { energy() {
var e = 0.0; var e = 0.0;
var bodies = this.bodies; var bodies = this.bodies;
var size = bodies.length; for (let i = 0, size = bodies.length; i < size; ++i) {
for (let i = 0; i < size; ++i) { let bodyi = unchecked(bodies[i]);
let bodyi = bodies[i];
let ix = bodyi.x; let ix = bodyi.x;
let iy = bodyi.y; let iy = bodyi.y;
let iz = bodyi.z; let iz = bodyi.z;

Binary file not shown.

View File

@ -706,10 +706,10 @@
(f64.const 39.47841760435743) (f64.const 39.47841760435743)
) )
) )
(if (result i32) (if
(i32.eqz
(get_local $0) (get_local $0)
(get_local $0) )
(block (result i32)
(i32.store (i32.store
(tee_local $0 (tee_local $0
(call $~lib/allocator/arena/allocate_memory (call $~lib/allocator/arena/allocate_memory
@ -718,10 +718,9 @@
) )
(get_local $1) (get_local $1)
) )
)
(get_local $0) (get_local $0)
) )
)
)
(func $assembly/index/init (; 6 ;) (type $v) (func $assembly/index/init (; 6 ;) (type $v)
(local $0 i32) (local $0 i32)
(local $1 i32) (local $1 i32)

View File

@ -25,7 +25,12 @@ var nbodyASMJS = eval("0," + src)({
// Load JS version // Load JS version
var src = fs.readFileSync(__dirname + "/../build/index.js", "utf8"); var src = fs.readFileSync(__dirname + "/../build/index.js", "utf8");
var nbodyJS = (new Function("require", "exports", src + " return exports;"))(function() {}, {}); var scopeJS = {
require: function() {},
exports: {},
unchecked: function(expr) { return expr }
};
var nbodyJS = new Function(...Object.keys(scopeJS).concat(src + "\nreturn exports"))(...Object.values(scopeJS));
function test(nbody, steps) { function test(nbody, steps) {
nbody.init(); nbody.init();

View File

@ -5,9 +5,7 @@
import { import {
Compiler, Compiler,
ConversionKind, ConversionKind
makeSmallIntegerWrap
} from "./compiler"; } from "./compiler";
import { import {
@ -383,10 +381,9 @@ export function compileCall(
case TypeKind.U8: case TypeKind.U8:
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: { case TypeKind.BOOL: {
ret = makeSmallIntegerWrap( ret = compiler.makeSmallIntegerWrap(
module.createBinary(BinaryOp.RotlI32, arg0, arg1), module.createBinary(BinaryOp.RotlI32, arg0, arg1),
compiler.currentType, compiler.currentType
module
); );
// fall-through // fall-through
} }
@ -469,10 +466,9 @@ export function compileCall(
case TypeKind.U8: case TypeKind.U8:
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: { case TypeKind.BOOL: {
ret = makeSmallIntegerWrap( ret = compiler.makeSmallIntegerWrap(
module.createBinary(BinaryOp.RotrI32, arg0, arg1), module.createBinary(BinaryOp.RotrI32, arg0, arg1),
compiler.currentType, compiler.currentType
module
); );
break; break;
} }

View File

@ -886,7 +886,7 @@ export class Compiler extends DiagnosticEmitter {
stmt = module.createBlock(null, [ stmt = module.createBlock(null, [
stmt, stmt,
module.createTeeLocal(0, module.createTeeLocal(0,
makeConditionalAllocate(this, <Class>parent, declaration.name) this.makeConditionalAllocate(<Class>parent, declaration.name)
) )
], nativeSizeType); ], nativeSizeType);
} }
@ -1421,9 +1421,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
} }
if (this.options.sourceMap) { if (this.options.sourceMap) this.addDebugLocation(expr, statement.range);
addDebugLocation(expr, statement.range, module, this.currentFunction);
}
return expr; return expr;
} }
@ -1529,10 +1527,9 @@ export class Compiler extends DiagnosticEmitter {
flow.continueLabel = previousContinueLabel; flow.continueLabel = previousContinueLabel;
var module = this.module; var module = this.module;
var condExpr = makeIsTrueish( var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE), this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType, this.currentType
module
); );
// No need to eliminate the condition in generic contexts as the statement is executed anyway. // No need to eliminate the condition in generic contexts as the statement is executed anyway.
@ -1628,10 +1625,9 @@ export class Compiler extends DiagnosticEmitter {
var ifFalse = statement.ifFalse; var ifFalse = statement.ifFalse;
// The condition doesn't initiate a branch yet // The condition doesn't initiate a branch yet
var condExpr = makeIsTrueish( var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE), this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType, this.currentType
module
); );
if ( if (
@ -1652,10 +1648,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide // Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ { } else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish( condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE), this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType, this.currentType
module
); );
} }
} }
@ -1995,10 +1990,9 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module; var module = this.module;
// The condition does not yet initialize a branch // The condition does not yet initialize a branch
var condExpr = makeIsTrueish( var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE), this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType, this.currentType
module
); );
if ( if (
@ -2015,10 +2009,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide // Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ { } else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish( condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE), this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType, this.currentType
module
); );
} }
} }
@ -2246,9 +2239,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentType = contextualType; this.currentType = contextualType;
} }
if (this.options.sourceMap) { if (this.options.sourceMap) this.addDebugLocation(expr, expression.range);
addDebugLocation(expr, expression.range, this.module, this.currentFunction);
}
return expr; return expr;
} }
@ -2359,14 +2350,14 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createUnary(UnaryOp.TruncF32ToI64, expr); expr = module.createUnary(UnaryOp.TruncF32ToI64, expr);
} else { } else {
expr = module.createUnary(UnaryOp.TruncF32ToI32, expr); expr = module.createUnary(UnaryOp.TruncF32ToI32, expr);
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module); if (toType.is(TypeFlags.SHORT)) expr = this.makeSmallIntegerWrap(expr, toType);
} }
} else { } else {
if (toType.is(TypeFlags.LONG)) { if (toType.is(TypeFlags.LONG)) {
expr = module.createUnary(UnaryOp.TruncF32ToU64, expr); expr = module.createUnary(UnaryOp.TruncF32ToU64, expr);
} else { } else {
expr = module.createUnary(UnaryOp.TruncF32ToU32, expr); expr = module.createUnary(UnaryOp.TruncF32ToU32, expr);
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module); if (toType.is(TypeFlags.SHORT)) expr = this.makeSmallIntegerWrap(expr, toType);
} }
} }
@ -2377,14 +2368,14 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createUnary(UnaryOp.TruncF64ToI64, expr); expr = module.createUnary(UnaryOp.TruncF64ToI64, expr);
} else { } else {
expr = module.createUnary(UnaryOp.TruncF64ToI32, expr); expr = module.createUnary(UnaryOp.TruncF64ToI32, expr);
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module); if (toType.is(TypeFlags.SHORT)) expr = this.makeSmallIntegerWrap(expr, toType);
} }
} else { } else {
if (toType.is(TypeFlags.LONG)) { if (toType.is(TypeFlags.LONG)) {
expr = module.createUnary(UnaryOp.TruncF64ToU64, expr); expr = module.createUnary(UnaryOp.TruncF64ToU64, expr);
} else { } else {
expr = module.createUnary(UnaryOp.TruncF64ToU32, expr); expr = module.createUnary(UnaryOp.TruncF64ToU32, expr);
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module); if (toType.is(TypeFlags.SHORT)) expr = this.makeSmallIntegerWrap(expr, toType);
} }
} }
} }
@ -2442,7 +2433,7 @@ export class Compiler extends DiagnosticEmitter {
// i64 to i32 // i64 to i32
if (!toType.is(TypeFlags.LONG)) { if (!toType.is(TypeFlags.LONG)) {
expr = module.createUnary(UnaryOp.WrapI64, expr); // discards upper bits expr = module.createUnary(UnaryOp.WrapI64, expr); // discards upper bits
if (toType.is(TypeFlags.SHORT)) expr = makeSmallIntegerWrap(expr, toType, module); if (toType.is(TypeFlags.SHORT)) expr = this.makeSmallIntegerWrap(expr, toType);
} }
// i32 to i64 // i32 to i64
@ -2460,7 +2451,7 @@ export class Compiler extends DiagnosticEmitter {
) )
) )
) { ) {
expr = makeSmallIntegerWrap(expr, toType, module); expr = this.makeSmallIntegerWrap(expr, toType);
} }
// otherwise (smaller) i32/u32 to (same size) i32/u32 // otherwise (smaller) i32/u32 to (same size) i32/u32
@ -4123,7 +4114,7 @@ export class Compiler extends DiagnosticEmitter {
} }
possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER);
condExpr = makeIsTrueish(leftExpr, this.currentType, module); condExpr = this.makeIsTrueish(leftExpr, this.currentType);
// simplify when cloning left without side effects was successful // simplify when cloning left without side effects was successful
if (expr) { if (expr) {
@ -4169,7 +4160,7 @@ export class Compiler extends DiagnosticEmitter {
} }
possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if right did possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if right did
condExpr = makeIsTrueish(leftExpr, this.currentType, module); condExpr = this.makeIsTrueish(leftExpr, this.currentType);
// simplify when cloning left without side effects was successful // simplify when cloning left without side effects was successful
if (expr) { if (expr) {
@ -4205,7 +4196,7 @@ export class Compiler extends DiagnosticEmitter {
} }
if (possiblyOverflows && wrapSmallIntegers) { if (possiblyOverflows && wrapSmallIntegers) {
assert(this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER)); // must be a small int assert(this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER)); // must be a small int
expr = makeSmallIntegerWrap(expr, this.currentType, module); expr = this.makeSmallIntegerWrap(expr, this.currentType);
} }
return compound return compound
? this.compileAssignmentWithValue(left, expr, contextualType != Type.void) ? this.compileAssignmentWithValue(left, expr, contextualType != Type.void)
@ -5425,7 +5416,7 @@ export class Compiler extends DiagnosticEmitter {
// must be conditional because `this` could have been provided by a derived class // must be conditional because `this` could have been provided by a derived class
this.currentType = thisType; this.currentType = thisType;
return module.createTeeLocal(0, return module.createTeeLocal(0,
makeConditionalAllocate(this, <Class>parent, expression) this.makeConditionalAllocate(<Class>parent, expression)
); );
} }
} }
@ -5965,7 +5956,7 @@ export class Compiler extends DiagnosticEmitter {
// otherwise simply allocate a new instance and initialize its fields // otherwise simply allocate a new instance and initialize its fields
} else { } else {
expr = makeAllocate(this, classInstance, expression); expr = this.makeAllocate(classInstance, expression);
} }
this.currentType = classInstance.type; this.currentType = classInstance.type;
@ -6092,10 +6083,9 @@ export class Compiler extends DiagnosticEmitter {
var ifElse = expression.ifElse; var ifElse = expression.ifElse;
var currentFunction = this.currentFunction; var currentFunction = this.currentFunction;
var condExpr = makeIsTrueish( var condExpr = this.makeIsTrueish(
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE), this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
this.currentType, this.currentType
this.module
); );
if ( if (
@ -6114,10 +6104,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide // Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ { } else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish( condExpr = this.makeIsTrueish(
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE), this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
this.currentType, this.currentType
this.module
); );
} }
} }
@ -6347,7 +6336,7 @@ export class Compiler extends DiagnosticEmitter {
if (possiblyOverflows) { if (possiblyOverflows) {
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER)); assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
setValue = makeSmallIntegerWrap(setValue, currentType, module); setValue = this.makeSmallIntegerWrap(setValue, currentType);
} }
setValue = this.compileAssignmentWithValue(expression.operand, setValue, false); setValue = this.compileAssignmentWithValue(expression.operand, setValue, false);
@ -6373,15 +6362,13 @@ export class Compiler extends DiagnosticEmitter {
wrapSmallIntegers: bool = true wrapSmallIntegers: bool = true
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var currentType = this.currentType;
var possiblyOverflows = false; var possiblyOverflows = false;
var compound = false; var compound = false;
var expr: ExpressionRef; var expr: ExpressionRef;
switch (expression.operator) { switch (expression.operator) {
case Token.PLUS: { case Token.PLUS: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6396,12 +6383,11 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE, ConversionKind.NONE,
false // wrapped below false // wrapped below
); );
currentType = this.currentType; possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if operand already did
possiblyOverflows = currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if operand already did
break; break;
} }
case Token.MINUS: { case Token.MINUS: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6414,11 +6400,8 @@ export class Compiler extends DiagnosticEmitter {
)) { )) {
// implicitly negate integer and float literals. also enables proper checking of literal ranges. // implicitly negate integer and float literals. also enables proper checking of literal ranges.
expr = this.compileLiteralExpression(<LiteralExpression>expression.operand, contextualType, true); expr = this.compileLiteralExpression(<LiteralExpression>expression.operand, contextualType, true);
if (this.options.sourceMap) { // compileExpression normally does this:
// compileExpression normally does this if (this.options.sourceMap) this.addDebugLocation(expr, expression.range);
addDebugLocation(expr, expression.range, module, this.currentFunction);
}
currentType = this.currentType;
} else { } else {
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
@ -6428,8 +6411,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE, ConversionKind.NONE,
false // wrapped below false // wrapped below
); );
currentType = this.currentType; switch (this.currentType.kind) {
switch (currentType.kind) {
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
case TypeKind.U8: case TypeKind.U8:
@ -6440,7 +6422,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case TypeKind.USIZE: { case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6454,7 +6436,7 @@ export class Compiler extends DiagnosticEmitter {
this.options.isWasm64 this.options.isWasm64
? BinaryOp.SubI64 ? BinaryOp.SubI64
: BinaryOp.SubI32, : BinaryOp.SubI32,
currentType.toNativeZero(module), this.currentType.toNativeZero(module),
expr expr
); );
break; break;
@ -6477,7 +6459,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case Token.PLUS_PLUS: { case Token.PLUS_PLUS: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6493,8 +6475,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE, ConversionKind.NONE,
false // wrapped below false // wrapped below
); );
currentType = this.currentType; switch (this.currentType.kind) {
switch (currentType.kind) {
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
case TypeKind.U8: case TypeKind.U8:
@ -6505,7 +6486,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case TypeKind.USIZE: { case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6520,7 +6501,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.AddI64 ? BinaryOp.AddI64
: BinaryOp.AddI32, : BinaryOp.AddI32,
expr, expr,
currentType.toNativeOne(module) this.currentType.toNativeOne(module)
); );
break; break;
} }
@ -6541,7 +6522,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case Token.MINUS_MINUS: { case Token.MINUS_MINUS: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6557,8 +6538,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE, ConversionKind.NONE,
false // wrapped below false // wrapped below
); );
currentType = this.currentType; switch (this.currentType.kind) {
switch (currentType.kind) {
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
case TypeKind.U8: case TypeKind.U8:
@ -6569,7 +6549,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case TypeKind.USIZE: { case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6584,7 +6564,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.SubI64 ? BinaryOp.SubI64
: BinaryOp.SubI32, : BinaryOp.SubI32,
expr, expr,
currentType.toNativeOne(module) this.currentType.toNativeOne(module)
); );
break; break;
} }
@ -6613,12 +6593,12 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE, ConversionKind.NONE,
true // must wrap small integers true // must wrap small integers
); );
expr = makeIsFalseish(expr, this.currentType, module); expr = this.makeIsFalseish(expr, this.currentType);
this.currentType = Type.bool; this.currentType = Type.bool;
break; break;
} }
case Token.TILDE: { case Token.TILDE: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6637,8 +6617,7 @@ export class Compiler extends DiagnosticEmitter {
: ConversionKind.IMPLICIT, : ConversionKind.IMPLICIT,
false // retains low bits of small integers false // retains low bits of small integers
); );
currentType = this.currentType; switch (this.currentType.kind) {
switch (currentType.kind) {
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
case TypeKind.U8: case TypeKind.U8:
@ -6649,7 +6628,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case TypeKind.USIZE: { case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) { if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error( this.error(
DiagnosticCode.Operation_not_supported, DiagnosticCode.Operation_not_supported,
expression.range expression.range
@ -6664,7 +6643,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.XorI64 ? BinaryOp.XorI64
: BinaryOp.XorI32, : BinaryOp.XorI32,
expr, expr,
currentType.toNativeNegOne(module) this.currentType.toNativeNegOne(module)
); );
break; break;
} }
@ -6698,13 +6677,208 @@ export class Compiler extends DiagnosticEmitter {
} }
} }
if (possiblyOverflows && wrapSmallIntegers) { if (possiblyOverflows && wrapSmallIntegers) {
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER)); assert(this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
expr = makeSmallIntegerWrap(expr, currentType, module); expr = this.makeSmallIntegerWrap(expr, this.currentType);
} }
return compound return compound
? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void) ? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void)
: expr; : expr;
} }
/** Makes sure that a 32-bit integer value is wrapped to a valid value of the specified type. */
makeSmallIntegerWrap(expr: ExpressionRef, type: Type): ExpressionRef {
var module = this.module;
switch (type.kind) {
case TypeKind.I8: { // TODO: Use 'i32.extend8_s' once sign-extension-ops lands
expr = module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
expr,
module.createI32(24)
),
module.createI32(24)
);
break;
}
case TypeKind.I16: { // TODO: Use 'i32.extend16_s' once sign-extension-ops lands
expr = module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
expr,
module.createI32(16)
),
module.createI32(16)
);
break;
}
case TypeKind.U8: {
expr = module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0xff)
);
break;
}
case TypeKind.U16: {
expr = module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0xffff)
);
break;
}
case TypeKind.BOOL: {
expr = module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0x1)
);
break;
}
}
return expr;
}
/** Creates a comparison whether an expression is 'false' in a broader sense. */
makeIsFalseish(expr: ExpressionRef, type: Type): ExpressionRef {
var module = this.module;
switch (type.kind) {
default: { // any native i32
return module.createUnary(UnaryOp.EqzI32, expr);
}
case TypeKind.I64:
case TypeKind.U64: {
return module.createUnary(UnaryOp.EqzI64, expr);
}
case TypeKind.USIZE: // TODO: strings?
case TypeKind.ISIZE: {
return module.createUnary(type.size == 64 ? UnaryOp.EqzI64 : UnaryOp.EqzI32, expr);
}
case TypeKind.F32: {
return module.createBinary(BinaryOp.EqF32, expr, module.createF32(0));
}
case TypeKind.F64: {
return module.createBinary(BinaryOp.EqF64, expr, module.createF64(0));
}
case TypeKind.VOID: {
assert(false);
return module.createI32(1);
}
}
}
/** Creates a comparison whether an expression is 'true' in a broader sense. */
makeIsTrueish(expr: ExpressionRef, type: Type): ExpressionRef {
var module = this.module;
switch (type.kind) {
default: { // any native i32
return expr;
}
case TypeKind.I64:
case TypeKind.U64: {
return module.createBinary(BinaryOp.NeI64, expr, module.createI64(0));
}
case TypeKind.USIZE: // TODO: strings?
case TypeKind.ISIZE: {
return type.size == 64
? module.createBinary(BinaryOp.NeI64, expr, module.createI64(0))
: expr;
}
case TypeKind.F32: {
return module.createBinary(BinaryOp.NeF32, expr, module.createF32(0));
}
case TypeKind.F64: {
return module.createBinary(BinaryOp.NeF64, expr, module.createF64(0));
}
case TypeKind.VOID: {
assert(false);
return module.createI32(0);
}
}
}
/** Makes an allocation expression for an instance of the specified class. */
makeAllocate(classInstance: Class, reportNode: Node): ExpressionRef {
var module = this.module;
var currentFunction = this.currentFunction;
var nativeSizeType = this.options.nativeSizeType;
// allocate the necessary memory and tee the pointer to a temp. local for reuse
var tempLocal = currentFunction.getTempLocal(classInstance.type);
var initializers = new Array<ExpressionRef>();
initializers.push(
module.createSetLocal(tempLocal.index,
compileBuiltinAllocate(this, classInstance, reportNode)
)
);
// apply field initializers
if (classInstance.members) {
for (let member of classInstance.members.values()) {
if (member.kind == ElementKind.FIELD) {
let field = <Field>member;
let fieldType = field.type;
let nativeFieldType = fieldType.toNativeType();
let fieldDeclaration = field.prototype.declaration;
assert(!field.isAny(CommonFlags.CONST));
if (fieldDeclaration.initializer) { // use initializer
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
this.compileExpression(fieldDeclaration.initializer, fieldType), // reports
nativeFieldType,
field.memoryOffset
));
} else { // initialize with zero
// TODO: might be unnecessary if the ctor initializes the field
let parameterIndex = (<FieldDeclaration>field.prototype.declaration).parameterIndex;
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
parameterIndex >= 0 // initialized via parameter
? module.createGetLocal(1 + parameterIndex, nativeFieldType)
: fieldType.toNativeZero(module),
nativeFieldType,
field.memoryOffset
));
}
}
}
}
// return `this`
initializers.push(
module.createGetLocal(tempLocal.index, nativeSizeType)
);
currentFunction.freeTempLocal(tempLocal);
this.currentType = classInstance.type;
return module.createBlock(null, initializers, nativeSizeType);
}
/** Makes a conditional allocation expression inside of the constructor of the specified class. */
makeConditionalAllocate(classInstance: Class, reportNode: Node): ExpressionRef {
// requires that `this` is the first local
var module = this.module;
var nativeSizeType = this.options.nativeSizeType;
this.currentType = classInstance.type;
return module.createIf(
nativeSizeType == NativeType.I64
? module.createBinary(
BinaryOp.NeI64,
module.createGetLocal(0, NativeType.I64),
module.createI64(0)
)
: module.createGetLocal(0, NativeType.I32),
module.createGetLocal(0, nativeSizeType),
module.createTeeLocal(0,
this.makeAllocate(classInstance, reportNode)
)
);
}
/** Adds the debug location of the specified expression at the specified range to the source map. */
addDebugLocation(expr: ExpressionRef, range: Range): void {
var currentFunction = this.currentFunction;
var source = range.source;
if (source.debugInfoIndex < 0) source.debugInfoIndex = this.module.addDebugInfoFile(source.normalizedPath);
range.debugInfoRef = expr;
if (!currentFunction.debugLocations) currentFunction.debugLocations = [];
currentFunction.debugLocations.push(range);
}
} }
// helpers // helpers
@ -6752,200 +6926,3 @@ function mangleExportName(element: Element, explicitSimpleName: string | null =
} }
} }
} }
/** Adds the debug location of the specified expression at the specified range to the source map. */
function addDebugLocation(expr: ExpressionRef, range: Range, module: Module, currentFunction: Function): void {
var source = range.source;
if (source.debugInfoIndex < 0) {
source.debugInfoIndex = module.addDebugInfoFile(source.normalizedPath);
}
range.debugInfoRef = expr;
if (!currentFunction.debugLocations) currentFunction.debugLocations = [];
currentFunction.debugLocations.push(range);
}
/** Wraps a 32-bit integer expression so it evaluates to a valid value of the specified type. */
export function makeSmallIntegerWrap(expr: ExpressionRef, type: Type, module: Module): ExpressionRef {
switch (type.kind) {
case TypeKind.I8: {
return module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
expr,
module.createI32(24)
),
module.createI32(24)
);
}
case TypeKind.I16: {
return module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
expr,
module.createI32(16)
),
module.createI32(16)
);
}
case TypeKind.U8: {
return module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0xff)
);
}
case TypeKind.U16: {
return module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0xffff)
);
}
case TypeKind.BOOL: {
return module.createBinary(BinaryOp.AndI32,
expr,
module.createI32(0x1)
);
}
default: {
assert(false);
return expr;
}
}
}
/** Creates a comparison whether an expression is not 'true' in a broader sense. */
export function makeIsFalseish(expr: ExpressionRef, type: Type, module: Module): ExpressionRef {
switch (type.kind) {
default: { // any native i32
return module.createUnary(UnaryOp.EqzI32, expr);
}
case TypeKind.I64:
case TypeKind.U64: {
return module.createUnary(UnaryOp.EqzI64, expr);
}
case TypeKind.USIZE: // TODO: strings?
case TypeKind.ISIZE: {
return module.createUnary(type.size == 64 ? UnaryOp.EqzI64 : UnaryOp.EqzI32, expr);
}
case TypeKind.F32: {
return module.createBinary(BinaryOp.EqF32, expr, module.createF32(0));
}
case TypeKind.F64: {
return module.createBinary(BinaryOp.EqF64, expr, module.createF64(0));
}
case TypeKind.VOID: {
assert(false);
return module.createI32(1);
}
}
}
/** Creates a comparison whether an expression is 'true' in a broader sense. */
export function makeIsTrueish(expr: ExpressionRef, type: Type, module: Module): ExpressionRef {
switch (type.kind) {
default: { // any native i32
return expr;
}
case TypeKind.I64:
case TypeKind.U64: {
return module.createBinary(BinaryOp.NeI64, expr, module.createI64(0));
}
case TypeKind.USIZE: // TODO: strings?
case TypeKind.ISIZE: {
return type.size == 64
? module.createBinary(BinaryOp.NeI64, expr, module.createI64(0))
: expr;
}
case TypeKind.F32: {
return module.createBinary(BinaryOp.NeF32, expr, module.createF32(0));
}
case TypeKind.F64: {
return module.createBinary(BinaryOp.NeF64, expr, module.createF64(0));
}
case TypeKind.VOID: {
assert(false);
return module.createI32(0);
}
}
}
/** Makes an allocation expression for an instance of the specified class. */
export function makeAllocate(compiler: Compiler, classInstance: Class, reportNode: Node): ExpressionRef {
var module = compiler.module;
var currentFunction = compiler.currentFunction;
var nativeSizeType = compiler.options.nativeSizeType;
var tempLocal = currentFunction.getTempLocal(classInstance.type);
// allocate the necessary memory
var initializers = new Array<ExpressionRef>();
initializers.push(
module.createSetLocal(tempLocal.index,
compileBuiltinAllocate(compiler, classInstance, reportNode)
)
);
// apply field initializers
if (classInstance.members) {
for (let member of classInstance.members.values()) {
if (member.kind == ElementKind.FIELD) {
let field = <Field>member;
let fieldType = field.type;
let nativeFieldType = fieldType.toNativeType();
let fieldDeclaration = field.prototype.declaration;
assert(!field.isAny(CommonFlags.CONST));
if (fieldDeclaration.initializer) { // use initializer
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
compiler.compileExpression(fieldDeclaration.initializer, fieldType), // reports
nativeFieldType,
field.memoryOffset
));
} else { // initialize with zero
// TODO: might be unnecessary if the ctor initializes the field
let parameterIndex = (<FieldDeclaration>field.prototype.declaration).parameterIndex;
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
parameterIndex >= 0 // initialized via parameter
? module.createGetLocal(1 + parameterIndex, nativeFieldType)
: fieldType.toNativeZero(module),
nativeFieldType,
field.memoryOffset
));
}
}
}
}
// return `this`
initializers.push(
module.createGetLocal(tempLocal.index, nativeSizeType)
);
currentFunction.freeTempLocal(tempLocal);
compiler.currentType = classInstance.type;
return module.createBlock(null, initializers, nativeSizeType);
}
/** Makes a conditional allocation expression inside of the constructor of the specified class. */
export function makeConditionalAllocate(compiler: Compiler, classInstance: Class, reportNode: Node): ExpressionRef {
// requires that `this` is the first local
var module = compiler.module;
var nativeSizeType = compiler.options.nativeSizeType;
compiler.currentType = classInstance.type;
return module.createIf(
nativeSizeType == NativeType.I64
? module.createBinary(
BinaryOp.NeI64,
module.createGetLocal(0, NativeType.I64),
module.createI64(0)
)
: module.createGetLocal(0, NativeType.I32),
module.createGetLocal(0, nativeSizeType),
module.createTeeLocal(0,
makeAllocate(compiler, classInstance, reportNode)
)
);
}
export function isI32Const(expr: ExpressionRef): bool {
return _BinaryenExpressionGetId(expr) == ExpressionId.Const
&& _BinaryenExpressionGetType(expr) == NativeType.I32;
}

2
std/portable.d.ts vendored
View File

@ -183,6 +183,8 @@ declare function bswap16<T = i16 | u16 | i32 | u32>(value: T): T;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/ /** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T; declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */ /** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool; declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */ /** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */

View File

@ -202,6 +202,10 @@ globalScope["isString"] = function isString(arg) {
globalScope["isArray"] = Array.isArray; globalScope["isArray"] = Array.isArray;
globalScope["unchecked"] = function(expr) {
return expr;
};
globalScope["fmod"] = function fmod(x, y) { globalScope["fmod"] = function fmod(x, y) {
return x % y; return x % y;
}; };