diff --git a/examples/game-of-life/assembly/game-of-life.ts b/examples/game-of-life/assembly/game-of-life.ts index 517155c4..bd4c7baf 100644 --- a/examples/game-of-life/assembly/game-of-life.ts +++ b/examples/game-of-life/assembly/game-of-life.ts @@ -1,8 +1,8 @@ // A simplified version of the game of life as seen on http://dcode.io -let w: u32; // width -let h: u32; // height -let s: u32; // total size +var w: u32; // width +var h: u32; // height +var s: u32; // total size /** Initializes width and height. */ export function init(w_: u32, h_: u32): void { @@ -13,10 +13,10 @@ export function init(w_: u32, h_: u32): void { /** Performs one step. */ export function step(): void { - let y: u32, ym1: u32, yp1: u32; // y, y-1 and y+1 - let x: u32, xm1: u32, xp1: u32; // x, x-1 and x+1 - let hm1: u32 = h - 1, wm1: u32 = w - 1; - let n: u32, v: u8, c: u32 = 0; + var y: u32, ym1: u32, yp1: u32; // y, y-1 and y+1 + var x: u32, xm1: u32, xp1: u32; // x, x-1 and x+1 + var hm1: u32 = h - 1, wm1: u32 = w - 1; + var n: u32, v: u8, c: u32 = 0; for (y = 0; y < h; ++y) { ym1 = select(hm1, y - 1, y == 0); yp1 = select(0, y + 1, y == hm1); diff --git a/examples/i64-polyfill/assembly/i64.ts b/examples/i64-polyfill/assembly/i64.ts index 540a535c..769ca810 100644 --- a/examples/i64-polyfill/assembly/i64.ts +++ b/examples/i64-polyfill/assembly/i64.ts @@ -10,180 +10,180 @@ export function getHi(): u32 { } function clz_(loLeft: u32, hiLeft: u32): void { - const ret: u64 = clz(loLeft | hiLeft << 32); + var ret = clz(loLeft | hiLeft << 32); lo = ret; hi = 0; } export { clz_ as clz }; function ctz_(loLeft: u32, hiLeft: u32): void { - const ret: u64 = ctz(loLeft | hiLeft << 32); + var ret = ctz(loLeft | hiLeft << 32); lo = ret; hi = 0; } export { ctz_ as ctz }; function popcnt_(loLeft: u32, hiLeft: u32): void { - const ret: u64 = popcnt(loLeft | hiLeft << 32); + var ret = popcnt(loLeft | hiLeft << 32); lo = ret; hi = 0; } export { popcnt_ as popcnt }; export function eqz(loLeft: u32, hiLeft: u32): void { - const ret: bool = !(loLeft | hiLeft << 32); + var ret: bool = !(loLeft | hiLeft << 32); lo = ret; hi = 0; } export function add(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) + (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) + (loRight | hiRight << 32); lo = ret; hi = (ret >> 32); } export function sub(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) - (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) - (loRight | hiRight << 32); lo = ret; hi = (ret >> 32); } export function mul(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) * (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) * (loRight | hiRight << 32); lo = ret; hi = (ret >> 32); } export function div_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = ((loLeft | hiLeft << 32) / (loRight | hiRight << 32)); + var ret = ((loLeft | hiLeft << 32) / (loRight | hiRight << 32)); lo = ret; hi = (ret >> 32); } export function div_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) / (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) / (loRight | hiRight << 32); lo = ret; hi = (ret >> 32); } export function rem_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = ((loLeft | hiLeft << 32) % (loRight | hiRight << 32)); + var ret = ((loLeft | hiLeft << 32) % (loRight | hiRight << 32)); lo = ret; hi = (ret >> 32); } export function rem_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) % (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) % (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export function and(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) & (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) & (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export function or(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) | (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) | (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export function xor(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) ^ (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) ^ (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export function shl(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) << (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) << (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export function shr_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = ((loLeft | hiLeft << 32) >> (loRight | hiRight << 32)); + var ret = ((loLeft | hiLeft << 32) >> (loRight | hiRight << 32)); lo = ret; hi = (ret >>> 32); } export function shr_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = (loLeft | hiLeft << 32) >> (loRight | hiRight << 32); + var ret = (loLeft | hiLeft << 32) >> (loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } function rotl_(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = rotl(loLeft | hiLeft << 32, loRight | hiRight << 32); + var ret = rotl(loLeft | hiLeft << 32, loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export { rotl_ as rotl }; function rotr_(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: u64 = rotr(loLeft | hiLeft << 32, loRight | hiRight << 32); + var ret = rotr(loLeft | hiLeft << 32, loRight | hiRight << 32); lo = ret; hi = (ret >>> 32); } export { rotr_ as rotr }; export function eq(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) == (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) == (loRight | hiRight << 32); lo = ret; hi = 0; } export function ne(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) != (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) != (loRight | hiRight << 32); lo = ret; hi = 0; } export function lt_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) < (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) < (loRight | hiRight << 32); lo = ret; hi = 0; } export function lt_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) < (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) < (loRight | hiRight << 32); lo = ret; hi = 0; } export function le_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) <= (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) <= (loRight | hiRight << 32); lo = ret; hi = 0; } export function le_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) <= (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) <= (loRight | hiRight << 32); lo = ret; hi = 0; } export function gt_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) > (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) > (loRight | hiRight << 32); lo = ret; hi = 0; } export function gt_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) > (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) > (loRight | hiRight << 32); lo = ret; hi = 0; } export function ge_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) >= (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) >= (loRight | hiRight << 32); lo = ret; hi = 0; } export function ge_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { - const ret: bool = (loLeft | hiLeft << 32) >= (loRight | hiRight << 32); + var ret: bool = (loLeft | hiLeft << 32) >= (loRight | hiRight << 32); lo = ret; hi = 0; } diff --git a/src/compiler.ts b/src/compiler.ts index f567dbe6..f54b85dd 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -38,7 +38,8 @@ import { Namespace, Parameter, EnumValue, - Property + Property, + VariableLikeElement } from "./program"; import { @@ -376,20 +377,7 @@ export class Compiler extends DiagnosticEmitter { var initializeInStart = false; if (global.hasConstantValue) { - if (global.type.isLongInteger) - initExpr = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0); - else if (global.type.kind == TypeKind.F32) - initExpr = this.module.createF32(global.constantFloatValue); - else if (global.type.kind == TypeKind.F64) - initExpr = this.module.createF64(global.constantFloatValue); - else if (global.type.isSmallInteger) { - if (global.type.isSignedInteger) { - var shift = global.type.smallIntegerShift; - initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0); - } else - initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & global.type.smallIntegerMask: 0); - } else - initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0); + initExpr = makeInlineConstant(global, this.module); } else if (declaration) { if (declaration.initializer) { if (!initExpr) @@ -1018,6 +1006,34 @@ export class Compiler extends DiagnosticEmitter { if (this.currentFunction.locals.has(name)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name); // recoverable else { + if (hasModifier(ModifierKind.CONST, declaration.modifiers)) { + if (init) { + init = this.precomputeExpressionRef(init); + if (_BinaryenExpressionGetId(init) == ExpressionId.Const) { + var local = new Local(this.program, name, -1, type); + switch (_BinaryenExpressionGetType(init)) { + case NativeType.I32: + local = local.withConstantIntegerValue(_BinaryenConstGetValueI32(init), 0); + break; + case NativeType.I64: + local = local.withConstantIntegerValue(_BinaryenConstGetValueI64Low(init), _BinaryenConstGetValueI64High(init)); + break; + case NativeType.F32: + local = local.withConstantFloatValue(_BinaryenConstGetValueF32(init)); + break; + case NativeType.F64: + local = local.withConstantFloatValue(_BinaryenConstGetValueF64(init)); + break; + default: + throw new Error("concrete type expected"); + } + this.currentFunction.locals.set(name, local); + continue; + } + } else { + this.error(DiagnosticCode._const_declarations_must_be_initialized, declaration.range); + } + } this.currentFunction.addLocal(type, name); if (init) initializers.push(this.compileAssignmentWithValue(declaration.name, init)); @@ -1582,7 +1598,7 @@ export class Compiler extends DiagnosticEmitter { ? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0)) : this.module.createTeeLocal(tempLocal.index, left), right, - this.module.createGetLocal(tempLocal.index, tempLocal.type.toNativeType()) + this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()) ); case Token.BAR_BAR: // left || right @@ -1614,7 +1630,7 @@ export class Compiler extends DiagnosticEmitter { : this.currentType == Type.f32 ? this.module.createBinary(BinaryOp.NeF32, condition, this.module.createF32(0)) : this.module.createTeeLocal(tempLocal.index, left), - this.module.createGetLocal(tempLocal.index, tempLocal.type.toNativeType()), + this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()), right ); @@ -1712,8 +1728,9 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); if (element.kind == ElementKind.LOCAL) { + assert((element).type != null); if (tee) { - this.currentType = (element).type; + this.currentType = (element).type; return this.module.createTeeLocal((element).index, valueWithCorrectType); } this.currentType = Type.void; @@ -1896,7 +1913,11 @@ export class Compiler extends DiagnosticEmitter { // local if (element.kind == ElementKind.LOCAL) { - this.currentType = (element).type; + assert((element).type != null); + this.currentType = (element).type; + if ((element).hasConstantValue) + return makeInlineConstant(element, this.module); + assert((element).index >= 0); return this.module.createGetLocal((element).index, this.currentType.toNativeType()); } @@ -1910,18 +1931,9 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); assert(global.type != null); this.currentType = global.type; - if (global.hasConstantValue) { - if (global.type == Type.f32) - return this.module.createF32((element).constantFloatValue); - else if (global.type == Type.f64) - return this.module.createF64((element).constantFloatValue); - else if ((global.type).isLongInteger) - return this.module.createI64((global.constantIntegerValue).lo, (global.constantIntegerValue).hi); - else if ((global.type).isAnyInteger) - return this.module.createI32((global.constantIntegerValue).lo); - else - throw new Error("concrete type expected"); - } else + if (global.hasConstantValue) + return makeInlineConstant(global, this.module); + else return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); } @@ -2020,9 +2032,10 @@ export class Compiler extends DiagnosticEmitter { switch (target.kind) { case ElementKind.LOCAL: - element = (target).type.classType; + assert((target).type != null); + element = ((target).type).classType; if (!element) { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (target).type.toString()); + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, ((target).type).toString()); return this.module.createUnreachable(); } target = element; @@ -2033,7 +2046,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.createUnreachable(); element = ((target).type).classType; if (!element) { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, (target).type.toString()); + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, ((target).type).toString()); return this.module.createUnreachable(); } target = element; @@ -2066,7 +2079,8 @@ export class Compiler extends DiagnosticEmitter { switch (element.kind) { case ElementKind.LOCAL: - return this.module.createGetLocal((element).index, (this.currentType = (element).type).toNativeType()); + assert((element).type != null); + return this.module.createGetLocal((element).index, (this.currentType = (element).type).toNativeType()); case ElementKind.GLOBAL: if (!this.compileGlobal(element)) @@ -2074,12 +2088,9 @@ export class Compiler extends DiagnosticEmitter { assert((element).type != null); this.currentType = (element).type; if ((element).hasConstantValue) - return this.currentType== Type.f32 ? this.module.createF32((element).constantFloatValue) - : this.currentType == Type.f64 ? this.module.createF64((element).constantFloatValue) - : this.currentType.isLongInteger - ? this.module.createI64(((element).constantIntegerValue).lo, ((element).constantIntegerValue).hi) - : this.module.createI32(((element).constantIntegerValue).lo); - return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); + return makeInlineConstant(element, this.module); + else + return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); case ElementKind.PROPERTY: // getter var getterPrototype = (element).getterPrototype; @@ -2114,22 +2125,23 @@ export class Compiler extends DiagnosticEmitter { // use a temp local for the intermediate value var tempLocal = this.currentFunction.getTempLocal(this.currentType); + assert(tempLocal.type != null); var op: BinaryOp; var nativeType: NativeType; var nativeOne: ExpressionRef; - if (tempLocal.type == Type.f32) { + if (tempLocal.type == Type.f32) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddF32 : BinaryOp.SubF32; nativeType = NativeType.F32; nativeOne = this.module.createF32(1); - } else if (tempLocal.type == Type.f64) { + } else if (tempLocal.type == Type.f64) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddF64 : BinaryOp.SubF64; nativeType = NativeType.F64; nativeOne = this.module.createF64(1); - } else if (tempLocal.type.isLongInteger) { + } else if ((tempLocal.type).isLongInteger) { op = operator == Token.PLUS_PLUS ? BinaryOp.AddI64 : BinaryOp.SubI64; nativeType = NativeType.I64; nativeOne = this.module.createI64(1, 0); @@ -2150,7 +2162,7 @@ export class Compiler extends DiagnosticEmitter { // NOTE: can't preemptively tee_local the return value on the stack because binaryen expects // this to be well-formed. becomes a tee_local when optimizing, though. - this.currentType = tempLocal.type; + this.currentType = tempLocal.type; this.currentFunction.freeTempLocal(tempLocal); return this.module.createBlock(null, [ this.module.createSetLocal(tempLocal.index, getValue), // +++ this.module.createTeeLocal(tempLocal.index, getValue), @@ -2233,6 +2245,7 @@ export class Compiler extends DiagnosticEmitter { // helpers +/** Tests whether an element is a module-level export from the entry file. */ function isModuleExport(element: Element, declaration: DeclarationStatement): bool { if (!element.isExported) return false; @@ -2251,3 +2264,28 @@ function isModuleExport(element: Element, declaration: DeclarationStatement): bo return false; return isModuleExport(parent, parentNode); } + +/** Creates an inlined expression of a constant variable-like element. */ +function makeInlineConstant(element: VariableLikeElement, module: Module): ExpressionRef { + assert(element.hasConstantValue); + assert(element.type != null); + if (element.type == Type.f32) + return module.createF32((element).constantFloatValue); + else if (element.type == Type.f64) + return module.createF64((element).constantFloatValue); + else if ((element.type).isLongInteger) + return element.constantIntegerValue + ? module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) + : module.createI64(0, 0); + else if ((element.type).isSmallInteger) { + if ((element.type).isSignedInteger) { + var shift = (element.type).smallIntegerShift; + return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0); + } else + return module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & (element.type).smallIntegerMask: 0); + } else if ((element.type).isAnyInteger) + return element.constantIntegerValue + ? module.createI32(element.constantIntegerValue.lo) + : module.createI32(0); + throw new Error("concrete type expected"); +} diff --git a/src/program.ts b/src/program.ts index ce29b1d5..775bddd5 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1173,10 +1173,9 @@ export class EnumValue extends Element { } } -/** A global variable. */ -export class Global extends Element { +export class VariableLikeElement extends Element { - kind = ElementKind.GLOBAL; + // kind varies /** Declaration reference. */ declaration: VariableLikeDeclarationStatement | null; @@ -1187,6 +1186,26 @@ export class Global extends Element { /** Constant float value, if applicable. */ constantFloatValue: f64 = 0; + withConstantIntegerValue(lo: i32, hi: i32): this { + this.constantIntegerValue = new I64(lo, hi); + this.hasConstantValue = true; + this.isMutable = false; + return this; + } + + withConstantFloatValue(value: f64): this { + this.constantFloatValue = value; + this.hasConstantValue = true; + this.isMutable = false; + return this; + } +} + +/** A global variable. */ +export class Global extends VariableLikeElement { + + kind = ElementKind.GLOBAL; + constructor(program: Program, simpleName: string, internalName: string, declaration: VariableLikeDeclarationStatement | null = null, type: Type | null = null) { super(program, simpleName, internalName); if (this.declaration = declaration) { @@ -1207,20 +1226,6 @@ export class Global extends Element { } this.type = type; // resolved later if `null` } - - withConstantIntegerValue(lo: i32, hi: i32): this { - this.constantIntegerValue = new I64(lo, hi); - this.hasConstantValue = true; - this.isMutable = false; - return this; - } - - withConstantFloatValue(value: f64): this { - this.constantFloatValue = value; - this.hasConstantValue = true; - this.isMutable = false; - return this; - } } /** A function parameter. */ @@ -1244,14 +1249,12 @@ export class Parameter { } /** A function local. */ -export class Local extends Element { +export class Local extends VariableLikeElement { kind = ElementKind.LOCAL; /** Local index. */ index: i32; - /** Local type. */ - type: Type; constructor(program: Program, simpleName: string, index: i32, type: Type) { super(program, simpleName, simpleName); diff --git a/tests/compiler/inlining.optimized.wast b/tests/compiler/inlining.optimized.wast new file mode 100644 index 00000000..c4e3db29 --- /dev/null +++ b/tests/compiler/inlining.optimized.wast @@ -0,0 +1,20 @@ +(module + (type $i (func (result i32))) + (type $v (func)) + (memory $0 1) + (export "test" (func $inlining/test)) + (export "memory" (memory $0)) + (start $start) + (func $inlining/test (; 0 ;) (type $i) (result i32) + (i32.const 3) + ) + (func $start (; 1 ;) (type $v) + (if + (i32.ne + (call $inlining/test) + (i32.const 3) + ) + (unreachable) + ) + ) +) diff --git a/tests/compiler/inlining.ts b/tests/compiler/inlining.ts new file mode 100644 index 00000000..c3bf37c2 --- /dev/null +++ b/tests/compiler/inlining.ts @@ -0,0 +1,8 @@ +const constantGlobal = 1; + +export function test(): i32 { + const constantLocal = 2; + return constantGlobal + constantLocal; +} + +assert(test() == 3); diff --git a/tests/compiler/inlining.wast b/tests/compiler/inlining.wast new file mode 100644 index 00000000..7ceddccf --- /dev/null +++ b/tests/compiler/inlining.wast @@ -0,0 +1,81 @@ +(module + (type $i (func (result i32))) + (type $v (func)) + (global $inlining/constantGlobal i32 (i32.const 1)) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "test" (func $inlining/test)) + (export "memory" (memory $0)) + (start $start) + (func $inlining/test (; 0 ;) (type $i) (result i32) + (nop) + (return + (i32.add + (i32.const 1) + (i32.const 2) + ) + ) + ) + (func $start (; 1 ;) (type $v) + (if + (i32.eqz + (i32.eq + (call $inlining/test) + (i32.const 3) + ) + ) + (unreachable) + ) + ) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: parseFloat + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE + GLOBAL: inlining/constantGlobal + FUNCTION_PROTOTYPE: inlining/test +[program.exports] + FUNCTION_PROTOTYPE: inlining/test +;)