Update internal ABI to zero/sign-extend where necessary only (#87)

This commit is contained in:
Daniel Wirtz
2018-05-06 00:00:54 +02:00
committed by GitHub
parent ce2bf00d62
commit 50f6c1c460
72 changed files with 8846 additions and 9459 deletions

View File

@ -5,7 +5,8 @@
import {
Compiler,
ConversionKind
ConversionKind,
WrapMode
} from "./compiler";
import {
@ -33,7 +34,12 @@ import {
HostOp,
NativeType,
ExpressionRef,
ExpressionId
ExpressionId,
getExpressionId,
getExpressionType,
getConstValueI64High,
getConstValueI64Low,
getConstValueI32
} from "./module";
import {
@ -151,12 +157,21 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE, WrapMode.WRAP);
}
switch (compiler.currentType.kind) {
default: { // any integer up to 32-bits incl. bool
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16: {
ret = module.createUnary(UnaryOp.ClzI32, arg0);
break;
}
case TypeKind.BOOL: // usually overflows
case TypeKind.I32:
case TypeKind.U32: {
ret = module.createUnary(UnaryOp.ClzI32, arg0);
break;
}
@ -185,9 +200,7 @@ export function compileCall(
ret = module.createUnary(UnaryOp.ClzI64, arg0);
break;
}
case TypeKind.F32:
case TypeKind.F64:
case TypeKind.VOID: {
default: {
compiler.error(
DiagnosticCode.Operation_not_supported,
reportNode.range
@ -224,12 +237,21 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.NONE, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE, WrapMode.WRAP);
}
switch (compiler.currentType.kind) {
default: { // any integer up to 32-bits incl. bool
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16: {
ret = module.createUnary(UnaryOp.CtzI32, arg0);
break;
}
case TypeKind.BOOL: // usually overflows
case TypeKind.I32:
case TypeKind.U32: {
ret = module.createUnary(UnaryOp.CtzI32, arg0);
break;
}
@ -258,9 +280,7 @@ export function compileCall(
ret = module.createUnary(UnaryOp.CtzI64, arg0);
break;
}
case TypeKind.F32:
case TypeKind.F64:
case TypeKind.VOID: {
default: {
compiler.error(
DiagnosticCode.Operation_not_supported,
reportNode.range
@ -297,12 +317,21 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE, WrapMode.WRAP);
}
switch (compiler.currentType.kind) {
default: { // any integer up to 32-bits incl. bool
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16: {
ret = module.createUnary(UnaryOp.PopcntI32, arg0);
break;
}
case TypeKind.BOOL: // usually overflows
case TypeKind.I32:
case TypeKind.U32: {
ret = module.createUnary(UnaryOp.PopcntI32, arg0);
break;
}
@ -331,9 +360,7 @@ export function compileCall(
ret = module.createUnary(UnaryOp.PopcntI64, arg0);
break;
}
case TypeKind.F32:
case TypeKind.F64:
case TypeKind.VOID: {
default: {
compiler.error(
DiagnosticCode.Operation_not_supported,
reportNode.range
@ -370,18 +397,18 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE, WrapMode.WRAP);
}
arg1 = compiler.compileExpression(operands[1], compiler.currentType);
arg1 = compiler.compileExpression(operands[1], compiler.currentType, ConversionKind.IMPLICIT, WrapMode.NONE);
switch (compiler.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL: {
ret = compiler.makeSmallIntegerWrap(
ret = compiler.ensureSmallIntegerWrap(
module.createBinary(BinaryOp.RotlI32, arg0, arg1),
compiler.currentType
);
@ -427,7 +454,7 @@ export function compileCall(
break;
}
}
return ret;
return ret; // possibly overflows
}
case "rotr": { // rotr<T?>(value: T, shift: T) -> T
if (operands.length != 2) {
@ -455,18 +482,18 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE, WrapMode.WRAP);
}
arg1 = compiler.compileExpression(operands[1], compiler.currentType);
arg1 = compiler.compileExpression(operands[1], compiler.currentType, ConversionKind.IMPLICIT, WrapMode.NONE);
switch (compiler.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL: {
ret = compiler.makeSmallIntegerWrap(
ret = compiler.ensureSmallIntegerWrap(
module.createBinary(BinaryOp.RotrI32, arg0, arg1),
compiler.currentType
);
@ -512,7 +539,7 @@ export function compileCall(
break;
}
}
return ret;
return ret; // possibly overflowws
}
case "abs": { // abs<T?>(value: T) -> T
if (operands.length != 1) {
@ -540,18 +567,17 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.WRAP);
}
switch (compiler.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
// doesn't need sign-extension here because ifFalse below is either positive
// or MIN_VALUE (-MIN_VALUE == MIN_VALUE) if selected
case TypeKind.I32: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
ret = module.createSelect(
// possibly overflows, e.g. abs<i8>(-128) == 128
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i32, false);
ret = module.createSelect( // x > 0 ? x : 0-x
module.createTeeLocal(tempLocal.index, arg0),
module.createBinary(BinaryOp.SubI32, // ifFalse
module.createI32(0),
@ -565,7 +591,7 @@ export function compileCall(
break;
}
case TypeKind.ISIZE: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
ret = module.createSelect(
module.createTeeLocal(tempLocal.index, arg0),
module.createBinary(
@ -586,7 +612,7 @@ export function compileCall(
break;
}
case TypeKind.I64: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
ret = module.createSelect(
module.createTeeLocal(tempLocal.index, arg0),
module.createBinary(BinaryOp.SubI64,
@ -668,17 +694,24 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.WRAP);
}
arg1 = compiler.compileExpression(operands[1], compiler.currentType);
arg1 = compiler.compileExpression(operands[1], compiler.currentType, ConversionKind.IMPLICIT, WrapMode.WRAP);
switch (compiler.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
let flow = compiler.currentFunction.flow;
let tempLocal0 = compiler.currentFunction.getTempLocal(
compiler.currentType,
!flow.canOverflow(arg0, compiler.currentType)
);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(
compiler.currentType,
!flow.canOverflow(arg1, compiler.currentType)
);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -694,8 +727,15 @@ export function compileCall(
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.BOOL: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
let flow = compiler.currentFunction.flow;
let tempLocal0 = compiler.currentFunction.getTempLocal(
compiler.currentType,
!flow.canOverflow(arg0, compiler.currentType)
);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(
compiler.currentType,
!flow.canOverflow(arg1, compiler.currentType)
);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -708,8 +748,8 @@ export function compileCall(
break;
}
case TypeKind.I64: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -722,8 +762,8 @@ export function compileCall(
break;
}
case TypeKind.U64: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -736,8 +776,8 @@ export function compileCall(
break;
}
case TypeKind.ISIZE: {
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -761,8 +801,8 @@ export function compileCall(
ret = module.createUnreachable();
break;
}
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -822,17 +862,24 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.WRAP);
}
arg1 = compiler.compileExpression(operands[1], compiler.currentType);
arg1 = compiler.compileExpression(operands[1], compiler.currentType, ConversionKind.IMPLICIT, WrapMode.WRAP);
switch (compiler.currentType.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
let flow = compiler.currentFunction.flow;
let tempLocal0 = compiler.currentFunction.getTempLocal(
compiler.currentType,
!flow.canOverflow(arg0, compiler.currentType)
);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(
compiler.currentType,
!flow.canOverflow(arg1, compiler.currentType)
);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -848,8 +895,15 @@ export function compileCall(
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.BOOL: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
let flow = compiler.currentFunction.flow;
let tempLocal0 = compiler.currentFunction.getTempLocal(
compiler.currentType,
!flow.canOverflow(arg0, compiler.currentType)
);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(
compiler.currentType,
!flow.canOverflow(arg1, compiler.currentType)
);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -862,8 +916,8 @@ export function compileCall(
break;
}
case TypeKind.I64: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -876,8 +930,8 @@ export function compileCall(
break;
}
case TypeKind.U64: {
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -890,8 +944,8 @@ export function compileCall(
break;
}
case TypeKind.ISIZE: {
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -915,8 +969,8 @@ export function compileCall(
ret = module.createUnreachable();
break;
}
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal0 = compiler.currentFunction.getTempLocal(compiler.options.usizeType, false);
let tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
compiler.currentFunction.freeTempLocal(tempLocal0);
ret = module.createSelect(
module.createTeeLocal(tempLocal0.index, arg0),
@ -976,9 +1030,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
switch (compiler.currentType.kind) {
case TypeKind.USIZE: {
@ -1041,9 +1095,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
switch (compiler.currentType.kind) {
case TypeKind.USIZE: {
@ -1106,11 +1160,11 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
arg1 = compiler.compileExpression(operands[1], compiler.currentType);
arg1 = compiler.compileExpression(operands[1], compiler.currentType, ConversionKind.IMPLICIT, WrapMode.NONE);
switch (compiler.currentType.kind) { // TODO: does an integer version make sense?
case TypeKind.F32: {
ret = module.createBinary(BinaryOp.CopysignF32, arg0, arg1);
@ -1157,9 +1211,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
switch (compiler.currentType.kind) {
case TypeKind.USIZE: {
@ -1222,13 +1276,13 @@ export function compileCall(
switch (typeArguments[0].kind) {
case TypeKind.I32:
case TypeKind.U32: {
arg0 = compiler.compileExpression(operands[0], Type.f32);
arg0 = compiler.compileExpression(operands[0], Type.f32, ConversionKind.IMPLICIT, WrapMode.NONE);
ret = module.createUnary(UnaryOp.ReinterpretF32, arg0);
break;
}
case TypeKind.I64:
case TypeKind.U64: {
arg0 = compiler.compileExpression(operands[0], Type.f64);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.IMPLICIT, WrapMode.NONE);
ret = module.createUnary(UnaryOp.ReinterpretF64, arg0);
break;
}
@ -1248,8 +1302,10 @@ export function compileCall(
operands[0],
compiler.options.isWasm64
? Type.f64
: Type.f32
);
: Type.f32,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
ret = module.createUnary(
compiler.options.isWasm64
? UnaryOp.ReinterpretF64
@ -1259,12 +1315,12 @@ export function compileCall(
break;
}
case TypeKind.F32: {
arg0 = compiler.compileExpression(operands[0], Type.u32);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.IMPLICIT, WrapMode.NONE);
ret = module.createUnary(UnaryOp.ReinterpretI32, arg0);
break;
}
case TypeKind.F64: {
arg0 = compiler.compileExpression(operands[0], Type.u64);
arg0 = compiler.compileExpression(operands[0], Type.i64, ConversionKind.IMPLICIT, WrapMode.NONE);
ret = module.createUnary(UnaryOp.ReinterpretI64, arg0);
break;
}
@ -1306,9 +1362,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
switch (compiler.currentType.kind) { // TODO: integer versions (that return f64 or convert)?
case TypeKind.F32: {
@ -1358,9 +1414,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE);
arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE, WrapMode.NONE);
}
switch (compiler.currentType.kind) {
case TypeKind.USIZE: {
@ -1430,7 +1486,12 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType);
arg0 = compiler.compileExpression(
operands[0],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
let offset = operands.length == 2 ? evaluateConstantOffset(compiler, operands[1]) : 0; // reports
if (offset < 0) { // reported in evaluateConstantOffset
return module.createUnreachable();
@ -1477,24 +1538,33 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType);
arg0 = compiler.compileExpression(
operands[0],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
arg1 = compiler.compileExpression(
operands[1],
typeArguments[0],
typeArguments[0].is(TypeFlags.INTEGER)
? ConversionKind.NONE // wraps a larger integer type to a smaller one, i.e. i32.store8
: ConversionKind.IMPLICIT
? ConversionKind.NONE // no need to convert to small int (but now might result in a float)
: ConversionKind.IMPLICIT,
WrapMode.NONE
);
let type: Type;
if (
compiler.currentType.is(TypeFlags.INTEGER) &&
typeArguments[0].is(TypeFlags.INTEGER) &&
typeArguments[0].size > compiler.currentType.size
(
!compiler.currentType.is(TypeFlags.INTEGER) || // float to int
compiler.currentType.size < typeArguments[0].size // int to larger int (clear garbage bits)
)
) {
arg1 = compiler.convertExpression(
arg1,
compiler.currentType, typeArguments[0],
ConversionKind.IMPLICIT,
WrapMode.NONE, // still clears garbage bits
operands[1]
);
type = typeArguments[0];
@ -1701,16 +1771,27 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
arg0 = compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
}
let type = compiler.currentType;
arg1 = compiler.compileExpression(operands[1], type);
arg2 = compiler.compileExpression(operands[2], Type.i32);
arg1 = compiler.compileExpression(operands[1], type, ConversionKind.IMPLICIT, WrapMode.NONE);
arg2 = compiler.makeIsTrueish(
compiler.compileExpressionRetainType(operands[2], Type.bool, WrapMode.NONE),
compiler.currentType
);
compiler.currentType = type;
switch (compiler.currentType.kind) {
default: { // any value type
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL: {
ret = module.createSelect(arg0, arg1, arg2);
break;
}
default: { // any other value type
ret = module.createSelect(arg0, arg1, arg2);
break;
}
@ -1768,7 +1849,7 @@ export function compileCall(
);
arg0 = module.createUnreachable();
} else {
arg0 = compiler.compileExpression(operands[0], Type.i32);
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.IMPLICIT, WrapMode.NONE);
}
if (typeArguments) {
compiler.error(
@ -1794,14 +1875,29 @@ export function compileCall(
compiler.currentType = Type.void;
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType);
arg1 = compiler.compileExpression(operands[1], compiler.options.usizeType);
arg2 = compiler.compileExpression(operands[2], compiler.options.usizeType);
arg0 = compiler.compileExpression(
operands[0],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
arg1 = compiler.compileExpression(
operands[1],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
arg2 = compiler.compileExpression(
operands[2],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
compiler.currentType = Type.void;
throw new Error("not implemented");
// return module.createHost(HostOp.MoveMemory, null, [ arg0, arg1, arg2 ]);
}
case "set_memory": { // set_memory(dest: usize, value: u32, n: usize) -> void
case "set_memory": { // set_memory(dest: usize, value: u8, n: usize) -> void
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
@ -1816,9 +1912,24 @@ export function compileCall(
compiler.currentType = Type.void;
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType);
arg1 = compiler.compileExpression(operands[1], Type.u32);
arg2 = compiler.compileExpression(operands[2], compiler.options.usizeType);
arg0 = compiler.compileExpression(
operands[0],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
arg1 = compiler.compileExpression(
operands[1],
Type.u32,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
arg2 = compiler.compileExpression(
operands[2],
compiler.options.usizeType,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
compiler.currentType = Type.void;
throw new Error("not implemented");
// return module.createHost(HostOp.SetMemory, null, [ arg0, arg1, arg2 ]);
@ -1850,10 +1961,10 @@ export function compileCall(
compiler.currentType = typeArguments[0];
return module.createUnreachable();
}
arg0 = compiler.compileExpression(
arg0 = compiler.compileExpressionRetainType(
operands[0],
compiler.options.usizeType,
ConversionKind.NONE
WrapMode.NONE
);
compiler.currentType = typeArguments[0];
if (compiler.currentType.kind != TypeKind.USIZE) {
@ -1900,9 +2011,9 @@ export function compileCall(
);
return module.createUnreachable();
}
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
arg0 = compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.WRAP);
} else {
arg0 = compiler.compileExpressionRetainType(operands[0], Type.i32);
arg0 = compiler.compileExpressionRetainType(operands[0], Type.bool, WrapMode.WRAP);
}
let type = compiler.currentType;
@ -1988,20 +2099,37 @@ export function compileCall(
compiler.currentType = Type.void;
} else {
switch (compiler.currentType.kind) {
default: { // any integer up to 32-bits incl. bool
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i32);
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.BOOL: {
let flow = compiler.currentFunction.flow;
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(
compiler.currentType,
!flow.canOverflow(arg0, compiler.currentType)
);
ret = module.createIf(
module.createUnary(UnaryOp.EqzI32,
module.createTeeLocal(tempLocal.index, arg0)
),
abort,
module.createGetLocal(tempLocal.index, NativeType.I32)
module.createTeeLocal(tempLocal.index, arg0),
module.createGetLocal(tempLocal.index, NativeType.I32),
abort
);
break;
}
case TypeKind.I32:
case TypeKind.U32:
default: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i32, false);
ret = module.createIf(
module.createTeeLocal(tempLocal.index, arg0),
module.createGetLocal(tempLocal.index, NativeType.I32),
abort
);
break;
}
case TypeKind.I64:
case TypeKind.U64: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i64);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.i64, false);
ret = module.createIf(
module.createUnary(UnaryOp.EqzI64,
module.createTeeLocal(tempLocal.index, arg0)
@ -2013,7 +2141,7 @@ export function compileCall(
}
case TypeKind.ISIZE:
case TypeKind.USIZE: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(compiler.options.usizeType, false);
ret = module.createIf(
module.createUnary(
compiler.options.isWasm64
@ -2027,7 +2155,7 @@ export function compileCall(
break;
}
case TypeKind.F32: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.f32);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.f32, false);
ret = module.createIf(
module.createBinary(BinaryOp.EqF32,
module.createTeeLocal(tempLocal.index, arg0),
@ -2039,7 +2167,7 @@ export function compileCall(
break;
}
case TypeKind.F64: {
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.f64);
let tempLocal = compiler.currentFunction.getAndFreeTempLocal(Type.f64, false);
ret = module.createIf(
module.createBinary(BinaryOp.EqF64,
module.createTeeLocal(tempLocal.index, arg0),
@ -2078,7 +2206,7 @@ export function compileCall(
}
let flow = compiler.currentFunction.flow;
flow.set(FlowFlags.UNCHECKED_CONTEXT);
ret = compiler.compileExpressionRetainType(operands[0], contextualType, false);
ret = compiler.compileExpressionRetainType(operands[0], contextualType, WrapMode.NONE);
flow.unset(FlowFlags.UNCHECKED_CONTEXT);
return ret;
}
@ -2100,7 +2228,12 @@ export function compileCall(
compiler.currentType = Type.i8;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.i8, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.i8,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "i16": {
if (typeArguments) {
@ -2117,7 +2250,12 @@ export function compileCall(
compiler.currentType = Type.i16;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.i16, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.i16,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "i32": {
if (typeArguments) {
@ -2134,7 +2272,12 @@ export function compileCall(
compiler.currentType = Type.i32;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.i32, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.i32,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "i64": {
if (typeArguments) {
@ -2151,7 +2294,12 @@ export function compileCall(
compiler.currentType = Type.i64;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.i64, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.i64,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "isize": {
if (typeArguments) {
@ -2175,7 +2323,8 @@ export function compileCall(
compiler.options.isWasm64
? Type.isize64
: Type.isize32,
ConversionKind.EXPLICIT
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "u8": {
@ -2193,7 +2342,12 @@ export function compileCall(
compiler.currentType = Type.u8;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.u8, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.u8,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "u16": {
if (typeArguments) {
@ -2210,7 +2364,12 @@ export function compileCall(
compiler.currentType = Type.u16;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.u16, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.u16,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "u32": {
if (typeArguments) {
@ -2227,7 +2386,12 @@ export function compileCall(
compiler.currentType = Type.u32;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.u32, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.u32,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "u64": {
if (typeArguments) {
@ -2244,7 +2408,12 @@ export function compileCall(
compiler.currentType = Type.u64;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.u64, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.u64,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "usize": {
if (typeArguments) {
@ -2264,7 +2433,8 @@ export function compileCall(
return compiler.compileExpression(
operands[0],
compiler.options.usizeType,
ConversionKind.EXPLICIT
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "bool": {
@ -2282,7 +2452,12 @@ export function compileCall(
compiler.currentType = Type.bool;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.bool, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.bool,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "f32": {
if (typeArguments) {
@ -2299,7 +2474,12 @@ export function compileCall(
compiler.currentType = Type.f32;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.f32, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.f32,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
case "f64": {
if (typeArguments) {
@ -2316,7 +2496,12 @@ export function compileCall(
compiler.currentType = Type.f64;
return module.createUnreachable();
}
return compiler.compileExpression(operands[0], Type.f64, ConversionKind.EXPLICIT);
return compiler.compileExpression(
operands[0],
Type.f64,
ConversionKind.EXPLICIT,
WrapMode.NONE
);
}
}
var expr = deferASMCall(compiler, prototype, operands, contextualType, reportNode);
@ -2460,7 +2645,7 @@ function evaluateConstantType(
if (operands.length == 1) { // optional type argument
if (typeArguments) {
if (typeArguments.length == 1) {
compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, false);
compiler.compileExpression(operands[0], typeArguments[0], ConversionKind.IMPLICIT, WrapMode.NONE);
} else {
if (typeArguments.length) {
compiler.error(
@ -2469,10 +2654,10 @@ function evaluateConstantType(
);
return null;
}
compiler.compileExpressionRetainType(operands[0], Type.i32, false);
compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
}
} else {
compiler.compileExpressionRetainType(operands[0], Type.i32, false);
compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
}
return compiler.currentType;
}
@ -2494,12 +2679,12 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
var expr: ExpressionRef;
var value: i32;
if (compiler.options.isWasm64) {
expr = compiler.precomputeExpression(expression, Type.usize64);
expr = compiler.precomputeExpression(expression, Type.usize64, ConversionKind.IMPLICIT, WrapMode.NONE);
if (
_BinaryenExpressionGetId(expr) != ExpressionId.Const ||
_BinaryenExpressionGetType(expr) != NativeType.I64 ||
_BinaryenConstGetValueI64High(expr) != 0 ||
(value = _BinaryenConstGetValueI64Low(expr)) < 0
getExpressionId(expr) != ExpressionId.Const ||
getExpressionType(expr) != NativeType.I64 ||
getConstValueI64High(expr) != 0 ||
(value = getConstValueI64Low(expr)) < 0
) {
compiler.error(
DiagnosticCode.Operation_not_supported,
@ -2508,11 +2693,11 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
value = -1;
}
} else {
expr = compiler.precomputeExpression(expression, Type.usize32);
expr = compiler.precomputeExpression(expression, Type.usize32, ConversionKind.IMPLICIT, WrapMode.NONE);
if (
_BinaryenExpressionGetId(expr) != ExpressionId.Const ||
_BinaryenExpressionGetType(expr) != NativeType.I32 ||
(value = _BinaryenConstGetValueI32(expr)) < 0
getExpressionId(expr) != ExpressionId.Const ||
getExpressionType(expr) != NativeType.I32 ||
(value = getConstValueI32(expr)) < 0
) {
compiler.error(
DiagnosticCode.Operation_not_supported,
@ -2588,7 +2773,7 @@ export function compileAbort(
if (!(abortInstance && compiler.compileFunction(abortInstance))) return module.createUnreachable();
var messageArg = message != null
? compiler.compileExpression(message, stringType)
? compiler.compileExpression(message, stringType, ConversionKind.IMPLICIT, WrapMode.NONE)
: stringType.toNativeZero(module);
var filenameArg = compiler.compileStaticString(reportNode.range.source.normalizedPath);

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,48 @@ import {
FunctionRef,
ExpressionRef,
Index,
readString
getFunctionName,
getFunctionBody,
getFunctionParamCount,
getFunctionParamType,
getFunctionResultType,
getExpressionId,
getExpressionType,
getBlockName,
getBlockChildCount,
getBlockChild,
getIfCondition,
getIfTrue,
getIfFalse,
getLoopName,
getLoopBody,
getBreakName,
getBreakCondition,
getGetLocalIndex,
getSetLocalIndex,
getSetLocalValue,
getLoadOffset,
getLoadPtr,
getStoreOffset,
getStorePtr,
getStoreValue,
getConstValueI32,
getConstValueI64Low,
getConstValueI64High,
getConstValueF32,
getConstValueF64,
getUnaryOp,
getUnaryValue,
getBinaryOp,
getBinaryLeft,
getBinaryRight,
getSelectThen,
getSelectElse,
getSelectCondition,
getDropValue,
getReturnValue,
getHostOp,
getHostOperand
} from "./module";
// TODO :-)
@ -37,34 +78,34 @@ export class Decompiler {
}
decompileFunction(func: FunctionRef): void {
var name = readString(_BinaryenFunctionGetName(func)) || "$" + this.functionId.toString(10);
var body = _BinaryenFunctionGetBody(func);
var name = getFunctionName(func) || "$" + this.functionId.toString(10);
var body = getFunctionBody(func);
this.push("function ");
this.push(name);
this.push("(");
for (let i: Index = 0, k: Index = _BinaryenFunctionGetNumParams(func); i < k; ++i) {
for (let i: Index = 0, k: Index = getFunctionParamCount(func); i < k; ++i) {
if (i > 0) this.push(", ");
this.push("$");
this.push(i.toString(10));
this.push(": ");
this.push(nativeTypeToType(_BinaryenFunctionGetParam(func, i)));
this.push(nativeTypeToType(getFunctionParamType(func, i)));
}
this.push("): ");
this.push(nativeTypeToType(_BinaryenFunctionGetResult(func)));
this.push(nativeTypeToType(getFunctionResultType(func)));
this.push(" ");
if (_BinaryenExpressionGetId(body) != ExpressionId.Block) {
if (getExpressionId(body) != ExpressionId.Block) {
this.push("{\n");
}
this.decompileExpression(body);
if (_BinaryenExpressionGetId(body) != ExpressionId.Block) {
if (getExpressionId(body) != ExpressionId.Block) {
this.push("\n}\n");
}
++this.functionId;
}
decompileExpression(expr: ExpressionRef): void {
var id = _BinaryenExpressionGetId(expr);
var type = _BinaryenExpressionGetType(expr);
var id = getExpressionId(expr);
var type = getExpressionType(expr);
var nested: ExpressionRef;
var string: string | null;
@ -72,14 +113,14 @@ export class Decompiler {
switch (id) {
case ExpressionId.Block: { // TODO: magic
if ((string = readString(_BinaryenBlockGetName(expr))) != null) {
if ((string = getBlockName(expr)) != null) {
this.push(string);
this.push(": ");
}
this.push("{\n");
k = _BinaryenBlockGetNumChildren(expr);
k = getBlockChildCount(expr);
for (i = 0; i < k; ++i) {
this.decompileExpression(_BinaryenBlockGetChild(expr, i));
this.decompileExpression(getBlockChild(expr, i));
}
this.push("}\n");
return;
@ -87,38 +128,38 @@ export class Decompiler {
case ExpressionId.If: {
if (type == NativeType.None) {
this.push("if (");
this.decompileExpression(_BinaryenIfGetCondition(expr));
this.decompileExpression(getIfCondition(expr));
this.push(") ");
this.decompileExpression(_BinaryenIfGetIfTrue(expr));
if (nested = _BinaryenIfGetIfFalse(expr)) {
this.decompileExpression(getIfTrue(expr));
if (nested = getIfFalse(expr)) {
this.push(" else ");
this.decompileExpression(nested);
}
} else {
this.decompileExpression(_BinaryenIfGetCondition(expr));
this.decompileExpression(getIfCondition(expr));
this.push(" ? ");
this.decompileExpression(_BinaryenIfGetIfTrue(expr));
this.decompileExpression(getIfTrue(expr));
this.push(" : ");
this.decompileExpression(_BinaryenIfGetIfFalse(expr));
this.decompileExpression(getIfFalse(expr));
}
return;
}
case ExpressionId.Loop: {
if ((string = readString(_BinaryenLoopGetName(expr))) != null) {
if ((string = getLoopName(expr)) != null) {
this.push(string);
this.push(": ");
}
this.push("do ");
this.decompileExpression(_BinaryenLoopGetBody(expr));
this.decompileExpression(getLoopBody(expr));
this.push("while (0);\n");
}
case ExpressionId.Break: {
if (nested = _BinaryenBreakGetCondition(expr)) {
if (nested = getBreakCondition(expr)) {
this.push("if (");
this.decompileExpression(nested);
this.push(") ");
}
if ((string = readString(_BinaryenBreakGetName(expr))) != null) {
if ((string = getBreakName(expr)) != null) {
this.push("break ");
this.push(string);
this.push(";\n");
@ -135,14 +176,14 @@ export class Decompiler {
}
case ExpressionId.GetLocal: {
this.push("$");
this.push(_BinaryenGetLocalGetIndex(expr).toString(10));
this.push(getGetLocalIndex(expr).toString(10));
return;
}
case ExpressionId.SetLocal: {
this.push("$");
this.push(_BinaryenSetLocalGetIndex(expr).toString(10));
this.push(getSetLocalIndex(expr).toString(10));
this.push(" = ");
this.decompileExpression(_BinaryenSetLocalGetValue(expr));
this.decompileExpression(getSetLocalValue(expr));
return;
}
case ExpressionId.GetGlobal:
@ -153,9 +194,9 @@ export class Decompiler {
this.push("load<");
this.push(nativeTypeToType(type));
this.push(">(");
this.push(_BinaryenLoadGetOffset(expr).toString(10));
this.push(getLoadOffset(expr).toString(10));
this.push(" + ");
this.decompileExpression(_BinaryenLoadGetPtr(expr));
this.decompileExpression(getLoadPtr(expr));
this.push(")");
return;
}
@ -163,290 +204,290 @@ export class Decompiler {
this.push("store<");
this.push(nativeTypeToType(type));
this.push(">(");
this.push(_BinaryenStoreGetOffset(expr).toString(10));
this.push(getStoreOffset(expr).toString(10));
this.push(" + ");
this.decompileExpression(_BinaryenStoreGetPtr(expr));
this.decompileExpression(getStorePtr(expr));
this.push(", ");
this.decompileExpression(_BinaryenStoreGetValue(expr));
this.decompileExpression(getStoreValue(expr));
this.push(")");
return;
}
case ExpressionId.Const: {
switch (type) {
case NativeType.I32: {
this.push(_BinaryenConstGetValueI32(expr).toString(10));
this.push(getConstValueI32(expr).toString(10));
return;
}
case NativeType.I64: {
this.push(
i64_to_string(
i64_new(
_BinaryenConstGetValueI64Low(expr),
_BinaryenConstGetValueI64High(expr)
getConstValueI64Low(expr),
getConstValueI64High(expr)
)
)
);
return;
}
case NativeType.F32: {
this.push(_BinaryenConstGetValueF32(expr).toString(10));
this.push(getConstValueF32(expr).toString(10));
return;
}
case NativeType.F64: {
this.push(_BinaryenConstGetValueF64(expr).toString(10));
this.push(getConstValueF64(expr).toString(10));
return;
}
}
break;
}
case ExpressionId.Unary: {
switch (_BinaryenUnaryGetOp(expr)) {
switch (getUnaryOp(expr)) {
case UnaryOp.ClzI32: {
this.push("clz<i32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.CtzI32: {
this.push("ctz<i32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.PopcntI32: {
this.push("popcnt<i32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.NegF32:
case UnaryOp.NegF64: {
this.push("-");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.AbsF32: {
this.push("abs<f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.CeilF32: {
this.push("ceil<f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.FloorF32: {
this.push("floor<f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.TruncF32: {
this.push("trunc<f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.NearestF32: {
this.push("nearest<i32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.SqrtF32: {
this.push("sqrt<f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.EqzI32:
case UnaryOp.EqzI64: {
this.push("!");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ClzI64: {
this.push("clz<i64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.CtzI64: {
this.push("ctz<i64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.PopcntI64: {
this.push("popcnt<i64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.AbsF64: {
this.push("abs<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.CeilF64: {
this.push("ceil<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.FloorF64: {
this.push("floor<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.TruncF64: {
this.push("trunc<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.NearestF64: {
this.push("nearest<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.SqrtF64: {
this.push("sqrt<f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.ExtendI32: {
this.push("<i64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ExtendU32: {
this.push("<i64><u64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.WrapI64: {
this.push("<i32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF32ToI32: {
this.push("<i32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF32ToI64: {
this.push("<i64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF32ToU32: {
this.push("<i32><u32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF32ToU64: {
this.push("<i64><u64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF64ToI32: {
this.push("<i32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF64ToI64: {
this.push("<i64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF64ToU32: {
this.push("<i32><u32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.TruncF64ToU64: {
this.push("<i64><u64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ReinterpretF32: {
this.push("reinterpret<f32,i32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.ReinterpretF64: {
this.push("reinterpret<f64,i64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.ConvertI32ToF32: {
this.push("<f32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertI32ToF64: {
this.push("<f64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertU32ToF32: {
this.push("<f32><u32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertU32ToF64: {
this.push("<f64><u32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertI64ToF32: {
this.push("<f32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertI64ToF64: {
this.push("<f64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertU64ToF32: {
this.push("<f32><u64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ConvertU64ToF64: {
this.push("<f64><u64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.PromoteF32: {
this.push("<f64>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.DemoteF64: {
this.push("<f32>");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
return;
}
case UnaryOp.ReinterpretI32: {
this.push("reinterpret<i32,f32>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
case UnaryOp.ReinterpretI64: {
this.push("reinterpret<i64,f64>(");
this.decompileExpression(_BinaryenUnaryGetValue(expr));
this.decompileExpression(getUnaryValue(expr));
this.push(")");
return;
}
@ -454,121 +495,121 @@ export class Decompiler {
break;
}
case ExpressionId.Binary: { // TODO: precedence
switch (_BinaryenBinaryGetOp(expr)) {
switch (getBinaryOp(expr)) {
case BinaryOp.AddI32:
case BinaryOp.AddI64:
case BinaryOp.AddF32:
case BinaryOp.AddF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" + ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.SubI32:
case BinaryOp.SubI64:
case BinaryOp.SubF32:
case BinaryOp.SubF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" - ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.MulI32:
case BinaryOp.MulI64:
case BinaryOp.MulF32:
case BinaryOp.MulF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" * ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.DivI32:
case BinaryOp.DivI64:
case BinaryOp.DivF32:
case BinaryOp.DivF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" / ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.DivU32: {
this.push("<i32>(<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" / <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.RemI32:
case BinaryOp.RemI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" % ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.RemU32: {
this.push("<i32>(<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" / <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.AndI32:
case BinaryOp.AndI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" & ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.OrI32:
case BinaryOp.OrI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" | ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.XorI32:
case BinaryOp.XorI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" ^ ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.ShlI32:
case BinaryOp.ShlI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" << ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.ShrU32:
case BinaryOp.ShrU64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" >>> ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.ShrI32:
case BinaryOp.ShrI64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" >> ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.RotlI32: {
this.push("rotl<i32>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.RotrI32: {
this.push("rotr<i32>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
@ -576,187 +617,187 @@ export class Decompiler {
case BinaryOp.EqI64:
case BinaryOp.EqF32:
case BinaryOp.EqF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" == ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.NeI32:
case BinaryOp.NeI64:
case BinaryOp.NeF32:
case BinaryOp.NeF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" != ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.LtI32:
case BinaryOp.LtI64:
case BinaryOp.LtF32:
case BinaryOp.LtF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" < ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.LtU32: {
this.push("<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" < <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.LeI32:
case BinaryOp.LeI64:
case BinaryOp.LeF32:
case BinaryOp.LeF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" <= ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.LeU32: {
this.push("<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" <= <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GtI32:
case BinaryOp.GtI64:
case BinaryOp.GtF32:
case BinaryOp.GtF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" > ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GtU32: {
this.push("<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" > <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GeI32:
case BinaryOp.GeI64:
case BinaryOp.GeF32:
case BinaryOp.GeF64: {
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" >= ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GeU32: {
this.push("<u32>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" >= <u32>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.DivU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" / <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.RemU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" % <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.RotlI64: {
this.push("rotl<i64>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.RotrI64: {
this.push("rotr<i64>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.LtU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" < <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.LeU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" <= <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GtU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" > <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.GeU64: {
this.push("<u64>");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(" >= <u64>");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
return;
}
case BinaryOp.CopysignF32: {
this.push("copysign<f32>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.MinF32: {
this.push("min<f32>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.MaxF32: {
this.push("max<f32>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.CopysignF64: {
this.push("copysign<f64>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.MinF64: {
this.push("min<f64>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
case BinaryOp.MaxF64: {
this.push("max<f64>(");
this.decompileExpression(_BinaryenBinaryGetLeft(expr));
this.decompileExpression(getBinaryLeft(expr));
this.push(", ");
this.decompileExpression(_BinaryenBinaryGetRight(expr));
this.decompileExpression(getBinaryRight(expr));
this.push(")");
return;
}
@ -767,21 +808,21 @@ export class Decompiler {
this.push("select<");
this.push(nativeTypeToType(type));
this.push(">(");
this.decompileExpression(_BinaryenSelectGetIfTrue(expr));
this.decompileExpression(getSelectThen(expr));
this.push(", ");
this.decompileExpression(_BinaryenSelectGetIfFalse(expr));
this.decompileExpression(getSelectElse(expr));
this.push(", ");
this.decompileExpression(_BinaryenSelectGetCondition(expr));
this.decompileExpression(getSelectCondition(expr));
this.push(")");
return;
}
case ExpressionId.Drop: {
this.decompileExpression(_BinaryenDropGetValue(expr));
this.decompileExpression(getDropValue(expr));
this.push(";\n");
return;
}
case ExpressionId.Return: {
if (nested = _BinaryenReturnGetValue(expr)) {
if (nested = getReturnValue(expr)) {
this.push("return ");
this.decompileExpression(nested);
this.push(";\n");
@ -791,14 +832,14 @@ export class Decompiler {
return;
}
case ExpressionId.Host: {
switch (_BinaryenHostGetOp(expr)) {
switch (getHostOp(expr)) {
case HostOp.CurrentMemory: {
this.push("current_memory()");
return;
}
case HostOp.GrowMemory: {
this.push("grow_memory(");
this.decompileExpression(_BinaryenHostGetOperand(expr, 0));
this.decompileExpression(getHostOperand(expr, 0));
this.push(")");
return;
}

View File

@ -2,6 +2,9 @@
declare type I64 = { __Long__: true }; // opaque
declare const i64_zero: I64;
declare const i64_one: I64;
declare function i64_new(lo: i32, hi?: i32): I64;
declare function i64_low(value: I64): i32;
declare function i64_high(value: I64): i32;
@ -21,6 +24,9 @@ declare function i64_shr(left: I64, right: I64): I64;
declare function i64_shr_u(left: I64, right: I64): I64;
declare function i64_not(value: I64): I64;
declare function i64_eq(left: I64, right: I64): bool;
declare function i64_ne(left: I64, right: I64): bool;
declare function i64_align(value: I64, alignment: i32): I64;
declare function i64_is_i8(value: I64): bool;

View File

@ -1,5 +1,9 @@
const Long = global.Long || require("long");
global.i64_zero = Long.ZERO;
global.i64_one = Long.ONE;
global.i64_new = function(lo, hi) {
return Long.fromBits(lo, hi);
};
@ -68,6 +72,14 @@ global.i64_not = function(value) {
return value.not();
};
global.i64_eq = function(left, right) {
return left.eq(right);
};
global.i64_ne = function(left, right) {
return left.ne(right);
};
global.i64_align = function(value, alignment) {
assert(alignment && (alignment & (alignment - 1)) == 0);
var mask = Long.fromInt(alignment - 1);

View File

@ -2,6 +2,12 @@
type I64 = i64;
@global
const i64_zero: I64 = 0;
@global
const i64_one: I64 = 1;
@global
function i64_new(lo: i32, hi: i32 = 0): I64 {
return lo | (hi << 32);
@ -87,6 +93,16 @@ function i64_not(value: I64): I64 {
return ~value;
}
@global
function i64_eq(left: I64, right: I64): bool {
return left == right;
}
@global
function i64_ne(left: I64, right: I64): bool {
return left != right;
}
@global
function i64_align(value: I64, alignment: i64): I64 {
var mask: i64 = alignment - 1;

View File

@ -346,7 +346,7 @@ export class Module {
operands: ExpressionRef[] | null = null
): ExpressionRef {
var cStr = allocString(name);
var cArr = allocI32Array(operands);
var cArr = allocPtrArray(operands);
try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<ExpressionRef[]>operands).length : 0);
} finally {
@ -485,7 +485,7 @@ export class Module {
type: NativeType = NativeType.None
): ExpressionRef {
var cStr = allocString(label);
var cArr = allocI32Array(children);
var cArr = allocPtrArray(children);
try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally {
@ -579,7 +579,7 @@ export class Module {
returnType: NativeType
): ExpressionRef {
var cStr = allocString(target);
var cArr = allocI32Array(operands);
var cArr = allocPtrArray(operands);
try {
return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally {
@ -594,7 +594,7 @@ export class Module {
returnType: NativeType
): ExpressionRef {
var cStr = allocString(target);
var cArr = allocI32Array(operands);
var cArr = allocPtrArray(operands);
try {
return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally {
@ -608,7 +608,7 @@ export class Module {
operands: ExpressionRef[] | null,
typeName: string
): ExpressionRef {
var cArr = allocI32Array(operands);
var cArr = allocPtrArray(operands);
var cStr = allocString(typeName);
try {
return _BinaryenCallIndirect(this.ref, index, cArr, operands && operands.length || 0, cStr);
@ -843,7 +843,7 @@ export class Module {
}
setFunctionTable(funcs: FunctionRef[]): void {
var cArr = allocI32Array(funcs);
var cArr = allocPtrArray(funcs);
try {
_BinaryenSetFunctionTable(this.ref, cArr, funcs.length);
} finally {
@ -943,7 +943,8 @@ export class Module {
return Relooper.create(this);
}
cloneExpression(expr: ExpressionRef,
cloneExpression(
expr: ExpressionRef,
noSideEffects: bool = false,
maxDepth: i32 = i32.MAX_VALUE
): ExpressionRef { // currently supports side effect free expressions only
@ -953,7 +954,7 @@ export class Module {
var nested1: ExpressionRef,
nested2: ExpressionRef;
switch (_BinaryenExpressionGetId(expr)) {
switch (_BinaryenExpressionGetId(expr)) {
case ExpressionId.Const: {
switch (_BinaryenExpressionGetType(expr)) {
case NativeType.I32: {
@ -991,21 +992,23 @@ export class Module {
if (!(nested1 = this.cloneExpression(_BinaryenLoadGetPtr(expr), noSideEffects, maxDepth))) {
break;
}
return _BinaryenLoadIsAtomic(expr)
? _BinaryenAtomicLoad(this.ref,
_BinaryenLoadGetBytes(expr),
_BinaryenLoadGetOffset(expr),
_BinaryenExpressionGetType(expr),
nested1
)
: _BinaryenLoad(this.ref,
_BinaryenLoadGetBytes(expr),
_BinaryenLoadIsSigned(expr) ? 1 : 0,
_BinaryenLoadGetOffset(expr),
_BinaryenLoadGetAlign(expr),
_BinaryenExpressionGetType(expr),
nested1
);
return (
_BinaryenLoadIsAtomic(expr)
? _BinaryenAtomicLoad(this.ref,
_BinaryenLoadGetBytes(expr),
_BinaryenLoadGetOffset(expr),
_BinaryenExpressionGetType(expr),
nested1
)
: _BinaryenLoad(this.ref,
_BinaryenLoadGetBytes(expr),
_BinaryenLoadIsSigned(expr) ? 1 : 0,
_BinaryenLoadGetOffset(expr),
_BinaryenLoadGetAlign(expr),
_BinaryenExpressionGetType(expr),
nested1
)
);
}
case ExpressionId.Unary: {
if (!(nested1 = this.cloneExpression(_BinaryenUnaryGetValue(expr), noSideEffects, maxDepth))) {
@ -1052,6 +1055,206 @@ export class Module {
}
}
// expressions
export function getExpressionId(expr: ExpressionRef): ExpressionId {
return _BinaryenExpressionGetId(expr);
}
export function getExpressionType(expr: ExpressionRef): NativeType {
return _BinaryenExpressionGetType(expr);
}
export function getConstValueI32(expr: ExpressionRef): i32 {
return _BinaryenConstGetValueI32(expr);
}
export function getConstValueI64Low(expr: ExpressionRef): i32 {
return _BinaryenConstGetValueI64Low(expr);
}
export function getConstValueI64High(expr: ExpressionRef): i32 {
return _BinaryenConstGetValueI64High(expr);
}
export function getConstValueF32(expr: ExpressionRef): f32 {
return _BinaryenConstGetValueF32(expr);
}
export function getConstValueF64(expr: ExpressionRef): f32 {
return _BinaryenConstGetValueF64(expr);
}
export function getGetLocalIndex(expr: ExpressionRef): Index {
return _BinaryenGetLocalGetIndex(expr);
}
export function getSetLocalIndex(expr: ExpressionRef): Index {
return _BinaryenSetLocalGetIndex(expr);
}
export function getSetLocalValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenSetLocalGetValue(expr);
}
export function isTeeLocal(expr: ExpressionRef): bool {
return _BinaryenSetLocalIsTee(expr);
}
export function getBinaryOp(expr: ExpressionRef): BinaryOp {
return _BinaryenBinaryGetOp(expr);
}
export function getBinaryLeft(expr: ExpressionRef): ExpressionRef {
return _BinaryenBinaryGetLeft(expr);
}
export function getBinaryRight(expr: ExpressionRef): ExpressionRef {
return _BinaryenBinaryGetRight(expr);
}
export function getUnaryOp(expr: ExpressionRef): UnaryOp {
return _BinaryenUnaryGetOp(expr);
}
export function getUnaryValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenUnaryGetValue(expr);
}
export function getLoadBytes(expr: ExpressionRef): u32 {
return _BinaryenLoadGetBytes(expr);
}
export function getLoadOffset(expr: ExpressionRef): u32 {
return _BinaryenLoadGetOffset(expr);
}
export function getLoadPtr(expr: ExpressionRef): ExpressionRef {
return _BinaryenLoadGetPtr(expr);
}
export function isLoadSigned(expr: ExpressionRef): bool {
return _BinaryenLoadIsSigned(expr);
}
export function getStoreBytes(expr: ExpressionRef): u32 {
return _BinaryenStoreGetBytes(expr);
}
export function getStoreOffset(expr: ExpressionRef): u32 {
return _BinaryenStoreGetOffset(expr);
}
export function getStorePtr(expr: ExpressionRef): ExpressionRef {
return _BinaryenStoreGetPtr(expr);
}
export function getStoreValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenStoreGetValue(expr);
}
export function getBlockName(expr: ExpressionRef): string | null {
return readString(_BinaryenBlockGetName(expr));
}
export function getBlockChildCount(expr: ExpressionRef): Index {
return _BinaryenBlockGetNumChildren(expr);
}
export function getBlockChild(expr: ExpressionRef, index: Index): ExpressionRef {
return _BinaryenBlockGetChild(expr, index);
}
export function getIfCondition(expr: ExpressionRef): ExpressionRef {
return _BinaryenIfGetCondition(expr);
}
export function getIfTrue(expr: ExpressionRef): ExpressionRef {
return _BinaryenIfGetIfTrue(expr);
}
export function getIfFalse(expr: ExpressionRef): ExpressionRef {
return _BinaryenIfGetIfFalse(expr);
}
export function getLoopName(expr: ExpressionRef): string | null {
return readString(_BinaryenLoopGetName(expr));
}
export function getLoopBody(expr: ExpressionRef): ExpressionRef {
return _BinaryenLoopGetBody(expr);
}
export function getBreakName(expr: ExpressionRef): string | null {
return readString(_BinaryenBreakGetName(expr));
}
export function getBreakCondition(expr: ExpressionRef): ExpressionRef {
return _BinaryenBreakGetCondition(expr);
}
export function getSelectThen(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetIfTrue(expr);
}
export function getSelectElse(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetIfFalse(expr);
}
export function getSelectCondition(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetCondition(expr);
}
export function getDropValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenDropGetValue(expr);
}
export function getReturnValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenReturnGetValue(expr);
}
export function getCallTarget(expr: ExpressionRef): string | null {
return readString(_BinaryenCallGetTarget(expr));
}
export function getHostOp(expr: ExpressionRef): ExpressionRef {
return _BinaryenHostGetOp(expr);
}
export function getHostOperandCount(expr: ExpressionRef): Index {
return _BinaryenHostGetNumOperands(expr);
}
export function getHostOperand(expr: ExpressionRef, index: Index): ExpressionRef {
return _BinaryenHostGetOperand(expr, index);
}
export function getHostName(expr: ExpressionRef): string | null {
return readString(_BinaryenHostGetNameOperand(expr));
}
// functions
export function getFunctionBody(func: FunctionRef): ExpressionRef {
return _BinaryenFunctionGetBody(func);
}
export function getFunctionName(func: FunctionRef): string | null {
return readString(_BinaryenFunctionGetName(func));
}
export function getFunctionParamCount(func: FunctionRef): Index {
return _BinaryenFunctionGetNumParams(func);
}
export function getFunctionParamType(func: FunctionRef, index: Index): NativeType {
return _BinaryenFunctionGetParam(func, index);
}
export function getFunctionResultType(func: FunctionRef): NativeType {
return _BinaryenFunctionGetResult(func);
}
export class Relooper {
module: Module;
@ -1100,7 +1303,7 @@ export class Relooper {
}
// export function hasSideEffects(expr: ExpressionRef): bool {
// switch (_BinaryenExpressionGetId(expr)) {
// switch (_BinaryenExpressionGetId(expr = getPtr(expr))) {
// case ExpressionId.GetLocal:
// case ExpressionId.GetGlobal:
// case ExpressionId.Const:
@ -1168,6 +1371,10 @@ function allocI32Array(i32s: i32[] | null): usize {
return ptr;
}
function allocPtrArray(ptrs: usize[] | null): usize {
return allocI32Array(ptrs); // TODO: WASM64 one day
}
function stringLengthUTF8(str: string): usize {
var len = 0;
for (let i = 0, k = str.length; i < k; ++i) {

View File

@ -15,6 +15,8 @@ import {
import {
Type,
TypeKind,
TypeFlags,
Signature,
typesToString
@ -69,6 +71,34 @@ import {
Module,
NativeType,
FunctionRef,
ExpressionRef,
ExpressionId,
BinaryOp,
UnaryOp,
getExpressionId,
getGetLocalIndex,
isTeeLocal,
getSetLocalValue,
getBinaryOp,
getConstValueI32,
getBinaryLeft,
getBinaryRight,
getUnaryOp,
getExpressionType,
getLoadBytes,
isLoadSigned,
getIfTrue,
getIfFalse,
getSelectThen,
getSelectElse,
getCallTarget,
getBlockChildCount,
getBlockChild,
getBlockName,
getConstValueF32,
getConstValueF64,
getConstValueI64Low
} from "./module";
/** Path delimiter inserted between file system levels. */
@ -173,6 +203,8 @@ export class Program extends DiagnosticEmitter {
options: Options;
/** Elements by internal name. */
elementsLookup: Map<string,Element> = new Map();
/** Class and function instances by internal name. */
instancesLookup: Map<string,Element> = new Map();
/** Types by internal name. */
typesLookup: Map<string,Type> = noTypesYet;
/** Declared type aliases. */
@ -2671,6 +2703,7 @@ export class FunctionPrototype extends Element {
contextualTypeArguments
);
this.instances.set(instanceKey, instance);
this.program.instancesLookup.set(internalName, instance);
return instance;
}
@ -2865,7 +2898,7 @@ export class Function extends Element {
private tempF64s: Local[] | null = null;
/** Gets a free temporary local of the specified type. */
getTempLocal(type: Type): Local {
getTempLocal(type: Type, wrapped: bool): Local {
var temps: Local[] | null;
switch (type.toNativeType()) {
case NativeType.I32: {
@ -2886,12 +2919,15 @@ export class Function extends Element {
}
default: throw new Error("concrete type expected");
}
var local: Local;
if (temps && temps.length) {
let ret = temps.pop();
ret.type = type;
return ret;
local = temps.pop();
local.type = type;
} else {
local = this.addLocal(type);
}
return this.addLocal(type);
this.flow.setLocalWrapped(local.index, wrapped);
return local;
}
/** Frees the temporary local for reuse. */
@ -2924,7 +2960,7 @@ export class Function extends Element {
}
/** Gets and immediately frees a temporary local of the specified type. */
getAndFreeTempLocal(type: Type): Local {
getAndFreeTempLocal(type: Type, wrapped: bool): Local {
var temps: Local[];
switch (type.toNativeType()) {
case NativeType.I32: {
@ -2945,11 +2981,15 @@ export class Function extends Element {
}
default: throw new Error("concrete type expected");
}
if (temps.length > 0) {
return temps[temps.length - 1];
var local: Local;
if (temps.length) {
local = temps[temps.length - 1];
local.type = type;
} else {
local = this.addLocal(type);
temps.push(local);
}
var local: Local = this.addLocal(type);
temps.push(local);
this.flow.setLocalWrapped(local.index, wrapped);
return local;
}
@ -3201,6 +3241,7 @@ export class ClassPrototype extends Element {
instance = new Class(this, simpleName, internalName, typeArguments, baseClass);
instance.contextualTypeArguments = contextualTypeArguments;
this.instances.set(instanceKey, instance);
this.program.instancesLookup.set(internalName, instance);
var memoryOffset: u32 = 0;
if (baseClass) {
@ -3541,7 +3582,9 @@ export const enum FlowFlags {
/** This branch is part of inlining a function. */
INLINE_CONTEXT = 1 << 10,
/** This branch explicitly requests no bounds checking. */
UNCHECKED_CONTEXT = 1 << 11
UNCHECKED_CONTEXT = 1 << 11,
/** This branch returns a properly wrapped value. */
RETURNS_WRAPPED = 1 << 12
}
/** A control flow evaluator. */
@ -3565,8 +3608,10 @@ export class Flow {
contextualTypeArguments: Map<string,Type> | null;
/** Scoped local variables. */
scopedLocals: Map<string,Local> | null = null;
/** Scoped global variables. */
// scopedGlobals: Map<Local,Global> | null = null;
/** Local variable wrap states for the first 64 locals. */
wrappedLocals: I64;
/** Local variable wrap states for locals with index >= 64. */
wrappedLocalsExt: I64[] | null;
/** Creates the parent flow of the specified function. */
static create(currentFunction: Function): Flow {
@ -3579,6 +3624,8 @@ export class Flow {
parentFlow.returnLabel = null;
parentFlow.returnType = currentFunction.signature.returnType;
parentFlow.contextualTypeArguments = currentFunction.contextualTypeArguments;
parentFlow.wrappedLocals = i64_new(0);
parentFlow.wrappedLocalsExt = null;
return parentFlow;
}
@ -3602,6 +3649,8 @@ export class Flow {
branch.returnLabel = this.returnLabel;
branch.returnType = this.returnType;
branch.contextualTypeArguments = this.contextualTypeArguments;
branch.wrappedLocals = this.wrappedLocals;
branch.wrappedLocalsExt = this.wrappedLocalsExt ? this.wrappedLocalsExt.slice() : null;
return branch;
}
@ -3619,7 +3668,7 @@ export class Flow {
this.scopedLocals = null;
}
// Propagate flags to parent
// Propagate conditionaal flags to parent
if (this.is(FlowFlags.RETURNS)) {
parent.set(FlowFlags.CONDITIONALLY_RETURNS);
}
@ -3640,8 +3689,8 @@ export class Flow {
}
/** Adds a new scoped local of the specified name. */
addScopedLocal(type: Type, name: string, declaration?: VariableDeclaration): Local {
var scopedLocal = this.currentFunction.getTempLocal(type);
addScopedLocal(type: Type, name: string, wrapped: bool, declaration?: VariableDeclaration): Local {
var scopedLocal = this.currentFunction.getTempLocal(type, false);
if (!this.scopedLocals) this.scopedLocals = new Map();
else {
let existingLocal = this.scopedLocals.get(name);
@ -3656,6 +3705,7 @@ export class Flow {
}
}
this.scopedLocals.set(name, scopedLocal);
this.setLocalWrapped(scopedLocal.index, wrapped);
return scopedLocal;
}
@ -3699,26 +3749,360 @@ export class Flow {
return this.currentFunction.localsByName.get(name);
}
/** Adds a scoped global for an outer scoped local. */
// addScopedGlobal(scopedLocal: Local): Global {
// var scopedGlobals = this.scopedGlobals;
// var scopedGlobal: Global | null;
// if (!scopedGlobals) {
// this.scopedGlobals = scopedGlobals = new Map();
// } else {
// scopedGlobal = scopedGlobals.get(scopedLocal);
// if (scopedGlobal) return scopedGlobal;
// }
// scopedGlobal = new Global(
// scopedLocal.program,
// scopedLocal.simpleName,
// this.currentFunction.internalName + INNER_DELIMITER + scopedLocal.internalName,
// scopedLocal.type,
// assert(scopedLocal.declaration)
// );
// scopedGlobals.set(scopedLocal, scopedGlobal);
// return scopedGlobal;
// }
/** Tests if the local with the specified index is considered wrapped. */
isLocalWrapped(index: i32): bool {
var map: I64;
var ext: I64[] | null;
if (index < 64) {
if (index < 0) return true; // inlined constant
map = this.wrappedLocals;
} else if (ext = this.wrappedLocalsExt) {
let i = ((index - 64) / 64) | 0;
if (i >= ext.length) return false;
map = ext[i];
index -= (i + 1) * 64;
} else {
return false;
}
return i64_ne(
i64_and(
map,
i64_shl(
i64_one,
i64_new(index)
)
),
i64_zero
);
}
/** Sets if the local with the specified index is considered wrapped. */
setLocalWrapped(index: i32, wrapped: bool): void {
var map: I64;
var i: i32 = -1;
if (index < 64) {
if (index < 0) return; // inlined constant
map = this.wrappedLocals;
} else {
let ext = this.wrappedLocalsExt;
i = ((index - 64) / 64) | 0;
if (!ext) ext = new Array(i + 1);
else while (ext.length <= i) ext.push(i64_new(0));
map = ext[i];
index -= (i + 1) * 64;
}
map = wrapped
? i64_or(
map,
i64_shl(
i64_one,
i64_new(index)
)
)
: i64_and(
map,
i64_not(
i64_shl(
i64_one,
i64_new(index)
)
)
);
if (i >= 0) (<I64[]>this.wrappedLocalsExt)[i] = map;
else this.wrappedLocals = map;
}
/** Inherits flags and local wrap states from the specified flow (e.g. on inner block). */
inherit(other: Flow): void {
this.flags |= other.flags & (
FlowFlags.RETURNS |
FlowFlags.RETURNS_WRAPPED |
FlowFlags.THROWS |
FlowFlags.BREAKS |
FlowFlags.CONTINUES |
FlowFlags.ALLOCATES
);
this.wrappedLocals = other.wrappedLocals;
this.wrappedLocalsExt = other.wrappedLocalsExt; // no need to slice because other flow is finished
}
/** Inherits mutual flags and local wrap states from the specified flows (e.g. on then/else branches). */
inheritMutual(left: Flow, right: Flow): void {
// flags set in both arms
this.flags |= left.flags & right.flags & (
FlowFlags.RETURNS |
FlowFlags.RETURNS_WRAPPED |
FlowFlags.THROWS |
FlowFlags.BREAKS |
FlowFlags.CONTINUES |
FlowFlags.ALLOCATES
);
// locals wrapped in both arms
this.wrappedLocals = i64_and(
left.wrappedLocals,
right.wrappedLocals
);
var leftExt = left.wrappedLocalsExt;
var rightExt = right.wrappedLocalsExt;
if (leftExt != null && rightExt != null) {
let thisExt = this.wrappedLocalsExt;
let minLength = min(leftExt.length, rightExt.length);
if (minLength) {
if (!thisExt) thisExt = new Array(minLength);
else while (thisExt.length < minLength) thisExt.push(i64_new(0));
for (let i = 0; i < minLength; ++i) {
thisExt[i] = i64_and(
leftExt[i],
rightExt[i]
);
}
}
}
}
/**
* Tests if an expression can possibly overflow in the context of this flow. Assumes that the
* expression might already have overflown and returns `false` only if the operation neglects
* any possibly combination of garbage bits being present.
*/
canOverflow(expr: ExpressionRef, type: Type): bool {
// TODO: the following catches most common and a few uncommon cases, but there are additional
// opportunities here, obviously.
assert(type != Type.void);
// types other than i8, u8, i16, u16 and bool do not overflow
if (!type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) return false;
var operand: ExpressionRef;
switch (getExpressionId(expr)) {
// overflows if the local isn't wrapped or the conversion does
case ExpressionId.GetLocal: {
let currentFunction = this.currentFunction;
let local = currentFunction.localsByIndex[getGetLocalIndex(expr)];
return !currentFunction.flow.isLocalWrapped(local.index)
|| canConversionOverflow(local.type, type);
}
// overflows if the value does
case ExpressionId.SetLocal: {
assert(isTeeLocal(expr));
return this.canOverflow(getSetLocalValue(expr), type);
}
// never overflows because globals are wrapped on set
case ExpressionId.GetGlobal: return false;
case ExpressionId.Binary: {
switch (getBinaryOp(expr)) {
// comparisons do not overflow (result is 0 or 1)
case BinaryOp.EqI32:
case BinaryOp.EqI64:
case BinaryOp.EqF32:
case BinaryOp.EqF64:
case BinaryOp.NeI32:
case BinaryOp.NeI64:
case BinaryOp.NeF32:
case BinaryOp.NeF64:
case BinaryOp.LtI32:
case BinaryOp.LtU32:
case BinaryOp.LtI64:
case BinaryOp.LtU64:
case BinaryOp.LtF32:
case BinaryOp.LtF64:
case BinaryOp.LeI32:
case BinaryOp.LeU32:
case BinaryOp.LeI64:
case BinaryOp.LeU64:
case BinaryOp.LeF32:
case BinaryOp.LeF64:
case BinaryOp.GtI32:
case BinaryOp.GtU32:
case BinaryOp.GtI64:
case BinaryOp.GtU64:
case BinaryOp.GtF32:
case BinaryOp.GtF64:
case BinaryOp.GeI32:
case BinaryOp.GeU32:
case BinaryOp.GeI64:
case BinaryOp.GeU64:
case BinaryOp.GeF32:
case BinaryOp.GeF64: return false;
// result won't overflow if one side is 0 or if one side is 1 and the other wrapped
case BinaryOp.MulI32: {
return !(
(
getExpressionId(operand = getBinaryLeft(expr)) == ExpressionId.Const &&
(
getConstValueI32(operand) == 0 ||
(
getConstValueI32(operand) == 1 &&
!this.canOverflow(getBinaryRight(expr), type)
)
)
) || (
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
(
getConstValueI32(operand) == 0 ||
(
getConstValueI32(operand) == 1 &&
!this.canOverflow(getBinaryLeft(expr), type)
)
)
)
);
}
// result won't overflow if one side is a constant less than this type's mask or one side
// is wrapped
case BinaryOp.AndI32: {
// note that computeSmallIntegerMask returns the mask minus the MSB for signed types
// because signed value garbage bits must be guaranteed to be equal to the MSB.
return !(
(
(
getExpressionId(operand = getBinaryLeft(expr)) == ExpressionId.Const &&
getConstValueI32(operand) <= type.computeSmallIntegerMask(Type.i32)
) || !this.canOverflow(operand, type)
) || (
(
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
getConstValueI32(operand) <= type.computeSmallIntegerMask(Type.i32)
) || !this.canOverflow(operand, type)
)
);
}
// overflows if the shift doesn't clear potential garbage bits
case BinaryOp.ShlI32: {
let shift = 32 - type.size;
return getExpressionId(operand = getBinaryRight(expr)) != ExpressionId.Const
|| getConstValueI32(operand) < shift;
}
// overflows if the value does and the shift doesn't clear potential garbage bits
case BinaryOp.ShrI32: {
let shift = 32 - type.size;
return this.canOverflow(getBinaryLeft(expr), type) && (
getExpressionId(operand = getBinaryRight(expr)) != ExpressionId.Const ||
getConstValueI32(operand) < shift
);
}
// overflows if the shift does not clear potential garbage bits. if an unsigned value is
// wrapped, it can't overflow.
case BinaryOp.ShrU32: {
let shift = 32 - type.size;
return type.is(TypeFlags.SIGNED)
? !(
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
getConstValueI32(operand) > shift // must clear MSB
)
: this.canOverflow(getBinaryLeft(expr), type) && !(
getExpressionId(operand = getBinaryRight(expr)) == ExpressionId.Const &&
getConstValueI32(operand) >= shift // can leave MSB
);
}
// overflows if any side does
case BinaryOp.DivU32:
case BinaryOp.RemI32:
case BinaryOp.RemU32: {
return this.canOverflow(getBinaryLeft(expr), type)
|| this.canOverflow(getBinaryRight(expr), type);
}
}
break;
}
case ExpressionId.Unary: {
switch (getUnaryOp(expr)) {
// comparisons do not overflow (result is 0 or 1)
case UnaryOp.EqzI32:
case UnaryOp.EqzI64: return false;
// overflow if the maximum result (32) cannot be represented in the target type
case UnaryOp.ClzI32:
case UnaryOp.CtzI32:
case UnaryOp.PopcntI32: return type.size < 7;
}
break;
}
// overflows if the value cannot be represented in the target type
case ExpressionId.Const: {
let value: i32 = 0;
switch (getExpressionType(expr)) {
case NativeType.I32: { value = getConstValueI32(expr); break; }
case NativeType.I64: { value = getConstValueI64Low(expr); break; } // discards upper bits
case NativeType.F32: { value = i32(getConstValueF32(expr)); break; }
case NativeType.F64: { value = i32(getConstValueF64(expr)); break; }
default: assert(false);
}
switch (type.kind) {
case TypeKind.I8: return value < i8.MIN_VALUE || value > i8.MAX_VALUE;
case TypeKind.I16: return value < i16.MIN_VALUE || value > i16.MAX_VALUE;
case TypeKind.U8: return value < 0 || value > u8.MAX_VALUE;
case TypeKind.U16: return value < 0 || value > u16.MAX_VALUE;
case TypeKind.BOOL: return (value & ~1) != 0;
}
break;
}
// overflows if the conversion does
case ExpressionId.Load: {
let fromType: Type;
switch (getLoadBytes(expr)) {
case 1: { fromType = isLoadSigned(expr) ? Type.i8 : Type.u8; break; }
case 2: { fromType = isLoadSigned(expr) ? Type.i16 : Type.u16; break; }
default: { fromType = isLoadSigned(expr) ? Type.i32 : Type.u32; break; }
}
return canConversionOverflow(fromType, type);
}
// overflows if the result does, which is either
// - the last expression of the block, by contract, if the block doesn't have a label
// - the last expression or the value of an inner br if the block has a label (TODO)
case ExpressionId.Block: {
if (!getBlockName(expr)) {
let size = assert(getBlockChildCount(expr));
let last = getBlockChild(expr, size - 1);
return this.canOverflow(last, type);
}
// actually, brs with a value that'd be handled here is not emitted atm
break;
}
// overflows if either side does
case ExpressionId.If: {
return this.canOverflow(getIfTrue(expr), type)
|| this.canOverflow(assert(getIfFalse(expr)), type);
}
// overflows if either side does
case ExpressionId.Select: {
return this.canOverflow(getSelectThen(expr), type)
|| this.canOverflow(getSelectElse(expr), type);
}
// overflows if the call does not return a wrapped value or the conversion does
case ExpressionId.Call: {
let program = this.currentFunction.program;
let instance = assert(program.instancesLookup.get(assert(getCallTarget(expr))));
assert(instance.kind == ElementKind.FUNCTION);
let returnType = (<Function>instance).signature.returnType;
return !(<Function>instance).flow.is(FlowFlags.RETURNS_WRAPPED)
|| canConversionOverflow(returnType, type);
}
// doesn't technically overflow
case ExpressionId.Unreachable: return false;
}
return true;
}
/** Finalizes this flow. Must be the topmost parent flow of the function. */
finalize(): void {
@ -3729,3 +4113,12 @@ export class Flow {
this.contextualTypeArguments = null;
}
}
/** Tests if a conversion from one type to another can technically overflow. */
function canConversionOverflow(fromType: Type, toType: Type): bool {
var fromSize = fromType.byteSize;
var toSize = toType.byteSize;
return !fromType.is(TypeFlags.INTEGER) // non-i32 locals or returns
|| fromSize > toSize
|| fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED);
}

View File

@ -114,6 +114,24 @@ export class Type {
this.nonNullableType = this;
}
/** Returns the int type of this type. Defaults to `Type.i32` if this is not an int type. */
get intType(): Type {
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: return this;
case TypeKind.BOOL:
default: return Type.i32;
}
}
/** Computes the sign-extending shift in the target type. */
computeSmallIntegerShift(targetType: Type): u32 {
return targetType.size - this.size;
@ -121,7 +139,8 @@ export class Type {
/** Computes the truncating mask in the target type. */
computeSmallIntegerMask(targetType: Type): u32 {
return ~0 >>> (targetType.size - this.size);
var size = this.is(TypeFlags.UNSIGNED) ? this.size : this.size - 1;
return ~0 >>> (targetType.size - size);
}
/** Tests if this type has (all of) the specified flags. */