mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-22 03:01:55 +00:00
Update internal ABI to zero/sign-extend where necessary only (#87)
This commit is contained in:
481
src/builtins.ts
481
src/builtins.ts
@ -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);
|
||||
|
1678
src/compiler.ts
1678
src/compiler.ts
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
6
src/glue/js/i64.d.ts
vendored
6
src/glue/js/i64.d.ts
vendored
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
255
src/module.ts
255
src/module.ts
@ -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) {
|
||||
|
465
src/program.ts
465
src/program.ts
@ -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);
|
||||
}
|
||||
|
21
src/types.ts
21
src/types.ts
@ -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. */
|
||||
|
Reference in New Issue
Block a user