Transition to TypeFlags for specific type checks; Optimize logical ops a bit

This commit is contained in:
dcodeIO 2018-01-10 23:19:14 +01:00
parent fc777b3a89
commit d89703cdad
18 changed files with 750 additions and 720 deletions

View File

@ -1,7 +1,9 @@
import { import {
Compiler, Compiler,
Target, Target,
ConversionKind ConversionKind,
makeSmallIntegerWrap
} from "./compiler"; } from "./compiler";
import { import {
@ -15,7 +17,9 @@ import {
} from "./ast"; } from "./ast";
import { import {
Type, TypeKind Type,
TypeKind,
TypeFlags
} from "./types"; } from "./types";
import { import {
@ -511,23 +515,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
ret = module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
module.createBinary(BinaryOp.RotlI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerShift)
),
module.createI32(compiler.currentType.smallIntegerShift)
);
break;
case TypeKind.U8: case TypeKind.U8:
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: case TypeKind.BOOL:
ret = module.createBinary(BinaryOp.AndI32, ret = makeSmallIntegerWrap(module.createBinary(BinaryOp.RotlI32, arg0, arg1), compiler.currentType, module);
module.createBinary(BinaryOp.RotlI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerMask)
);
break;
case TypeKind.I32: case TypeKind.I32:
case TypeKind.U32: case TypeKind.U32:
@ -584,22 +575,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
ret = module.createBinary(BinaryOp.ShrI32,
module.createBinary(BinaryOp.ShlI32,
module.createBinary(BinaryOp.RotrI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerShift)
),
module.createI32(compiler.currentType.smallIntegerShift)
);
break;
case TypeKind.U8: case TypeKind.U8:
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: case TypeKind.BOOL:
ret = module.createBinary(BinaryOp.AndI32, ret = makeSmallIntegerWrap(module.createBinary(BinaryOp.RotrI32, arg0, arg1), compiler.currentType, module);
module.createBinary(BinaryOp.RotrI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerMask)
);
break; break;
case TypeKind.I32: case TypeKind.I32:
@ -1249,7 +1228,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.F32: case TypeKind.F32:
if (typeArguments) { if (typeArguments) {
if (!(typeArguments[1].isAnyInteger && typeArguments[1].size == 32)) { if (!(typeArguments[1].is(TypeFlags.INTEGER) && typeArguments[1].size == 32)) {
compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString());
return module.createUnreachable(); return module.createUnreachable();
} }
@ -1261,7 +1240,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.F64: case TypeKind.F64:
if (typeArguments) { if (typeArguments) {
if (!(typeArguments[1].isLongInteger && !typeArguments[1].isReference)) { if (!(typeArguments[1].is(TypeFlags.LONG | TypeFlags.INTEGER) && !typeArguments[1].isReference)) {
compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString());
return module.createUnreachable(); return module.createUnreachable();
} }
@ -1390,7 +1369,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
} }
arg0 = compiler.compileExpression(operands[0], usizeType); arg0 = compiler.compileExpression(operands[0], usizeType);
compiler.currentType = typeArguments[0]; compiler.currentType = typeArguments[0];
return module.createLoad(typeArguments[0].size >>> 3, typeArguments[0].isAnySignedInteger, arg0, typeArguments[0].toNativeType()); return module.createLoad(typeArguments[0].size >>> 3, typeArguments[0].is(TypeFlags.SIGNED | TypeFlags.INTEGER), arg0, typeArguments[0].toNativeType());
case "store": // store<T?>(offset: usize, value: T) -> void case "store": // store<T?>(offset: usize, value: T) -> void
compiler.currentType = Type.void; compiler.currentType = Type.void;

View File

@ -110,6 +110,7 @@ import {
import { import {
Type, Type,
TypeKind, TypeKind,
TypeFlags,
typesToNativeTypes typesToNativeTypes
} from "./types"; } from "./types";
@ -1196,147 +1197,125 @@ export class Compiler extends DiagnosticEmitter {
if (toType.kind == TypeKind.VOID) if (toType.kind == TypeKind.VOID)
return this.module.createDrop(expr); return this.module.createDrop(expr);
var fromFloat = fromType.isAnyFloat;
var toFloat = toType.isAnyFloat;
var mod = this.module; var mod = this.module;
var losesInformation = false; var losesInformation = false;
if (fromFloat) { if (fromType.is(TypeFlags.FLOAT)) {
// float to float // float to float
if (toFloat) { if (toType.is(TypeFlags.FLOAT)) {
if (fromType.kind == TypeKind.F32) { if (fromType.kind == TypeKind.F32) {
// f32 to f64 // f32 to f64
if (toType.kind == TypeKind.F64) if (toType.kind == TypeKind.F64)
expr = mod.createUnary(UnaryOp.PromoteF32, expr); expr = mod.createUnary(UnaryOp.PromoteF32, expr);
// otherwise f32 to f32
// f64 to f32 // f64 to f32
} else if (toType.kind == TypeKind.F32) { } else if (toType.kind == TypeKind.F32) {
losesInformation = true; losesInformation = true;
expr = mod.createUnary(UnaryOp.DemoteF64, expr); expr = mod.createUnary(UnaryOp.DemoteF64, expr);
} }
// otherwise f64 to f64
// float to int // float to int
} else { } else if (toType.is(TypeFlags.INTEGER)) {
losesInformation = true; losesInformation = true;
// f32 to int // f32 to int
if (fromType.kind == TypeKind.F32) { if (fromType.kind == TypeKind.F32) {
if (toType.isAnySignedInteger) { if (toType.is(TypeFlags.SIGNED)) {
if (toType.isLongInteger) if (toType.is(TypeFlags.LONG))
expr = mod.createUnary(UnaryOp.TruncF32ToI64, expr); expr = mod.createUnary(UnaryOp.TruncF32ToI64, expr);
else { else {
expr = mod.createUnary(UnaryOp.TruncF32ToI32, expr); expr = mod.createUnary(UnaryOp.TruncF32ToI32, expr);
if (toType.isSmallInteger) { if (toType.is(TypeFlags.SMALL))
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); expr = makeSmallIntegerWrap(expr, toType, this.module);
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
}
} }
} else { } else {
if (toType.isLongInteger) if (toType.is(TypeFlags.LONG))
expr = mod.createUnary(UnaryOp.TruncF32ToU64, expr); expr = mod.createUnary(UnaryOp.TruncF32ToU64, expr);
else { else {
expr = mod.createUnary(UnaryOp.TruncF32ToU32, expr); expr = mod.createUnary(UnaryOp.TruncF32ToU32, expr);
if (toType.isSmallInteger) if (toType.is(TypeFlags.SMALL))
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask)); expr = makeSmallIntegerWrap(expr, toType, this.module);
} }
} }
// f64 to int // f64 to int
} else { } else {
if (toType.isAnySignedInteger) { if (toType.is(TypeFlags.SIGNED)) {
if (toType.isLongInteger) if (toType.is(TypeFlags.LONG))
expr = mod.createUnary(UnaryOp.TruncF64ToI64, expr); expr = mod.createUnary(UnaryOp.TruncF64ToI64, expr);
else { else {
expr = mod.createUnary(UnaryOp.TruncF64ToI32, expr); expr = mod.createUnary(UnaryOp.TruncF64ToI32, expr);
if (toType.isSmallInteger) { if (toType.is(TypeFlags.SMALL))
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); expr = makeSmallIntegerWrap(expr, toType, this.module);
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
}
} }
} else { } else {
if (toType.isLongInteger) if (toType.is(TypeFlags.LONG))
expr = mod.createUnary(UnaryOp.TruncF64ToU64, expr); expr = mod.createUnary(UnaryOp.TruncF64ToU64, expr);
else { else {
expr = mod.createUnary(UnaryOp.TruncF64ToU32, expr); expr = mod.createUnary(UnaryOp.TruncF64ToU32, expr);
if (toType.isSmallInteger) if (toType.is(TypeFlags.SMALL))
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask)); expr = makeSmallIntegerWrap(expr, toType, this.module);
} }
} }
} }
// float to void
} else {
assert(toType.flags == TypeFlags.NONE);
expr = this.module.createDrop(expr);
} }
// int to float // int to float
} else if (toFloat) { } else if (fromType.is(TypeFlags.INTEGER) && toType.is(TypeFlags.FLOAT)) {
// int to f32 // int to f32
if (toType.kind == TypeKind.F32) { if (toType.kind == TypeKind.F32) {
if (fromType.isLongInteger) { if (fromType.is(TypeFlags.LONG)) {
losesInformation = true; losesInformation = true;
if (fromType.isAnySignedInteger) expr = mod.createUnary(select(UnaryOp.ConvertI64ToF32, UnaryOp.ConvertU64ToF32, fromType.is(TypeFlags.SIGNED)), expr);
expr = mod.createUnary(UnaryOp.ConvertI64ToF32, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU64ToF32, expr);
} else { } else {
if (!fromType.isSmallInteger) losesInformation = !fromType.is(TypeFlags.SMALL);
losesInformation = true; expr = mod.createUnary(select(UnaryOp.ConvertI32ToF32, UnaryOp.ConvertU32ToF32, fromType.is(TypeFlags.SIGNED)), expr);
if (fromType.isAnySignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI32ToF32, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU32ToF32, expr);
} }
// int to f64 // int to f64
} else { } else {
if (fromType.isLongInteger) { if (fromType.is(TypeFlags.LONG)) {
losesInformation = true; losesInformation = true;
if (fromType.isAnySignedInteger) expr = mod.createUnary(select(UnaryOp.ConvertI64ToF64, UnaryOp.ConvertU64ToF64, fromType.is(TypeFlags.SIGNED)), expr);
expr = mod.createUnary(UnaryOp.ConvertI64ToF64, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU64ToF64, expr);
} else } else
if (fromType.isAnySignedInteger) expr = mod.createUnary(select(UnaryOp.ConvertI32ToF64, UnaryOp.ConvertU32ToF64, fromType.is(TypeFlags.SIGNED)), expr);
expr = mod.createUnary(UnaryOp.ConvertI32ToF64, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU32ToF64, expr);
} }
// int to int // int to int
} else { } else {
if (fromType.isLongInteger) { if (fromType.is(TypeFlags.LONG)) {
// i64 to i32 // i64 to i32
if (!toType.isLongInteger) { if (!toType.is(TypeFlags.LONG)) {
losesInformation = true; losesInformation = true;
expr = mod.createUnary(UnaryOp.WrapI64, expr); // discards upper bits expr = mod.createUnary(UnaryOp.WrapI64, expr); // discards upper bits
if (toType.isSmallInteger) { if (toType.is(TypeFlags.SMALL))
if (toType.isAnySignedInteger) { expr = makeSmallIntegerWrap(expr, toType, this.module);
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
} else
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
}
} }
// i32 to i64 // i32 to i64
} else if (toType.isLongInteger) { } else if (toType.is(TypeFlags.LONG)) {
if (toType.isAnySignedInteger) expr = mod.createUnary(select(UnaryOp.ExtendI32, UnaryOp.ExtendU32, toType.is(TypeFlags.SIGNED)), expr);
expr = mod.createUnary(UnaryOp.ExtendI32, expr);
else
expr = mod.createUnary(UnaryOp.ExtendU32, expr);
// i32 or smaller to even smaller int // i32 or smaller to even smaller or same size int with change of sign
} else if (toType.isSmallInteger && fromType.size > toType.size) { } else if (toType.is(TypeFlags.SMALL) && (fromType.size > toType.size || (fromType.size == toType.size && fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED)))) {
losesInformation = true; losesInformation = true;
if (toType.isAnySignedInteger) { expr = makeSmallIntegerWrap(expr, toType, this.module);
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
} else
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
} }
// otherwise (smaller) i32/u32 to (same size) i32/u32
} }
if (losesInformation && conversionKind == ConversionKind.IMPLICIT) if (losesInformation && conversionKind == ConversionKind.IMPLICIT)
@ -1360,8 +1339,7 @@ export class Compiler extends DiagnosticEmitter {
var compound = false; var compound = false;
var possiblyOverflows = false; var possiblyOverflows = false;
var tempLocal: Local | null = null
var tempLocal: Local;
switch (expression.operator) { switch (expression.operator) {
@ -1905,7 +1883,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.LESSTHAN_LESSTHAN_EQUALS: case Token.LESSTHAN_LESSTHAN_EQUALS:
compound = true; compound = true;
case Token.LESSTHAN_LESSTHAN: // retains low bits of small integers case Token.LESSTHAN_LESSTHAN: // retains low bits of small integers
left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE, false); left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE, false);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -1940,7 +1918,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.GREATERTHAN_GREATERTHAN_EQUALS: case Token.GREATERTHAN_GREATERTHAN_EQUALS:
compound = true; compound = true;
case Token.GREATERTHAN_GREATERTHAN: // must wrap small integers case Token.GREATERTHAN_GREATERTHAN: // must wrap small integers
left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE); left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -1984,7 +1962,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
compound = true; compound = true;
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: // modifies low bits of small integers if unsigned case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: // modifies low bits of small integers if unsigned
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); left = this.compileExpression(expression.left, select(Type.u64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -2018,7 +1996,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.AMPERSAND_EQUALS: case Token.AMPERSAND_EQUALS:
compound = true; compound = true;
case Token.AMPERSAND: // retains low bits of small integers case Token.AMPERSAND: // retains low bits of small integers
left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE, false); left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE, false);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -2053,7 +2031,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.BAR_EQUALS: case Token.BAR_EQUALS:
compound = true; compound = true;
case Token.BAR: // retains low bits of small integers case Token.BAR: // retains low bits of small integers
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE, false);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -2088,7 +2066,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.CARET_EQUALS: case Token.CARET_EQUALS:
compound = true; compound = true;
case Token.CARET: // retains low bits of small integers case Token.CARET: // retains low bits of small integers
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), ConversionKind.NONE, false);
right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -2124,74 +2102,98 @@ export class Compiler extends DiagnosticEmitter {
case Token.AMPERSAND_AMPERSAND: // left && right case Token.AMPERSAND_AMPERSAND: // left && right
left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
// simplify if left is free of side effects while tolerating one level of nesting, e.g., i32.load(i32.const) // clone left if free of side effects while tolerating one level of nesting
if (condition = this.module.cloneExpression(left, true, 1)) { expr = this.module.cloneExpression(left, true, 1);
expr = this.module.createIf(
this.currentType.isLongInteger // if not possible, tee left to a temp. local
? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) if (!expr) {
: this.currentType == Type.f64 tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType);
? this.module.createBinary(BinaryOp.NeF64, condition, this.module.createF64(0)) left = this.module.createTeeLocal(tempLocal.index, left);
: this.currentType == Type.f32
? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0))
: condition, // usual case: saves one EQZ when not using EQZ above
right,
left
);
break;
} }
// otherwise use a temporary local for the intermediate value // make a condition that checks !left in case an optimizer can take advantage of guaranteed 0 or 1
tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType); // binaryen just switches the arms here, see: https://github.com/WebAssembly/binaryen/issues/1355
condition = this.module.createTeeLocal(tempLocal.index, left); possiblyOverflows = this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER);
expr = this.module.createIf( condition = makeEqualsZero(left, this.currentType, this.module);
this.currentType.isLongInteger
? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) // simplify when cloning left without side effects was successful
: this.currentType == Type.f64 if (expr) {
? this.module.createBinary(BinaryOp.NeF64, condition, this.module.createF64(0))
: this.currentType == Type.f32 // if right is also free of side effects, do a select
? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0)) if (left = this.module.cloneExpression(right, true, 1)) {
: this.module.createTeeLocal(tempLocal.index, left), expr = this.module.createSelect(
right, expr, // then cloned left
this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()) left, // else cloned right, actually
condition // if !left
); );
// otherwise make it an if
} else
expr = this.module.createIf(
condition, // if !left
expr, // then cloned left
right // else right
);
// otherwise make use of the temp. local
} else {
assert(tempLocal);
expr = this.module.createIf(
condition, // if !left
this.module.createGetLocal((<Local>tempLocal).index, this.currentType.toNativeType()),
right
);
}
break; break;
case Token.BAR_BAR: // left || right case Token.BAR_BAR: // left || right
left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType); right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false);
// simplify if left is free of side effects while tolerating one level of nesting // clone left if free of side effects while tolerating one level of nesting
if (condition = this.module.cloneExpression(left, true, 1)) { expr = this.module.cloneExpression(left, true, 1);
expr = this.module.createIf(
this.currentType.isLongInteger // if not possible, tee left to a temp. local
? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) if (!expr) {
: this.currentType == Type.f64 tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType);
? this.module.createBinary(BinaryOp.NeF64, condition, this.module.createF64(0)) left = this.module.createTeeLocal(tempLocal.index, left);
: this.currentType == Type.f32
? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0))
: condition, // usual case: saves one EQZ when not using EQZ above
left,
right
);
break;
} }
// otherwise use a temporary local for the intermediate value // make a condition that checks !left in case an optimizer can take advantage of guaranteed 0 or 1
tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType); // binaryen just switches the arms here, see: https://github.com/WebAssembly/binaryen/issues/1355
condition = this.module.createTeeLocal(tempLocal.index, left); possiblyOverflows = this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER); // if right already did
expr = this.module.createIf( condition = makeEqualsZero(left, this.currentType, this.module);
this.currentType.isLongInteger
? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) // simplify when cloning left without side effects was successful
: this.currentType == Type.f64 if (expr) {
? this.module.createBinary(BinaryOp.NeF64, condition, this.module.createF64(0))
: this.currentType == Type.f32 // if right is also free of side effects, do a select
? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0)) if (left = this.module.cloneExpression(right, true, 1)) {
: this.module.createTeeLocal(tempLocal.index, left), expr = this.module.createSelect(
this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()), left, // else cloned right, actually
right expr, // then cloned left
condition // if !left
); );
// otherwise make it an if
} else
expr = this.module.createIf(
condition, // if !left
right, // then right
expr // else cloned left
);
// otherwise make use of the temp. local
} else {
assert(tempLocal);
expr = this.module.createIf(
condition, // if !left
right,
this.module.createGetLocal((<Local>tempLocal).index, this.currentType.toNativeType())
);
}
break; break;
default: default:
@ -2199,8 +2201,8 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("not implemented"); throw new Error("not implemented");
} }
if (possiblyOverflows && wrapSmallIntegers) { if (possiblyOverflows && wrapSmallIntegers) {
assert(this.currentType.isSmallInteger); assert(this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER));
expr = wrapSmallInteger(expr, this.currentType, this.module); expr = makeSmallIntegerWrap(expr, this.currentType, this.module);
} }
return compound return compound
? this.compileAssignmentWithValue(expression.left, expr, contextualType != Type.void) ? this.compileAssignmentWithValue(expression.left, expr, contextualType != Type.void)
@ -2577,33 +2579,35 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createF64(floatValue); return this.module.createF64(floatValue);
} }
case LiteralKind.INTEGER: { case LiteralKind.INTEGER:
var intValue = (<IntegerLiteralExpression>expression).value; var intValue = (<IntegerLiteralExpression>expression).value;
if (contextualType == Type.bool && (intValue.isZero || intValue.isOne)) if (contextualType == Type.bool && (intValue.isZero || intValue.isOne))
return this.module.createI32(intValue.isZero ? 0 : 1); return this.module.createI32(select(0, 1, intValue.isZero));
if (contextualType == Type.f64) if (contextualType == Type.f64)
return this.module.createF64(intValue.toF64()); return this.module.createF64(intValue.toF64());
if (contextualType == Type.f32) if (contextualType == Type.f32)
return this.module.createF32(<f32>intValue.toF64()); return this.module.createF32(<f32>intValue.toF64());
if (contextualType.isLongInteger) if (contextualType.is(TypeFlags.LONG | TypeFlags.INTEGER))
return this.module.createI64(intValue.lo, intValue.hi); return this.module.createI64(intValue.lo, intValue.hi);
if (!intValue.fitsInI32) { if (!intValue.fitsInI32) {
this.currentType = select(Type.i64, Type.u64, contextualType.isAnySignedInteger); this.currentType = select(Type.i64, Type.u64, contextualType.is(TypeFlags.SIGNED));
return this.module.createI64(intValue.lo, intValue.hi); return this.module.createI64(intValue.lo, intValue.hi);
} }
if (contextualType.isSmallInteger) { if (contextualType.is(TypeFlags.SMALL | TypeFlags.INTEGER)) {
var smallIntValue: i32 = contextualType.isAnySignedInteger var shift = contextualType.computeSmallIntegerShift(Type.i32);
? intValue.lo << contextualType.smallIntegerShift >> contextualType.smallIntegerShift var mask = contextualType.computeSmallIntegerMask(Type.i32);
: intValue.lo & contextualType.smallIntegerMask; return this.module.createI32(select(
return this.module.createI32(smallIntValue); intValue.lo << shift >> shift,
intValue.lo & mask,
contextualType.is(TypeFlags.SIGNED)
));
} }
if (contextualType == Type.void && !intValue.fitsInI32) { if (contextualType == Type.void && !intValue.fitsInI32) {
this.currentType = Type.i64; this.currentType = Type.i64;
return this.module.createI64(intValue.lo, intValue.hi); return this.module.createI64(intValue.lo, intValue.hi);
} }
this.currentType = contextualType.isAnySignedInteger ? Type.i32 : Type.u32; this.currentType = select(Type.i32, Type.u32, contextualType.is(TypeFlags.SIGNED));
return this.module.createI32(intValue.toI32()); return this.module.createI32(intValue.toI32());
}
// case LiteralKind.OBJECT: // case LiteralKind.OBJECT:
// case LiteralKind.REGEXP: // case LiteralKind.REGEXP:
@ -2655,7 +2659,7 @@ export class Compiler extends DiagnosticEmitter {
assert((<Field>element).memoryOffset >= 0); assert((<Field>element).memoryOffset >= 0);
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64)); targetExpr = this.compileExpression(<Expression>resolved.targetExpression, select<Type>(Type.usize64, Type.usize32, this.options.target == Target.WASM64));
this.currentType = (<Field>element).type; this.currentType = (<Field>element).type;
return this.module.createLoad((<Field>element).type.byteSize, (<Field>element).type.isAnySignedInteger, return this.module.createLoad((<Field>element).type.byteSize, (<Field>element).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
targetExpr, targetExpr,
(<Field>element).type.toNativeType(), (<Field>element).type.toNativeType(),
(<Field>element).memoryOffset (<Field>element).memoryOffset
@ -2816,8 +2820,8 @@ export class Compiler extends DiagnosticEmitter {
nativeOne nativeOne
); );
if (possiblyOverflows) { if (possiblyOverflows) {
assert(this.currentType.isSmallInteger); assert(this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER));
setValue = wrapSmallInteger(setValue, this.currentType, this.module); setValue = makeSmallIntegerWrap(setValue, this.currentType, this.module);
} }
setValue = this.compileAssignmentWithValue(expression.operand, setValue, false); // sets currentType = void setValue = this.compileAssignmentWithValue(expression.operand, setValue, false); // sets currentType = void
this.currentType = tempLocal.type; this.currentType = tempLocal.type;
@ -2842,7 +2846,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false);
possiblyOverflows = this.currentType.isSmallInteger; // if operand already did possiblyOverflows = this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER); // if operand already did
break; break;
case Token.MINUS: case Token.MINUS:
@ -2856,7 +2860,6 @@ export class Compiler extends DiagnosticEmitter {
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: case TypeKind.BOOL:
possiblyOverflows = true; // or if operand already did possiblyOverflows = true; // or if operand already did
// fall-through
default: default:
expr = this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), expr); expr = this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), expr);
break; break;
@ -2866,7 +2869,6 @@ export class Compiler extends DiagnosticEmitter {
this.error(DiagnosticCode.Operation_not_supported, expression.range); this.error(DiagnosticCode.Operation_not_supported, expression.range);
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
// fall-through
case TypeKind.ISIZE: case TypeKind.ISIZE:
expr = this.module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64), this.currentType.toNativeZero(this.module), expr); expr = this.module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64), this.currentType.toNativeZero(this.module), expr);
break; break;
@ -2971,36 +2973,12 @@ export class Compiler extends DiagnosticEmitter {
case Token.EXCLAMATION: // must wrap small integers case Token.EXCLAMATION: // must wrap small integers
expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE);
expr = makeEqualsZero(expr, this.currentType, this.module);
switch (this.currentType.kind) {
default:
expr = this.module.createUnary(UnaryOp.EqzI32, expr);
break;
case TypeKind.ISIZE:
case TypeKind.USIZE:
expr = this.module.createUnary(select<UnaryOp>(UnaryOp.EqzI64, UnaryOp.EqzI32, this.options.target == Target.WASM64), expr);
break;
case TypeKind.I64:
case TypeKind.U64:
expr = this.module.createUnary(UnaryOp.EqzI64, expr);
break;
case TypeKind.F32:
expr = this.module.createBinary(BinaryOp.EqF32, expr, this.module.createF32(0));
break;
case TypeKind.F64:
expr = this.module.createBinary(BinaryOp.EqF64, expr, this.module.createF64(0));
break;
}
this.currentType = Type.bool; this.currentType = Type.bool;
break; break;
case Token.TILDE: // retains low bits of small integers case Token.TILDE: // retains low bits of small integers
expr = this.compileExpression(expression.operand, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), select(ConversionKind.NONE, ConversionKind.IMPLICIT, contextualType == Type.void), false); expr = this.compileExpression(expression.operand, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.is(TypeFlags.FLOAT)), select(ConversionKind.NONE, ConversionKind.IMPLICIT, contextualType == Type.void), false);
switch (this.currentType.kind) { switch (this.currentType.kind) {
@ -3036,8 +3014,8 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("unary operator expected"); throw new Error("unary operator expected");
} }
if (possiblyOverflows && wrapSmallIntegers) { if (possiblyOverflows && wrapSmallIntegers) {
assert(this.currentType.isSmallInteger); assert(this.currentType.is(TypeFlags.SMALL | TypeFlags.INTEGER));
expr = wrapSmallInteger(expr, this.currentType, this.module); expr = makeSmallIntegerWrap(expr, this.currentType, this.module);
} }
return compound return compound
? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void) ? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void)
@ -3075,13 +3053,14 @@ function makeInlineConstant(element: VariableLikeElement, module: Module): Expre
case TypeKind.I8: case TypeKind.I8:
case TypeKind.I16: case TypeKind.I16:
var shift = (<Type>element.type).smallIntegerShift; var shift = element.type.computeSmallIntegerShift(Type.i32);
return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0); return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0);
case TypeKind.U8: case TypeKind.U8:
case TypeKind.U16: case TypeKind.U16:
case TypeKind.BOOL: case TypeKind.BOOL:
return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & (<Type>element.type).smallIntegerMask: 0); var mask = element.type.computeSmallIntegerMask(Type.i32);
return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & mask : 0);
case TypeKind.I32: case TypeKind.I32:
case TypeKind.U32: case TypeKind.U32:
@ -3109,7 +3088,7 @@ function makeInlineConstant(element: VariableLikeElement, module: Module): Expre
} }
/** Wraps a 32-bit integer expression so it evaluates to a valid value in the range of the specified small integer type. */ /** Wraps a 32-bit integer expression so it evaluates to a valid value in the range of the specified small integer type. */
export function wrapSmallInteger(expr: ExpressionRef, type: Type, module: Module) { export function makeSmallIntegerWrap(expr: ExpressionRef, type: Type, module: Module) {
switch (type.kind) { switch (type.kind) {
case TypeKind.I8: case TypeKind.I8:
@ -3152,6 +3131,40 @@ export function wrapSmallInteger(expr: ExpressionRef, type: Type, module: Module
module.createI32(0x1) module.createI32(0x1)
); );
break; break;
case TypeKind.VOID:
throw new Error("concrete type expected");
}
return expr;
}
export function makeEqualsZero(expr: ExpressionRef, type: Type, module: Module): ExpressionRef {
switch (type.kind) {
default: // any integer up to 32 bits
expr = module.createUnary(UnaryOp.EqzI32, expr);
break;
case TypeKind.I64:
case TypeKind.U64:
expr = module.createUnary(UnaryOp.EqzI64, expr);
break;
case TypeKind.ISIZE:
case TypeKind.USIZE:
expr = module.createUnary(select(UnaryOp.EqzI64, UnaryOp.EqzI32, type.size == 64), expr);
break;
case TypeKind.F32:
expr = module.createBinary(BinaryOp.EqF32, expr, module.createF32(0));
break;
case TypeKind.F64:
expr = module.createBinary(BinaryOp.EqF64, expr, module.createF64(0));
break;
case TypeKind.VOID:
throw new Error("concrete type expected");
} }
return expr; return expr;
} }

