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

View File

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

Binary file not shown.

View File

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

View File

@ -25,7 +25,12 @@ var nbodyASMJS = eval("0," + src)({
// Load JS version
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) {
nbody.init();

View File

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

View File

@ -886,7 +886,7 @@ export class Compiler extends DiagnosticEmitter {
stmt = module.createBlock(null, [
stmt,
module.createTeeLocal(0,
makeConditionalAllocate(this, <Class>parent, declaration.name)
this.makeConditionalAllocate(<Class>parent, declaration.name)
)
], nativeSizeType);
}
@ -1421,9 +1421,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
}
if (this.options.sourceMap) {
addDebugLocation(expr, statement.range, module, this.currentFunction);
}
if (this.options.sourceMap) this.addDebugLocation(expr, statement.range);
return expr;
}
@ -1529,10 +1527,9 @@ export class Compiler extends DiagnosticEmitter {
flow.continueLabel = previousContinueLabel;
var module = this.module;
var condExpr = makeIsTrueish(
var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType,
module
this.currentType
);
// 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;
// The condition doesn't initiate a branch yet
var condExpr = makeIsTrueish(
var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType,
module
this.currentType
);
if (
@ -1652,10 +1648,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish(
condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType,
module
this.currentType
);
}
}
@ -1995,10 +1990,9 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module;
// The condition does not yet initialize a branch
var condExpr = makeIsTrueish(
var condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType,
module
this.currentType
);
if (
@ -2015,10 +2009,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish(
condExpr = this.makeIsTrueish(
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
this.currentType,
module
this.currentType
);
}
}
@ -2246,9 +2239,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentType = contextualType;
}
if (this.options.sourceMap) {
addDebugLocation(expr, expression.range, this.module, this.currentFunction);
}
if (this.options.sourceMap) this.addDebugLocation(expr, expression.range);
return expr;
}
@ -2359,14 +2350,14 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createUnary(UnaryOp.TruncF32ToI64, expr);
} else {
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 {
if (toType.is(TypeFlags.LONG)) {
expr = module.createUnary(UnaryOp.TruncF32ToU64, expr);
} else {
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);
} else {
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 {
if (toType.is(TypeFlags.LONG)) {
expr = module.createUnary(UnaryOp.TruncF64ToU64, expr);
} else {
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
if (!toType.is(TypeFlags.LONG)) {
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
@ -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
@ -4123,7 +4114,7 @@ export class Compiler extends DiagnosticEmitter {
}
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
if (expr) {
@ -4169,7 +4160,7 @@ export class Compiler extends DiagnosticEmitter {
}
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
if (expr) {
@ -4205,7 +4196,7 @@ export class Compiler extends DiagnosticEmitter {
}
if (possiblyOverflows && wrapSmallIntegers) {
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
? 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
this.currentType = thisType;
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
} else {
expr = makeAllocate(this, classInstance, expression);
expr = this.makeAllocate(classInstance, expression);
}
this.currentType = classInstance.type;
@ -6092,10 +6083,9 @@ export class Compiler extends DiagnosticEmitter {
var ifElse = expression.ifElse;
var currentFunction = this.currentFunction;
var condExpr = makeIsTrueish(
var condExpr = this.makeIsTrueish(
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
this.currentType,
this.module
this.currentType
);
if (
@ -6114,10 +6104,9 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise recompile to the original and let the optimizer decide
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {
condExpr = makeIsTrueish(
condExpr = this.makeIsTrueish(
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
this.currentType,
this.module
this.currentType
);
}
}
@ -6347,7 +6336,7 @@ export class Compiler extends DiagnosticEmitter {
if (possiblyOverflows) {
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
setValue = makeSmallIntegerWrap(setValue, currentType, module);
setValue = this.makeSmallIntegerWrap(setValue, currentType);
}
setValue = this.compileAssignmentWithValue(expression.operand, setValue, false);
@ -6373,15 +6362,13 @@ export class Compiler extends DiagnosticEmitter {
wrapSmallIntegers: bool = true
): ExpressionRef {
var module = this.module;
var currentType = this.currentType;
var possiblyOverflows = false;
var compound = false;
var expr: ExpressionRef;
switch (expression.operator) {
case Token.PLUS: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6396,12 +6383,11 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE,
false // wrapped below
);
currentType = this.currentType;
possiblyOverflows = currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if operand already did
possiblyOverflows = this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER); // if operand already did
break;
}
case Token.MINUS: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6414,11 +6400,8 @@ export class Compiler extends DiagnosticEmitter {
)) {
// implicitly negate integer and float literals. also enables proper checking of literal ranges.
expr = this.compileLiteralExpression(<LiteralExpression>expression.operand, contextualType, true);
if (this.options.sourceMap) {
// compileExpression normally does this
addDebugLocation(expr, expression.range, module, this.currentFunction);
}
currentType = this.currentType;
// compileExpression normally does this:
if (this.options.sourceMap) this.addDebugLocation(expr, expression.range);
} else {
expr = this.compileExpression(
expression.operand,
@ -6428,8 +6411,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE,
false // wrapped below
);
currentType = this.currentType;
switch (currentType.kind) {
switch (this.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
@ -6440,7 +6422,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6454,7 +6436,7 @@ export class Compiler extends DiagnosticEmitter {
this.options.isWasm64
? BinaryOp.SubI64
: BinaryOp.SubI32,
currentType.toNativeZero(module),
this.currentType.toNativeZero(module),
expr
);
break;
@ -6477,7 +6459,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case Token.PLUS_PLUS: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6493,8 +6475,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE,
false // wrapped below
);
currentType = this.currentType;
switch (currentType.kind) {
switch (this.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
@ -6505,7 +6486,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6520,7 +6501,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.AddI64
: BinaryOp.AddI32,
expr,
currentType.toNativeOne(module)
this.currentType.toNativeOne(module)
);
break;
}
@ -6541,7 +6522,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case Token.MINUS_MINUS: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6557,8 +6538,7 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE,
false // wrapped below
);
currentType = this.currentType;
switch (currentType.kind) {
switch (this.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
@ -6569,7 +6549,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6584,7 +6564,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.SubI64
: BinaryOp.SubI32,
expr,
currentType.toNativeOne(module)
this.currentType.toNativeOne(module)
);
break;
}
@ -6613,12 +6593,12 @@ export class Compiler extends DiagnosticEmitter {
ConversionKind.NONE,
true // must wrap small integers
);
expr = makeIsFalseish(expr, this.currentType, module);
expr = this.makeIsFalseish(expr, this.currentType);
this.currentType = Type.bool;
break;
}
case Token.TILDE: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6637,8 +6617,7 @@ export class Compiler extends DiagnosticEmitter {
: ConversionKind.IMPLICIT,
false // retains low bits of small integers
);
currentType = this.currentType;
switch (currentType.kind) {
switch (this.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
@ -6649,7 +6628,7 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.USIZE: {
if (currentType.is(TypeFlags.REFERENCE)) {
if (this.currentType.is(TypeFlags.REFERENCE)) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
@ -6664,7 +6643,7 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.XorI64
: BinaryOp.XorI32,
expr,
currentType.toNativeNegOne(module)
this.currentType.toNativeNegOne(module)
);
break;
}
@ -6698,13 +6677,208 @@ export class Compiler extends DiagnosticEmitter {
}
}
if (possiblyOverflows && wrapSmallIntegers) {
assert(currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
expr = makeSmallIntegerWrap(expr, currentType, module);
assert(this.currentType.is(TypeFlags.SHORT | TypeFlags.INTEGER));
expr = this.makeSmallIntegerWrap(expr, this.currentType);
}
return compound
? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void)
: 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
@ -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.*/
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`. */
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`. */

View File

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