mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Transition to TypeFlags for specific type checks; Optimize logical ops a bit
This commit is contained in:
parent
fc777b3a89
commit
d89703cdad
@ -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;
|
||||
|
377
src/compiler.ts
377
src/compiler.ts
@ -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;
|
||||
}
|
||||
|
229
src/types.ts
229
src/types.ts
@ -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. */
|
||||
|
19
tests/binaryen/optimize-if-eqz.js
Normal file
19
tests/binaryen/optimize-if-eqz.js
Normal 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());
|
14
tests/binaryen/optimize-if-eqz.wast
Normal file
14
tests/binaryen/optimize-if-eqz.wast
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user