View File

@ -43,11 +43,38 @@ export const enum TypeKind {
VOID VOID
} }
/** Indicates capabilities of a type. */
export const enum TypeFlags {
NONE = 0,
/** Is a signed type that can represent negative values. */
SIGNED = 1 << 0,
/** Is an unsigned type that cannot represent negative values. */
UNSIGNED = 1 << 1,
/** Is an integer type. */
INTEGER = 1 << 2,
/** Is a floating point type. */
FLOAT = 1 << 3,
/** Is a sized integer type with a target specific bit size. */
SIZE = 1 << 4,
/** Is a small type that is emulated in a larger type. */
SMALL = 1 << 5,
/** Is a long type larger than 32-bits. */
LONG = 1 << 6,
/** Is a value type. */
VALUE = 1 << 7,
/** Is a reference type. */
REFERENCE = 1 << 8,
/** Is a nullable type. */
NULLABLE = 1 << 9
}
/** Represents a resolved type. */ /** Represents a resolved type. */
export class Type { export class Type {
/** Type kind. */ /** Type kind. */
kind: TypeKind; kind: TypeKind;
/** Type flags. */
flags: TypeFlags;
/** Size in bits. */ /** Size in bits. */
size: i32; size: i32;
/** Size in bytes. */ /** Size in bytes. */
@ -56,171 +83,34 @@ export class Type {
classType: Class | null; classType: Class | null;
/** Underlying function type, if a function type. */ /** Underlying function type, if a function type. */
functionType: Function | null; functionType: Function | null;
/** Whether nullable or not. */
isNullable: bool = false;
/** Respective nullable type, if non-nullable. */ /** Respective nullable type, if non-nullable. */
nullableType: Type | null = null; nullableType: Type | null = null;
/** Respective non-nullable type, if nullable. */ /** Respective non-nullable type, if nullable. */
nonNullableType: Type; nonNullableType: Type;
/** Constructs a new resolved type. */ /** Constructs a new resolved type. */
constructor(kind: TypeKind, size: i32) { constructor(kind: TypeKind, flags: TypeFlags, size: i32) {
this.kind = kind; this.kind = kind;
this.flags = flags;
this.size = size; this.size = size;
this.byteSize = <i32>ceil<f64>(<f64>size / 8); this.byteSize = <i32>ceil<f64>(<f64>size / 8);
this.classType = null; this.classType = null;
this.nonNullableType = this; this.nonNullableType = this;
} }
/** Sign-extending 32-bit shift, if a small signed integer. */ /** Computes the sign-extending shift in the target type. */
get smallIntegerShift(): i32 { return 32 - this.size; } computeSmallIntegerShift(targetType: Type) {
/** Truncating 32-bit mask, if a small unsigned integer. */ return targetType.size - this.size;
get smallIntegerMask(): i32 { return -1 >>> (32 - this.size); }
/** Tests if this type is of any integer kind. */
get isAnyInteger(): bool {
switch (this.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32:
case TypeKind.I64:
case TypeKind.ISIZE:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.U64:
case TypeKind.USIZE:
case TypeKind.BOOL:
return true;
default:
return false;
}
} }
/** Tests if this type is of any unsigned integer kind. */ /** Computes the truncating mask in the target type. */
get isAnyUnsignedInteger(): bool { computeSmallIntegerMask(targetType: Type) {
switch (this.kind) { return -1 >>> (targetType.size - this.size);
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.U64:
case TypeKind.USIZE:
case TypeKind.BOOL:
return true;
default:
return false;
}
} }
/** Tests if this type is of any signed integer kind. */ /** Tests if this type has the specified capabilities. */
get isAnySignedInteger(): bool { is(flags: TypeFlags): bool {
switch (this.kind) { return (this.flags & flags) == flags;
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32:
case TypeKind.I64:
case TypeKind.ISIZE:
return true;
default:
return false;
}
}
/** Tests if this type is of any small integer kind. */
get isSmallInteger(): bool {
switch (this.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL:
return true;
default:
return false;
}
}
/** Tests if this type is of any small signed integer kind. */
get isSmallSignedInteger(): bool {
switch (this.kind) {
case TypeKind.I8:
case TypeKind.I16:
return true;
default:
return false;
}
}
/** Tests if this type is of any small unsigned integer kind. */
get isSmallUnsignedInteger(): bool {
switch (this.kind) {
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL:
return true;
default:
return false;
}
}
/** Tests if this type is of any long integer kind. */
get isLongInteger(): bool {
switch (this.kind) {
case TypeKind.I64:
case TypeKind.U64:
return true;
case TypeKind.ISIZE:
case TypeKind.USIZE:
return this.size == 64;
default:
return false;
}
}
/** Tests if this type is of any long signed integer kind. */
get isLongSignedInteger(): bool {
switch (this.kind) {
case TypeKind.I64:
return true;
case TypeKind.ISIZE:
return this.size == 64;
default:
return false;
}
}
/** Tests if this type is of any long unsigned integer kind. */
get isLongUnsignedInteger(): bool {
switch (this.kind) {
case TypeKind.U64:
return true;
case TypeKind.USIZE:
return this.size == 64;
default:
return false;
}
}
/** Tests if this type is of any size kind, that is `isize` or `usize`. */
get isAnySize(): bool {
switch (this.kind) {
case TypeKind.ISIZE:
case TypeKind.USIZE:
return true;
default:
return false;
}
}
/** Tests if this type is of any float kind, i.e., `f32` or `f64`. */
get isAnyFloat(): bool {
switch (this.kind) {
case TypeKind.F32:
case TypeKind.F64:
return true;
default:
return false;
}
} }
/** Tests if this type is a class type. */ /** Tests if this type is a class type. */
@ -233,7 +123,7 @@ export class Type {
/** Composes a class type from this type and a class. */ /** Composes a class type from this type and a class. */
asClass(classType: Class): Type { asClass(classType: Class): Type {
assert(this.kind == TypeKind.USIZE); assert(this.kind == TypeKind.USIZE);
var ret = new Type(this.kind, this.size); var ret = new Type(this.kind, this.flags & ~TypeFlags.VALUE | TypeFlags.REFERENCE, this.size);
ret.classType = classType; ret.classType = classType;
return ret; return ret;
} }
@ -241,16 +131,17 @@ export class Type {
/** Composes a function type from this type and a function. */ /** Composes a function type from this type and a function. */
asFunction(functionType: Function): Type { asFunction(functionType: Function): Type {
assert(this.kind == TypeKind.USIZE && !this.isReference); assert(this.kind == TypeKind.USIZE && !this.isReference);
var ret = new Type(this.kind, this.size); var ret = new Type(this.kind, this.flags & ~TypeFlags.VALUE | TypeFlags.REFERENCE, this.size);
ret.functionType = functionType; ret.functionType = functionType;
return ret; return ret;
} }
/** Composes the respective nullable type of this type. */ /** Composes the respective nullable type of this type. */
asNullable(): Type | null { asNullable(): Type | null {
assert(this.kind == TypeKind.USIZE && !this.isReference); assert(this.kind == TypeKind.USIZE);
if (this.isNullable && !this.nullableType) { if (!this.nullableType) {
(this.nullableType = new Type(this.kind, this.size)).isNullable = true; assert(!this.is(TypeFlags.NULLABLE) && this.isReference);
this.nullableType = new Type(this.kind, this.flags | TypeFlags.NULLABLE, this.size);
this.nullableType.classType = this.classType; this.nullableType.classType = this.classType;
this.nullableType.functionType = this.functionType; this.nullableType.functionType = this.functionType;
} }
@ -423,37 +314,37 @@ export class Type {
// Types // Types
/** An 8-bit signed integer. */ /** An 8-bit signed integer. */
static readonly i8: Type = new Type(TypeKind.I8, 8); static readonly i8: Type = new Type(TypeKind.I8, TypeFlags.SIGNED | TypeFlags.SMALL | TypeFlags.INTEGER | TypeFlags.VALUE, 8);
/** A 16-bit signed integer. */ /** A 16-bit signed integer. */
static readonly i16: Type = new Type(TypeKind.I16, 16); static readonly i16: Type = new Type(TypeKind.I16, TypeFlags.SIGNED | TypeFlags.SMALL | TypeFlags.INTEGER | TypeFlags.VALUE, 16);
/** A 32-bit signed integer. */ /** A 32-bit signed integer. */
static readonly i32: Type = new Type(TypeKind.I32, 32); static readonly i32: Type = new Type(TypeKind.I32, TypeFlags.SIGNED | TypeFlags.INTEGER | TypeFlags.VALUE, 32);
/** A 64-bit signed integer. */ /** A 64-bit signed integer. */
static readonly i64: Type = new Type(TypeKind.I64, 64); static readonly i64: Type = new Type(TypeKind.I64, TypeFlags.SIGNED | TypeFlags.LONG | TypeFlags.INTEGER | TypeFlags.VALUE, 64);
/** A 32-bit signed size. WASM32 only. */ /** A 32-bit signed size. WASM32 only. */
static readonly isize32: Type = new Type(TypeKind.ISIZE, 32); static readonly isize32: Type = new Type(TypeKind.ISIZE, TypeFlags.SIGNED | TypeFlags.SIZE | TypeFlags.INTEGER | TypeFlags.VALUE, 32);
/** A 64-bit signed size. WASM64 only. */ /** A 64-bit signed size. WASM64 only. */
static readonly isize64: Type = new Type(TypeKind.ISIZE, 64); static readonly isize64: Type = new Type(TypeKind.ISIZE, TypeFlags.SIGNED | TypeFlags.LONG | TypeFlags.SIZE | TypeFlags.INTEGER | TypeFlags.VALUE, 64);
/** An 8-bit unsigned integer. */ /** An 8-bit unsigned integer. */
static readonly u8: Type = new Type(TypeKind.U8, 8); static readonly u8: Type = new Type(TypeKind.U8, TypeFlags.UNSIGNED | TypeFlags.SMALL | TypeFlags.INTEGER | TypeFlags.VALUE, 8);
/** A 16-bit unsigned integer. */ /** A 16-bit unsigned integer. */
static readonly u16: Type = new Type(TypeKind.U16, 16); static readonly u16: Type = new Type(TypeKind.U16, TypeFlags.UNSIGNED | TypeFlags.SMALL | TypeFlags.INTEGER | TypeFlags.VALUE, 16);
/** A 32-bit unsigned integer. */ /** A 32-bit unsigned integer. */
static readonly u32: Type = new Type(TypeKind.U32, 32); static readonly u32: Type = new Type(TypeKind.U32, TypeFlags.UNSIGNED | TypeFlags.INTEGER | TypeFlags.VALUE, 32);
/** A 64-bit unsigned integer. */ /** A 64-bit unsigned integer. */
static readonly u64: Type = new Type(TypeKind.U64, 64); static readonly u64: Type = new Type(TypeKind.U64, TypeFlags.UNSIGNED | TypeFlags.LONG | TypeFlags.INTEGER | TypeFlags.VALUE, 64);
/** A 32-bit unsigned size. WASM32 only. */ /** A 32-bit unsigned size. WASM32 only. */
static readonly usize32: Type = new Type(TypeKind.USIZE, 32); static readonly usize32: Type = new Type(TypeKind.USIZE, TypeFlags.UNSIGNED | TypeFlags.SIZE | TypeFlags.INTEGER | TypeFlags.VALUE, 32);
/** A 64-bit unsigned size. WASM64 only. */ /** A 64-bit unsigned size. WASM64 only. */
static readonly usize64: Type = new Type(TypeKind.USIZE, 64); static readonly usize64: Type = new Type(TypeKind.USIZE, TypeFlags.UNSIGNED | TypeFlags.LONG | TypeFlags.SIZE | TypeFlags.INTEGER | TypeFlags.VALUE, 64);
/** A 1-bit unsigned integer. */ /** A 1-bit unsigned integer. */
static readonly bool: Type = new Type(TypeKind.BOOL, 1); static readonly bool: Type = new Type(TypeKind.BOOL, TypeFlags.UNSIGNED | TypeFlags.SMALL | TypeFlags.INTEGER | TypeFlags.VALUE, 1);
/** A 32-bit float. */ /** A 32-bit float. */
static readonly f32: Type = new Type(TypeKind.F32, 32); static readonly f32: Type = new Type(TypeKind.F32, TypeFlags.SIGNED | TypeFlags.FLOAT | TypeFlags.VALUE, 32);
/** A 64-bit float. */ /** A 64-bit float. */
static readonly f64: Type = new Type(TypeKind.F64, 64); static readonly f64: Type = new Type(TypeKind.F64, TypeFlags.SIGNED | TypeFlags.LONG | TypeFlags.FLOAT | TypeFlags.VALUE, 64);
/** No return type. */ /** No return type. */
static readonly void: Type = new Type(TypeKind.VOID, 0); static readonly void: Type = new Type(TypeKind.VOID, TypeFlags.NONE, 0);
} }
/** Converts an array of types to an array of native types. */ /** Converts an array of types to an array of native types. */

View File

@ -0,0 +1,19 @@
var binaryen = require("binaryen");
var mod = new binaryen.Module();
var funcType = mod.addFunctionType("i", binaryen.i32, [ binaryen.i32 ]);
var func = mod.addFunction("test", funcType, [],
mod.if(
mod.i32.eqz(mod.getLocal(0, binaryen.i32)),
mod.i32.const(0),
mod.getLocal(0, binaryen.i32)
)
);
mod.addExport("test", "test");
console.log(mod.emitText());
if (!mod.validate())
console.log("-> does not validate");
mod.optimize();
console.log(mod.emitText());

View File

@ -0,0 +1,14 @@
(module
(type $i (func (param i32) (result i32)))
(memory $0 0)
(export "test" (func $test))
(func $test (; 0 ;) (type $i) (param $0 i32) (result i32)
(if (result i32)
(i32.eqz
(get_local $0)
)
(i32.const 0)
(get_local $0)
)
)
)

View File

@ -55,7 +55,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $7
(i32.and
(if (result i32) (if (result i32)
(tee_local $7 (tee_local $7
(i64.eq (i64.eq
@ -67,7 +70,6 @@
) )
) )
(get_local $7) (get_local $7)
(tee_local $7
(f64.ne (f64.ne
(tee_local $9 (tee_local $9
(get_local $1) (get_local $1)
@ -75,6 +77,8 @@
(get_local $9) (get_local $9)
) )
) )
(i32.const 1)
)
) )
(get_local $7) (get_local $7)
(i32.eq (i32.eq
@ -82,6 +86,8 @@
(i32.const 2047) (i32.const 2047)
) )
) )
(i32.const 1)
)
(return (return
(f64.div (f64.div
(f64.mul (f64.mul
@ -434,7 +440,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $3
(i32.and
(if (result i32) (if (result i32)
(tee_local $3 (tee_local $3
(i32.eqz (i32.eqz
@ -445,7 +454,6 @@
) )
) )
(get_local $3) (get_local $3)
(tee_local $3
(f32.ne (f32.ne
(tee_local $8 (tee_local $8
(get_local $1) (get_local $1)
@ -453,6 +461,8 @@
(get_local $8) (get_local $8)
) )
) )
(i32.const 1)
)
) )
(get_local $3) (get_local $3)
(i32.eq (i32.eq
@ -460,6 +470,8 @@
(i32.const 255) (i32.const 255)
) )
) )
(i32.const 1)
)
(return (return
(f32.div (f32.div
(f32.mul (f32.mul

View File

@ -70,9 +70,13 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i64.eq (i64.eq
(i64.shl (i64.shl
@ -82,20 +86,26 @@
(i64.const 0) (i64.const 0)
) )
) )
(get_local $8) )
(f64.ne (f64.ne
(tee_local $7 (tee_local $7
(get_local $1) (get_local $1)
) )
(get_local $7) (get_local $7)
) )
)
)
(get_local $8) (get_local $8)
)
(i32.const 1)
)
)
)
(i32.eq (i32.eq
(get_local $4) (get_local $4)
(i32.const 2047) (i32.const 2047)
) )
(get_local $8)
)
(i32.const 1)
) )
(return (return
(f64.div (f64.div
@ -530,9 +540,13 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.eq (i32.eq
(i32.shl (i32.shl
@ -542,20 +556,26 @@
(i32.const 0) (i32.const 0)
) )
) )
(get_local $8) )
(f32.ne (f32.ne
(tee_local $7 (tee_local $7
(get_local $1) (get_local $1)
) )
(get_local $7) (get_local $7)
) )
)
)
(get_local $8) (get_local $8)
)
(i32.const 1)
)
)
)
(i32.eq (i32.eq
(get_local $4) (get_local $4)
(i32.const 255) (i32.const 255)
) )
(get_local $8)
)
(i32.const 1)
) )
(return (return
(f32.div (f32.div

View File

@ -209,19 +209,22 @@
) )
) )
(if (if
(if (result i32) (i32.and
(i32.lt_u (select
(get_local $2)
(i32.const 2)
)
(i32.lt_u
(get_local $2)
(i32.const 2)
)
(i32.gt_u (i32.gt_u
(get_local $2) (get_local $2)
(i32.const 3) (i32.const 3)
) )
(i32.lt_u
(get_local $2)
(i32.const 2)
)
(i32.ge_u
(get_local $2)
(i32.const 2)
)
)
(i32.const 1)
) )
(i32.store8 (i32.store8
(i32.add (i32.add

View File

@ -235,19 +235,24 @@
) )
) )
(if (if
(if (result i32) (i32.and
(i32.lt_u (select
(get_local $8)
(i32.const 2)
)
(i32.lt_u
(get_local $8)
(i32.const 2)
)
(i32.gt_u (i32.gt_u
(get_local $8) (get_local $8)
(i32.const 3) (i32.const 3)
) )
(i32.lt_u
(get_local $8)
(i32.const 2)
)
(i32.eqz
(i32.lt_u
(get_local $8)
(i32.const 2)
)
)
)
(i32.const 1)
) )
(i32.store8 (i32.store8
(i32.add (i32.add

View File

@ -13,77 +13,87 @@
(local $1 f64) (local $1 f64)
(drop (drop
(if (result i32) (if (result i32)
(i32.const 0) (i32.eqz
(unreachable)
(i32.const 0) (i32.const 0)
) )
) (i32.const 0)
(drop
(if (result f64)
(f64.ne
(f64.const 0)
(f64.const 0)
)
(unreachable)
(f64.const 0)
)
)
(drop
(if (result i32)
(i32.const 1)
(i32.const 1)
(unreachable) (unreachable)
) )
) )
(drop (drop
(if (result f64) (if (result f64)
(f64.ne (f64.eq
(f64.const 1) (f64.const 0)
(f64.const 0) (f64.const 0)
) )
(f64.const 1) (f64.const 0)
(unreachable) (unreachable)
) )
) )
(drop (drop
(if (result i32) (if (result i32)
(i32.eqz
(i32.const 1)
)
(unreachable)
(i32.const 1)
)
)
(drop
(if (result f64)
(f64.eq
(f64.const 1)
(f64.const 0)
)
(unreachable)
(f64.const 1)
)
)
(drop
(if (result i32)
(i32.eqz
(tee_local $0 (tee_local $0
(if (result i32) (select
(i32.const 1) (i32.const 1)
(i32.const 2) (i32.const 2)
(i32.eqz
(i32.const 1) (i32.const 1)
) )
) )
(get_local $0) )
)
(unreachable) (unreachable)
(get_local $0)
) )
) )
(drop (drop
(if (result f64) (if (result f64)
(f64.ne (f64.eq
(tee_local $1 (tee_local $1
(if (result f64) (select
(f64.ne
(f64.const 1) (f64.const 1)
(f64.const 0)
)
(f64.const 2) (f64.const 2)
(f64.eq
(f64.const 1) (f64.const 1)
(f64.const 0)
)
) )
) )
(f64.const 0) (f64.const 0)
) )
(get_local $1)
(unreachable) (unreachable)
(get_local $1)
) )
) )
(set_global $logical/i (set_global $logical/i
(if (result i32) (select
(i32.const 1) (i32.const 1)
(i32.const 2) (i32.const 2)
(i32.eqz
(i32.const 1) (i32.const 1)
) )
) )
)
(if (if
(i32.eqz (i32.eqz
(i32.eq (i32.eq
@ -94,10 +104,12 @@
(unreachable) (unreachable)
) )
(set_global $logical/i (set_global $logical/i
(if (result i32) (select
(i32.const 0)
(i32.const 0)
(i32.const 1) (i32.const 1)
(i32.const 0)
(i32.eqz
(i32.const 0)
)
) )
) )
(if (if
@ -110,15 +122,14 @@
(unreachable) (unreachable)
) )
(set_global $logical/I (set_global $logical/I
(if (result i64) (select
(i64.ne
(i64.const 1) (i64.const 1)
(i64.const 0)
)
(i64.const 2) (i64.const 2)
(i64.eqz
(i64.const 1) (i64.const 1)
) )
) )
)
(if (if
(i32.eqz (i32.eqz
(i64.eq (i64.eq
@ -129,13 +140,12 @@
(unreachable) (unreachable)
) )
(set_global $logical/I (set_global $logical/I
(if (result i64) (select
(i64.ne (i64.const 1)
(i64.const 0) (i64.const 0)
(i64.eqz
(i64.const 0) (i64.const 0)
) )
(i64.const 0)
(i64.const 1)
) )
) )
(if (if
@ -148,13 +158,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/f (set_global $logical/f
(if (result f32) (select
(f32.ne (f32.const 1)
(f32.const 2)
(f32.eq
(f32.const 1) (f32.const 1)
(f32.const 0) (f32.const 0)
) )
(f32.const 2)
(f32.const 1)
) )
) )
(if (if
@ -167,13 +177,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/f (set_global $logical/f
(if (result f32) (select
(f32.ne (f32.const 1)
(f32.const 0)
(f32.eq
(f32.const 0) (f32.const 0)
(f32.const 0) (f32.const 0)
) )
(f32.const 0)
(f32.const 1)
) )
) )
(if (if
@ -186,13 +196,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/F (set_global $logical/F
(if (result f64) (select
(f64.ne (f64.const 1)
(f64.const 2)
(f64.eq
(f64.const 1) (f64.const 1)
(f64.const 0) (f64.const 0)
) )
(f64.const 2)
(f64.const 1)
) )
) )
(if (if
@ -205,13 +215,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/F (set_global $logical/F
(if (result f64) (select
(f64.ne (f64.const 1)
(f64.const 0)
(f64.eq
(f64.const 0) (f64.const 0)
(f64.const 0) (f64.const 0)
) )
(f64.const 0)
(f64.const 1)
) )
) )
(if (if

View File

@ -18,13 +18,13 @@
) )
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2)
(i32.rem_u (i32.rem_u
(get_local $3) (get_local $3)
(i32.const 4) (i32.const 4)
) )
(get_local $2) (get_local $2)
(get_local $2)
) )
(block (block
(set_local $4 (set_local $4

View File

@ -26,14 +26,16 @@
(block $break|0 (block $break|0
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2) (get_local $2)
(i32.rem_u (i32.rem_u
(get_local $4) (get_local $4)
(i32.const 4) (i32.const 4)
) )
(i32.eqz
(get_local $2) (get_local $2)
) )
)
(block (block
(block (block
(i32.store8 (i32.store8

View File

@ -65,13 +65,13 @@
) )
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2)
(i32.rem_u (i32.rem_u
(get_local $3) (get_local $3)
(i32.const 4) (i32.const 4)
) )
(get_local $2) (get_local $2)
(get_local $2)
) )
(block (block
(set_local $4 (set_local $4
@ -1687,7 +1687,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $7
(i32.and
(if (result i32) (if (result i32)
(tee_local $7 (tee_local $7
(i64.eq (i64.eq
@ -1699,7 +1702,6 @@
) )
) )
(get_local $7) (get_local $7)
(tee_local $7
(f64.ne (f64.ne
(tee_local $9 (tee_local $9
(get_local $1) (get_local $1)
@ -1707,6 +1709,8 @@
(get_local $9) (get_local $9)
) )
) )
(i32.const 1)
)
) )
(get_local $7) (get_local $7)
(i32.eq (i32.eq
@ -1714,6 +1718,8 @@
(i32.const 2047) (i32.const 2047)
) )
) )
(i32.const 1)
)
(return (return
(f64.div (f64.div
(f64.mul (f64.mul
@ -2066,7 +2072,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $3
(i32.and
(if (result i32) (if (result i32)
(tee_local $3 (tee_local $3
(i32.eqz (i32.eqz
@ -2077,7 +2086,6 @@
) )
) )
(get_local $3) (get_local $3)
(tee_local $3
(f32.ne (f32.ne
(tee_local $8 (tee_local $8
(get_local $1) (get_local $1)
@ -2085,6 +2093,8 @@
(get_local $8) (get_local $8)
) )
) )
(i32.const 1)
)
) )
(get_local $3) (get_local $3)
(i32.eq (i32.eq
@ -2092,6 +2102,8 @@
(i32.const 255) (i32.const 255)
) )
) )
(i32.const 1)
)
(return (return
(f32.div (f32.div
(f32.mul (f32.mul

View File

@ -86,13 +86,13 @@
) )
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2)
(i32.rem_u (i32.rem_u
(get_local $3) (get_local $3)
(i32.const 4) (i32.const 4)
) )
(get_local $2) (get_local $2)
(get_local $2)
) )
(block (block
(set_local $4 (set_local $4
@ -1708,7 +1708,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $7
(i32.and
(if (result i32) (if (result i32)
(tee_local $7 (tee_local $7
(i64.eq (i64.eq
@ -1720,7 +1723,6 @@
) )
) )
(get_local $7) (get_local $7)
(tee_local $7
(f64.ne (f64.ne
(tee_local $9 (tee_local $9
(get_local $1) (get_local $1)
@ -1728,6 +1730,8 @@
(get_local $9) (get_local $9)
) )
) )
(i32.const 1)
)
) )
(get_local $7) (get_local $7)
(i32.eq (i32.eq
@ -1735,6 +1739,8 @@
(i32.const 2047) (i32.const 2047)
) )
) )
(i32.const 1)
)
(return (return
(f64.div (f64.div
(f64.mul (f64.mul
@ -2087,7 +2093,10 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(tee_local $3
(i32.and
(if (result i32) (if (result i32)
(tee_local $3 (tee_local $3
(i32.eqz (i32.eqz
@ -2098,7 +2107,6 @@
) )
) )
(get_local $3) (get_local $3)
(tee_local $3
(f32.ne (f32.ne
(tee_local $8 (tee_local $8
(get_local $1) (get_local $1)
@ -2106,6 +2114,8 @@
(get_local $8) (get_local $8)
) )
) )
(i32.const 1)
)
) )
(get_local $3) (get_local $3)
(i32.eq (i32.eq
@ -2113,6 +2123,8 @@
(i32.const 255) (i32.const 255)
) )
) )
(i32.const 1)
)
(return (return
(f32.div (f32.div
(f32.mul (f32.mul

View File

@ -134,14 +134,16 @@
(block $break|0 (block $break|0
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2) (get_local $2)
(i32.rem_u (i32.rem_u
(get_local $4) (get_local $4)
(i32.const 4) (i32.const 4)
) )
(i32.eqz
(get_local $2) (get_local $2)
) )
)
(block (block
(block (block
(i32.store8 (i32.store8
@ -1988,9 +1990,13 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i64.eq (i64.eq
(i64.shl (i64.shl
@ -2000,20 +2006,26 @@
(i64.const 0) (i64.const 0)
) )
) )
(get_local $8) )
(f64.ne (f64.ne
(tee_local $7 (tee_local $7
(get_local $1) (get_local $1)
) )
(get_local $7) (get_local $7)
) )
)
)
(get_local $8) (get_local $8)
)
(i32.const 1)
)
)
)
(i32.eq (i32.eq
(get_local $4) (get_local $4)
(i32.const 2047) (i32.const 2047)
) )
(get_local $8)
)
(i32.const 1)
) )
(return (return
(f64.div (f64.div
@ -2448,9 +2460,13 @@
) )
) )
(if (if
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.and
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $8 (tee_local $8
(i32.eq (i32.eq
(i32.shl (i32.shl
@ -2460,20 +2476,26 @@
(i32.const 0) (i32.const 0)
) )
) )
(get_local $8) )
(f32.ne (f32.ne
(tee_local $7 (tee_local $7
(get_local $1) (get_local $1)
) )
(get_local $7) (get_local $7)
) )
)
)
(get_local $8) (get_local $8)
)
(i32.const 1)
)
)
)
(i32.eq (i32.eq
(get_local $4) (get_local $4)
(i32.const 255) (i32.const 255)
) )
(get_local $8)
)
(i32.const 1)
) )
(return (return
(f32.div (f32.div
@ -4209,77 +4231,87 @@
) )
(drop (drop
(if (result i32) (if (result i32)
(i32.const 0) (i32.eqz
(unreachable)
(i32.const 0) (i32.const 0)
) )
) (i32.const 0)
(drop
(if (result f64)
(f64.ne
(f64.const 0)
(f64.const 0)
)
(unreachable)
(f64.const 0)
)
)
(drop
(if (result i32)
(i32.const 1)
(i32.const 1)
(unreachable) (unreachable)
) )
) )
(drop (drop
(if (result f64) (if (result f64)
(f64.ne (f64.eq
(f64.const 1) (f64.const 0)
(f64.const 0) (f64.const 0)
) )
(f64.const 1) (f64.const 0)
(unreachable) (unreachable)
) )
) )
(drop (drop
(if (result i32) (if (result i32)
(i32.eqz
(i32.const 1)
)
(unreachable)
(i32.const 1)
)
)
(drop
(if (result f64)
(f64.eq
(f64.const 1)
(f64.const 0)
)
(unreachable)
(f64.const 1)
)
)
(drop
(if (result i32)
(i32.eqz
(tee_local $0 (tee_local $0
(if (result i32) (select
(i32.const 1) (i32.const 1)
(i32.const 2) (i32.const 2)
(i32.eqz
(i32.const 1) (i32.const 1)
) )
) )
(get_local $0) )
)
(unreachable) (unreachable)
(get_local $0)
) )
) )
(drop (drop
(if (result f64) (if (result f64)
(f64.ne (f64.eq
(tee_local $3 (tee_local $3
(if (result f64) (select
(f64.ne
(f64.const 1) (f64.const 1)
(f64.const 0)
)
(f64.const 2) (f64.const 2)
(f64.eq
(f64.const 1) (f64.const 1)
(f64.const 0)
)
) )
) )
(f64.const 0) (f64.const 0)
) )
(get_local $3)
(unreachable) (unreachable)
(get_local $3)
) )
) )
(set_global $logical/i (set_global $logical/i
(if (result i32) (select
(i32.const 1) (i32.const 1)
(i32.const 2) (i32.const 2)
(i32.eqz
(i32.const 1) (i32.const 1)
) )
) )
)
(if (if
(i32.eqz (i32.eqz
(i32.eq (i32.eq
@ -4290,10 +4322,12 @@
(unreachable) (unreachable)
) )
(set_global $logical/i (set_global $logical/i
(if (result i32) (select
(i32.const 0)
(i32.const 0)
(i32.const 1) (i32.const 1)
(i32.const 0)
(i32.eqz
(i32.const 0)
)
) )
) )
(if (if
@ -4306,15 +4340,14 @@
(unreachable) (unreachable)
) )
(set_global $logical/I (set_global $logical/I
(if (result i64) (select
(i64.ne
(i64.const 1) (i64.const 1)
(i64.const 0)
)
(i64.const 2) (i64.const 2)
(i64.eqz
(i64.const 1) (i64.const 1)
) )
) )
)
(if (if
(i32.eqz (i32.eqz
(i64.eq (i64.eq
@ -4325,13 +4358,12 @@
(unreachable) (unreachable)
) )
(set_global $logical/I (set_global $logical/I
(if (result i64) (select
(i64.ne (i64.const 1)
(i64.const 0) (i64.const 0)
(i64.eqz
(i64.const 0) (i64.const 0)
) )
(i64.const 0)
(i64.const 1)
) )
) )
(if (if
@ -4344,13 +4376,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/f (set_global $logical/f
(if (result f32) (select
(f32.ne (f32.const 1)
(f32.const 2)
(f32.eq
(f32.const 1) (f32.const 1)
(f32.const 0) (f32.const 0)
) )
(f32.const 2)
(f32.const 1)
) )
) )
(if (if
@ -4363,13 +4395,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/f (set_global $logical/f
(if (result f32) (select
(f32.ne (f32.const 1)
(f32.const 0)
(f32.eq
(f32.const 0) (f32.const 0)
(f32.const 0) (f32.const 0)
) )
(f32.const 0)
(f32.const 1)
) )
) )
(if (if
@ -4382,13 +4414,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/F (set_global $logical/F
(if (result f64) (select
(f64.ne (f64.const 1)
(f64.const 2)
(f64.eq
(f64.const 1) (f64.const 1)
(f64.const 0) (f64.const 0)
) )
(f64.const 2)
(f64.const 1)
) )
) )
(if (if
@ -4401,13 +4433,13 @@
(unreachable) (unreachable)
) )
(set_global $logical/F (set_global $logical/F
(if (result f64) (select
(f64.ne (f64.const 1)
(f64.const 0)
(f64.eq
(f64.const 0) (f64.const 0)
(f64.const 0) (f64.const 0)
) )
(f64.const 0)
(f64.const 1)
) )
) )
(if (if

View File

@ -472,13 +472,13 @@
) )
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2)
(i32.rem_u (i32.rem_u
(get_local $1) (get_local $1)
(i32.const 4) (i32.const 4)
) )
(get_local $2) (get_local $2)
(get_local $2)
) )
(block (block
(set_local $4 (set_local $4

View File

@ -527,14 +527,16 @@
(block $break|0 (block $break|0
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (select
(get_local $2) (get_local $2)
(i32.rem_u (i32.rem_u
(get_local $1) (get_local $1)
(i32.const 4) (i32.const 4)
) )
(i32.eqz
(get_local $2) (get_local $2)
) )
)
(block (block
(block (block
(i32.store8 (i32.store8
@ -2335,6 +2337,9 @@
(loop $continue|0 (loop $continue|0
(if (if
(if (result i32) (if (result i32)
(i32.eqz
(get_local $2)
)
(get_local $2) (get_local $2)
(i32.eq (i32.eq
(i32.load8_u (i32.load8_u
@ -2344,7 +2349,6 @@
(get_local $1) (get_local $1)
) )
) )
(get_local $2)
) )
(block (block
(block (block

View File

@ -160,6 +160,7 @@
(loop $continue|3 (loop $continue|3
(if (if
(if (result i32) (if (result i32)
(i32.eqz
(tee_local $0 (tee_local $0
(block (result i32) (block (result i32)
(set_local $0 (set_local $0
@ -174,6 +175,8 @@
(get_local $0) (get_local $0)
) )
) )
)
(get_local $0)
(block (result i32) (block (result i32)
(set_global $while/m (set_global $while/m
(i32.add (i32.add
@ -183,7 +186,6 @@
) )
(get_global $while/m) (get_global $while/m)
) )
(get_local $0)
) )
(block (block
(nop) (nop)