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 {
Compiler,
Target,
ConversionKind
ConversionKind,
makeSmallIntegerWrap
} from "./compiler";
import {
@ -15,7 +17,9 @@ import {
} from "./ast";
import {
Type, TypeKind
Type,
TypeKind,
TypeFlags
} from "./types";
import {
@ -511,23 +515,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.I8:
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.U16:
case TypeKind.BOOL:
ret = module.createBinary(BinaryOp.AndI32,
module.createBinary(BinaryOp.RotlI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerMask)
);
break;
ret = makeSmallIntegerWrap(module.createBinary(BinaryOp.RotlI32, arg0, arg1), compiler.currentType, module);
case TypeKind.I32:
case TypeKind.U32:
@ -584,22 +575,10 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.I8:
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.U16:
case TypeKind.BOOL:
ret = module.createBinary(BinaryOp.AndI32,
module.createBinary(BinaryOp.RotrI32, arg0, arg1),
module.createI32(compiler.currentType.smallIntegerMask)
);
ret = makeSmallIntegerWrap(module.createBinary(BinaryOp.RotrI32, arg0, arg1), compiler.currentType, module);
break;
case TypeKind.I32:
@ -1249,7 +1228,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.F32:
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());
return module.createUnreachable();
}
@ -1261,7 +1240,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case TypeKind.F64:
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());
return module.createUnreachable();
}
@ -1390,7 +1369,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
}
arg0 = compiler.compileExpression(operands[0], usizeType);
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
compiler.currentType = Type.void;

View File

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

View File

@ -43,11 +43,38 @@ export const enum TypeKind {
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. */
export class Type {
/** Type kind. */
kind: TypeKind;
/** Type flags. */
flags: TypeFlags;
/** Size in bits. */
size: i32;
/** Size in bytes. */
@ -56,171 +83,34 @@ export class Type {
classType: Class | null;
/** Underlying function type, if a function type. */
functionType: Function | null;
/** Whether nullable or not. */
isNullable: bool = false;
/** Respective nullable type, if non-nullable. */
nullableType: Type | null = null;
/** Respective non-nullable type, if nullable. */
nonNullableType: Type;
/** Constructs a new resolved type. */
constructor(kind: TypeKind, size: i32) {
constructor(kind: TypeKind, flags: TypeFlags, size: i32) {
this.kind = kind;
this.flags = flags;
this.size = size;
this.byteSize = <i32>ceil<f64>(<f64>size / 8);
this.classType = null;
this.nonNullableType = this;
}
/** Sign-extending 32-bit shift, if a small signed integer. */
get smallIntegerShift(): i32 { return 32 - this.size; }
/** Truncating 32-bit mask, if a small unsigned integer. */
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;
}
/** Computes the sign-extending shift in the target type. */
computeSmallIntegerShift(targetType: Type) {
return targetType.size - this.size;
}
/** Tests if this type is of any unsigned integer kind. */
get isAnyUnsignedInteger(): bool {
switch (this.kind) {
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.U64:
case TypeKind.USIZE:
case TypeKind.BOOL:
return true;
default:
return false;
}
/** Computes the truncating mask in the target type. */
computeSmallIntegerMask(targetType: Type) {
return -1 >>> (targetType.size - this.size);
}
/** Tests if this type is of any signed integer kind. */
get isAnySignedInteger(): bool {
switch (this.kind) {
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 has the specified capabilities. */
is(flags: TypeFlags): bool {
return (this.flags & flags) == flags;
}
/** 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. */
asClass(classType: Class): Type {
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;
return ret;
}
@ -241,16 +131,17 @@ export class Type {
/** Composes a function type from this type and a function. */
asFunction(functionType: Function): Type {
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;
return ret;
}
/** Composes the respective nullable type of this type. */
asNullable(): Type | null {
assert(this.kind == TypeKind.USIZE && !this.isReference);
if (this.isNullable && !this.nullableType) {
(this.nullableType = new Type(this.kind, this.size)).isNullable = true;
assert(this.kind == TypeKind.USIZE);
if (!this.nullableType) {
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.functionType = this.functionType;
}
@ -423,37 +314,37 @@ export class Type {
// Types
/** 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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */
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. */

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,32 +55,38 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
)
(i64.const 0)
(i32.const 1)
)
)
(get_local $7)
(tee_local $7
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
(i32.eq
(get_local $3)
(i32.const 2047)
)
)
(get_local $7)
(i32.eq
(get_local $3)
(i32.const 2047)
)
(i32.const 1)
)
(return
(f64.div
@ -434,31 +440,37 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(get_local $3)
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
)
(i32.const 1)
)
)
(get_local $3)
(tee_local $3
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(get_local $3)
(i32.eq
(get_local $4)
(i32.const 255)
)
(i32.const 1)
)
(return
(f32.div

View File

@ -70,32 +70,42 @@
)
)
(if
(if (result i32)
(tee_local $8
(if (result i32)
(i32.and
(if (result i32)
(i32.eqz
(tee_local $8
(i64.eq
(i64.shl
(get_local $3)
(i64.const 1)
(i32.and
(if (result i32)
(i32.eqz
(tee_local $8
(i64.eq
(i64.shl
(get_local $3)
(i64.const 1)
)
(i64.const 0)
)
)
)
(f64.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
(get_local $8)
)
(i64.const 0)
(i32.const 1)
)
)
(get_local $8)
(f64.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
)
(i32.eq
(get_local $4)
(i32.const 2047)
)
(get_local $8)
)
(get_local $8)
(i32.eq
(get_local $4)
(i32.const 2047)
)
(i32.const 1)
)
(return
(f64.div
@ -530,32 +540,42 @@
)
)
(if
(if (result i32)
(tee_local $8
(if (result i32)
(i32.and
(if (result i32)
(i32.eqz
(tee_local $8
(i32.eq
(i32.shl
(get_local $3)
(i32.const 1)
(i32.and
(if (result i32)
(i32.eqz
(tee_local $8
(i32.eq
(i32.shl
(get_local $3)
(i32.const 1)
)
(i32.const 0)
)
)
)
(f32.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
(get_local $8)
)
(i32.const 0)
(i32.const 1)
)
)
(get_local $8)
(f32.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
)
(i32.eq
(get_local $4)
(i32.const 255)
)
(get_local $8)
)
(get_local $8)
(i32.eq
(get_local $4)
(i32.const 255)
)
(i32.const 1)
)
(return
(f32.div

View File

@ -209,19 +209,22 @@
)
)
(if
(if (result i32)
(i32.lt_u
(get_local $2)
(i32.const 2)
)
(i32.lt_u
(get_local $2)
(i32.const 2)
)
(i32.gt_u
(get_local $2)
(i32.const 3)
(i32.and
(select
(i32.gt_u
(get_local $2)
(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.add

View File

@ -235,19 +235,24 @@
)
)
(if
(if (result i32)
(i32.lt_u
(get_local $8)
(i32.const 2)
)
(i32.lt_u
(get_local $8)
(i32.const 2)
)
(i32.gt_u
(get_local $8)
(i32.const 3)
(i32.and
(select
(i32.gt_u
(get_local $8)
(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.add

View File

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

View File

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

View File

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

View File

@ -65,13 +65,13 @@
)
(loop $continue|0
(if
(if (result i32)
(get_local $2)
(select
(i32.rem_u
(get_local $3)
(i32.const 4)
)
(get_local $2)
(get_local $2)
)
(block
(set_local $4
@ -1687,32 +1687,38 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
)
(i64.const 0)
(i32.const 1)
)
)
(get_local $7)
(tee_local $7
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
(i32.eq
(get_local $3)
(i32.const 2047)
)
)
(get_local $7)
(i32.eq
(get_local $3)
(i32.const 2047)
)
(i32.const 1)
)
(return
(f64.div
@ -2066,31 +2072,37 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(get_local $3)
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
)
(i32.const 1)
)
)
(get_local $3)
(tee_local $3
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(get_local $3)
(i32.eq
(get_local $4)
(i32.const 255)
)
(i32.const 1)
)
(return
(f32.div

View File

@ -86,13 +86,13 @@
)
(loop $continue|0
(if
(if (result i32)
(get_local $2)
(select
(i32.rem_u
(get_local $3)
(i32.const 4)
)
(get_local $2)
(get_local $2)
)
(block
(set_local $4
@ -1708,32 +1708,38 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
)
(i64.const 0)
(i32.const 1)
)
)
(get_local $7)
(tee_local $7
(f64.ne
(tee_local $9
(get_local $1)
)
(get_local $9)
)
(i32.eq
(get_local $3)
(i32.const 2047)
)
)
(get_local $7)
(i32.eq
(get_local $3)
(i32.const 2047)
)
(i32.const 1)
)
(return
(f64.div
@ -2087,31 +2093,37 @@
)
)
(if
(if (result i32)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(get_local $3)
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
)
(i32.const 1)
)
)
(get_local $3)
(tee_local $3
(f32.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(get_local $3)
(i32.eq
(get_local $4)
(i32.const 255)
)
(i32.const 1)
)
(return
(f32.div

View File

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

View File

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

View File

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

View File

@ -160,20 +160,23 @@
(loop $continue|3
(if
(if (result i32)
(tee_local $0
(block (result i32)
(set_local $0
(get_global $while/n)
)
(set_global $while/n
(i32.sub
(get_local $0)
(i32.const 1)
(i32.eqz
(tee_local $0
(block (result i32)
(set_local $0
(get_global $while/n)
)
(set_global $while/n
(i32.sub
(get_local $0)
(i32.const 1)
)
)
(get_local $0)
)
(get_local $0)
)
)
(get_local $0)
(block (result i32)
(set_global $while/m
(i32.add
@ -183,7 +186,6 @@
)
(get_global $while/m)
)
(get_local $0)
)
(block
(nop)