From df38498d9b4c6f4ac98c4127963acb1273ec1f52 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 30 Jun 2022 23:14:12 +0800 Subject: [PATCH 01/55] feat: add type narrow to support better type check --- src/compiler.ts | 22 +- src/conditionInfo.ts | 31 + src/flow.ts | 20 + src/program.ts | 10 + tests/compiler/typenarrow-error.json | 8 + tests/compiler/typenarrow-error.ts | 13 + tests/compiler/typenarrow.debug.wat | 2314 +++++++++++++++++++++++++ tests/compiler/typenarrow.json | 4 + tests/compiler/typenarrow.release.wat | 1539 ++++++++++++++++ tests/compiler/typenarrow.ts | 9 + 10 files changed, 3969 insertions(+), 1 deletion(-) create mode 100644 src/conditionInfo.ts create mode 100644 tests/compiler/typenarrow-error.json create mode 100644 tests/compiler/typenarrow-error.ts create mode 100644 tests/compiler/typenarrow.debug.wat create mode 100644 tests/compiler/typenarrow.json create mode 100644 tests/compiler/typenarrow.release.wat create mode 100644 tests/compiler/typenarrow.ts diff --git a/src/compiler.ts b/src/compiler.ts index 98d9a42084..e1e55efccf 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -88,7 +88,8 @@ import { PropertyPrototype, IndexSignature, File, - mangleInternalName + mangleInternalName, + TypedElement } from "./program"; import { @@ -210,6 +211,7 @@ import { import { ShadowStackPass } from "./passes/shadowstack"; +import { TypeNarrowInfo } from "./conditionInfo"; /** Compiler options. */ export class Options { @@ -2665,12 +2667,17 @@ export class Compiler extends DiagnosticEmitter { // ) └┬─────────┴─┘ // ... ┌◄┘ + this.currentFlow.startCompileCondition(); + // Precompute the condition (always executes) var condExpr = this.makeIsTrueish( this.compileExpression(statement.condition, Type.bool), this.currentType, statement.condition ); + + const conditionInfoContainer = this.currentFlow.stopCompileCondition(); + var condKind = this.evaluateCondition(condExpr); // Shortcut if the condition is constant @@ -2700,11 +2707,13 @@ export class Compiler extends DiagnosticEmitter { var thenFlow = flow.fork(); this.currentFlow = thenFlow; thenFlow.inheritNonnullIfTrue(condExpr); + conditionInfoContainer.trueInfo.forEach((info) => info.apply()); if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); } else { thenStmts.push(this.compileStatement(ifTrue)); } + conditionInfoContainer.trueInfo.forEach((info) => info.recover()); var thenTerminates = thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS); if (thenTerminates) { thenStmts.push(module.unreachable()); @@ -2718,11 +2727,13 @@ export class Compiler extends DiagnosticEmitter { let elseFlow = flow.fork(); this.currentFlow = elseFlow; elseFlow.inheritNonnullIfFalse(condExpr); + conditionInfoContainer.falseInfo.forEach((info) => info.apply()); if (ifFalse.kind == NodeKind.BLOCK) { this.compileStatements((ifFalse).statements, false, elseStmts); } else { elseStmts.push(this.compileStatement(ifFalse)); } + conditionInfoContainer.falseInfo.forEach((info) => info.recover()); let elseTerminates = elseFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS); if (elseTerminates) { elseStmts.push(module.unreachable()); @@ -5846,6 +5857,7 @@ export class Compiler extends DiagnosticEmitter { var flow = this.currentFlow; var target = resolver.lookupExpression(expression, flow); // reports if (!target) return this.module.unreachable(); + if (target instanceof TypedElement) (target).recoverType(); var thisExpression = resolver.currentThisExpression; var elementExpression = resolver.currentElementExpression; @@ -7903,6 +7915,14 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.bool; return this.module.unreachable(); } + if (expression.expression.kind == NodeKind.IDENTIFIER) { + const identifier = expression.expression; + const element = flow.lookup(identifier.text); + if (element && element instanceof TypedElement) { + const typedElement = element; + flow.addConditionInfo(new TypeNarrowInfo(typedElement, expectedType)); + } + } return this.makeInstanceofType(expression, expectedType); } diff --git a/src/conditionInfo.ts b/src/conditionInfo.ts new file mode 100644 index 0000000000..a5d1cfb2b0 --- /dev/null +++ b/src/conditionInfo.ts @@ -0,0 +1,31 @@ +import { TypedElement } from "./program"; +import { Type } from "./types"; + +export class ConditionInfoContainer { + trueInfo: ConditionInfo[] = []; + falseInfo: ConditionInfo[] = []; + + switchTrueAndFalse(): void { + let tmp = this.trueInfo; + this.trueInfo = this.falseInfo; + this.falseInfo = tmp; + } +} + +export abstract class ConditionInfo { + abstract apply(): void; + abstract recover(): void; +} + +export class TypeNarrowInfo extends ConditionInfo { + constructor(public typedElement: TypedElement, public narrowedType: Type) { + super(); + } + + apply(): void { + this.typedElement.narrowType(this.narrowedType); + } + recover(): void { + this.typedElement.recoverType(); + } +} diff --git a/src/flow.ts b/src/flow.ts index 8e37112692..76e6c55571 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -90,6 +90,7 @@ import { import { BuiltinNames } from "./builtins"; +import { ConditionInfo, ConditionInfoContainer } from "./conditionInfo"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -1221,6 +1222,25 @@ export class Flow { } } + conditionInfoStack: ConditionInfoContainer[] = []; + startCompileCondition(): void { + this.conditionInfoStack.push(new ConditionInfoContainer()); + } + stopCompileCondition(): ConditionInfoContainer { + return assert(this.conditionInfoStack.pop()); + } + addConditionInfo(conditionInfo: ConditionInfo, branch: bool = true): void { + if (this.conditionInfoStack.length > 0) { + const infoContainer = + this.conditionInfoStack[this.conditionInfoStack.length - 1]; + if (branch) { + infoContainer.trueInfo.push(conditionInfo); + } else { + infoContainer.falseInfo.push(conditionInfo); + } + } + } + /** * 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 diff --git a/src/program.ts b/src/program.ts index 2958241478..b2c4eccdde 100644 --- a/src/program.ts +++ b/src/program.ts @@ -2992,6 +2992,7 @@ export abstract class TypedElement extends DeclaredElement { /** Resolved type. Set once `is(RESOLVED)`, otherwise void. */ type: Type = Type.void; + typeStack: Type[] = []; constructor( /** Specific element kind. */ @@ -3017,6 +3018,15 @@ export abstract class TypedElement extends DeclaredElement { this.type = type; this.set(CommonFlags.RESOLVED); } + narrowType(type: Type): void { + if (type.isClass) { + this.typeStack.push(this.type); + this.type = type; + } + } + recoverType(): void { + this.type = this.typeStack.length > 0 ? this.typeStack.pop() : this.type; + } } /** A file representing the implicit top-level namespace of a source. */ diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json new file mode 100644 index 0000000000..e6b94483bc --- /dev/null +++ b/tests/compiler/typenarrow-error.json @@ -0,0 +1,8 @@ +{ + "asc_flags": [ + ], + "stderr": [ + "TS2339: Property 'foo' does not exist on type 'typenarrow-error/A", + "EOF" + ] +} diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts new file mode 100644 index 0000000000..f52fc5ab54 --- /dev/null +++ b/tests/compiler/typenarrow-error.ts @@ -0,0 +1,13 @@ +class A {} +class B extends A { + foo(): void {} +} + +let t = new A(); +if (t instanceof B) { + t.foo(); + t = new A(); + t.foo(); +} + +ERROR("EOF"); \ No newline at end of file diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat new file mode 100644 index 0000000000..e40b1be409 --- /dev/null +++ b/tests/compiler/typenarrow.debug.wat @@ -0,0 +1,2314 @@ +(module + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_=>_none (func (param i32))) + (type $none_=>_none (func)) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $none_=>_i32 (func (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) + (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) + (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) + (global $typenarrow/t (mut i32) (i32.const 0)) + (global $~lib/rt/__rtti_base i32 (i32.const 416)) + (global $~lib/memory/__data_end i32 (i32.const 460)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16844)) + (global $~lib/memory/__heap_base i32 (i32.const 16844)) + (memory $0 1) + (data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") + (data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 144) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 176) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 204) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00") + (data (i32.const 268) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") + (data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 416) "\05\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00") + (table $0 1 1 funcref) + (elem $0 (i32.const 1)) + (export "memory" (memory $0)) + (start $~start) + (func $~lib/rt/itcms/Object#set:nextWithColor (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=4 + ) + (func $~lib/rt/itcms/Object#set:prev (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=8 + ) + (func $~lib/rt/itcms/initLazy (param $0 i32) (result i32) + local.get $0 + local.get $0 + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $0 + local.get $0 + call $~lib/rt/itcms/Object#set:prev + local.get $0 + ) + (func $~lib/rt/itcms/Object#get:next (param $0 i32) (result i32) + local.get $0 + i32.load offset=4 + i32.const 3 + i32.const -1 + i32.xor + i32.and + ) + (func $~lib/rt/itcms/Object#get:color (param $0 i32) (result i32) + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + ) + (func $~lib/rt/itcms/visitRoots (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + call $~lib/rt/__visit_globals + global.get $~lib/rt/itcms/pinSpace + local.set $1 + local.get $1 + call $~lib/rt/itcms/Object#get:next + local.set $2 + loop $while-continue|0 + local.get $2 + local.get $1 + i32.ne + local.set $3 + local.get $3 + if + i32.const 1 + drop + local.get $2 + call $~lib/rt/itcms/Object#get:color + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 159 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 20 + i32.add + local.get $0 + call $~lib/rt/__visit_members + local.get $2 + call $~lib/rt/itcms/Object#get:next + local.set $2 + br $while-continue|0 + end + end + ) + (func $~lib/rt/itcms/Object#set:color (param $0 i32) (param $1 i32) + local.get $0 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $1 + i32.or + call $~lib/rt/itcms/Object#set:nextWithColor + ) + (func $~lib/rt/itcms/Object#set:next (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + call $~lib/rt/itcms/Object#set:nextWithColor + ) + (func $~lib/rt/itcms/Object#unlink (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + call $~lib/rt/itcms/Object#get:next + local.set $1 + local.get $1 + i32.const 0 + i32.eq + if + i32.const 1 + drop + local.get $0 + i32.load offset=8 + i32.const 0 + i32.eq + if (result i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 127 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + return + end + local.get $0 + i32.load offset=8 + local.set $2 + i32.const 1 + drop + local.get $2 + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 131 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.get $2 + call $~lib/rt/itcms/Object#set:prev + local.get $2 + local.get $1 + call $~lib/rt/itcms/Object#set:next + ) + (func $~lib/rt/__typeinfo (param $0 i32) (result i32) + (local $1 i32) + global.get $~lib/rt/__rtti_base + local.set $1 + local.get $0 + local.get $1 + i32.load + i32.gt_u + if + i32.const 224 + i32.const 288 + i32.const 22 + i32.const 28 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + local.get $0 + i32.const 8 + i32.mul + i32.add + i32.load + ) + (func $~lib/rt/itcms/Object#get:isPointerfree (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.load offset=12 + local.set $1 + local.get $1 + i32.const 1 + i32.le_u + if (result i32) + i32.const 1 + else + local.get $1 + call $~lib/rt/__typeinfo + i32.const 32 + i32.and + i32.const 0 + i32.ne + end + ) + (func $~lib/rt/itcms/Object#linkTo (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + local.get $1 + i32.load offset=8 + local.set $3 + local.get $0 + local.get $1 + local.get $2 + i32.or + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $0 + local.get $3 + call $~lib/rt/itcms/Object#set:prev + local.get $3 + local.get $0 + call $~lib/rt/itcms/Object#set:next + local.get $1 + local.get $0 + call $~lib/rt/itcms/Object#set:prev + ) + (func $~lib/rt/itcms/Object#makeGray (param $0 i32) + (local $1 i32) + local.get $0 + global.get $~lib/rt/itcms/iter + i32.eq + if + local.get $0 + i32.load offset=8 + local.tee $1 + i32.eqz + if (result i32) + i32.const 0 + i32.const 96 + i32.const 147 + i32.const 30 + call $~lib/builtins/abort + unreachable + else + local.get $1 + end + global.set $~lib/rt/itcms/iter + end + local.get $0 + call $~lib/rt/itcms/Object#unlink + local.get $0 + global.get $~lib/rt/itcms/toSpace + local.get $0 + call $~lib/rt/itcms/Object#get:isPointerfree + if (result i32) + global.get $~lib/rt/itcms/white + i32.eqz + else + i32.const 2 + end + call $~lib/rt/itcms/Object#linkTo + ) + (func $~lib/rt/itcms/__visit (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $0 + i32.eqz + if + return + end + local.get $0 + i32.const 20 + i32.sub + local.set $2 + i32.const 0 + drop + local.get $2 + call $~lib/rt/itcms/Object#get:color + global.get $~lib/rt/itcms/white + i32.eq + if + local.get $2 + call $~lib/rt/itcms/Object#makeGray + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.add + global.set $~lib/rt/itcms/visitCount + end + ) + (func $~lib/rt/itcms/visitStack (param $0 i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + local.set $1 + loop $while-continue|0 + local.get $1 + global.get $~lib/memory/__heap_base + i32.lt_u + local.set $2 + local.get $2 + if + local.get $1 + i32.load + local.get $0 + call $~lib/rt/itcms/__visit + local.get $1 + i32.const 4 + i32.add + local.set $1 + br $while-continue|0 + end + end + ) + (func $~lib/rt/itcms/Object#get:size (param $0 i32) (result i32) + i32.const 4 + local.get $0 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + ) + (func $~lib/rt/tlsf/Root#set:flMap (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store + ) + (func $~lib/rt/common/BLOCK#set:mmInfo (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store + ) + (func $~lib/rt/tlsf/Block#set:prev (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=4 + ) + (func $~lib/rt/tlsf/Block#set:next (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=8 + ) + (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + local.get $1 + i32.load + local.set $2 + i32.const 1 + drop + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 268 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $3 + i32.const 1 + drop + local.get $3 + i32.const 12 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 270 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $4 + local.get $3 + i32.const 4 + i32.shr_u + local.set $5 + else + local.get $3 + local.tee $6 + i32.const 1073741820 + local.tee $7 + local.get $6 + local.get $7 + i32.lt_u + select + local.set $6 + i32.const 31 + local.get $6 + i32.clz + i32.sub + local.set $4 + local.get $6 + local.get $4 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $5 + local.get $4 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $4 + end + i32.const 1 + drop + local.get $4 + i32.const 23 + i32.lt_u + if (result i32) + local.get $5 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 284 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load offset=4 + local.set $8 + local.get $1 + i32.load offset=8 + local.set $9 + local.get $8 + if + local.get $8 + local.get $9 + call $~lib/rt/tlsf/Block#set:next + end + local.get $9 + if + local.get $9 + local.get $8 + call $~lib/rt/tlsf/Block#set:prev + end + local.get $1 + local.get $0 + local.set $10 + local.get $4 + local.set $6 + local.get $5 + local.set $7 + local.get $10 + local.get $6 + i32.const 4 + i32.shl + local.get $7 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + i32.eq + if + local.get $0 + local.set $11 + local.get $4 + local.set $10 + local.get $5 + local.set $6 + local.get $9 + local.set $7 + local.get $11 + local.get $10 + i32.const 4 + i32.shl + local.get $6 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $7 + i32.store offset=96 + local.get $9 + i32.eqz + if + local.get $0 + local.set $6 + local.get $4 + local.set $7 + local.get $6 + local.get $7 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.set $6 + local.get $0 + local.set $7 + local.get $4 + local.set $11 + local.get $6 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $6 + local.set $10 + local.get $7 + local.get $11 + i32.const 2 + i32.shl + i32.add + local.get $10 + i32.store offset=4 + local.get $6 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + call $~lib/rt/tlsf/Root#set:flMap + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + (local $12 i32) + (local $13 i32) + i32.const 1 + drop + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 201 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.set $2 + i32.const 1 + drop + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 203 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.set $3 + local.get $3 + i32.const 4 + i32.add + local.get $3 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $4 + local.get $4 + i32.load + local.set $5 + local.get $5 + i32.const 1 + i32.and + if + local.get $0 + local.get $4 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $2 + i32.const 4 + i32.add + local.get $5 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.tee $2 + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $1 + local.set $3 + local.get $3 + i32.const 4 + i32.add + local.get $3 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $4 + local.get $4 + i32.load + local.set $5 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $1 + local.set $3 + local.get $3 + i32.const 4 + i32.sub + i32.load + local.set $3 + local.get $3 + i32.load + local.set $6 + i32.const 1 + drop + local.get $6 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 221 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $3 + call $~lib/rt/tlsf/removeBlock + local.get $3 + local.set $1 + local.get $1 + local.get $6 + i32.const 4 + i32.add + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.tee $2 + call $~lib/rt/common/BLOCK#set:mmInfo + end + local.get $4 + local.get $5 + i32.const 2 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $7 + i32.const 1 + drop + local.get $7 + i32.const 12 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 233 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + drop + local.get $1 + i32.const 4 + i32.add + local.get $7 + i32.add + local.get $4 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 234 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $7 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $8 + local.get $7 + i32.const 4 + i32.shr_u + local.set $9 + else + local.get $7 + local.tee $3 + i32.const 1073741820 + local.tee $6 + local.get $3 + local.get $6 + i32.lt_u + select + local.set $3 + i32.const 31 + local.get $3 + i32.clz + i32.sub + local.set $8 + local.get $3 + local.get $8 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $9 + local.get $8 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $8 + end + i32.const 1 + drop + local.get $8 + i32.const 23 + i32.lt_u + if (result i32) + local.get $9 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 251 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $10 + local.get $8 + local.set $3 + local.get $9 + local.set $6 + local.get $10 + local.get $3 + i32.const 4 + i32.shl + local.get $6 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $11 + local.get $1 + i32.const 0 + call $~lib/rt/tlsf/Block#set:prev + local.get $1 + local.get $11 + call $~lib/rt/tlsf/Block#set:next + local.get $11 + if + local.get $11 + local.get $1 + call $~lib/rt/tlsf/Block#set:prev + end + local.get $0 + local.set $12 + local.get $8 + local.set $10 + local.get $9 + local.set $3 + local.get $1 + local.set $6 + local.get $12 + local.get $10 + i32.const 4 + i32.shl + local.get $3 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=96 + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $8 + i32.shl + i32.or + call $~lib/rt/tlsf/Root#set:flMap + local.get $0 + local.set $13 + local.get $8 + local.set $12 + local.get $0 + local.set $3 + local.get $8 + local.set $6 + local.get $3 + local.get $6 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const 1 + local.get $9 + i32.shl + i32.or + local.set $10 + local.get $13 + local.get $12 + i32.const 2 + i32.shl + i32.add + local.get $10 + i32.store offset=4 + ) + (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + i32.const 1 + drop + local.get $1 + local.get $2 + i32.le_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 377 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + local.set $1 + local.get $2 + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $2 + local.get $0 + local.set $3 + local.get $3 + i32.load offset=1568 + local.set $4 + i32.const 0 + local.set $5 + local.get $4 + if + i32.const 1 + drop + local.get $1 + local.get $4 + i32.const 4 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 384 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 16 + i32.sub + local.get $4 + i32.eq + if + local.get $1 + i32.const 16 + i32.sub + local.set $1 + local.get $4 + i32.load + local.set $5 + else + nop + end + else + i32.const 1 + drop + local.get $1 + local.get $0 + i32.const 1572 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 397 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + local.get $1 + i32.sub + local.set $6 + local.get $6 + i32.const 4 + i32.const 12 + i32.add + i32.const 4 + i32.add + i32.lt_u + if + i32.const 0 + return + end + local.get $6 + i32.const 2 + i32.const 4 + i32.mul + i32.sub + local.set $7 + local.get $1 + local.set $8 + local.get $8 + local.get $7 + i32.const 1 + i32.or + local.get $5 + i32.const 2 + i32.and + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $8 + i32.const 0 + call $~lib/rt/tlsf/Block#set:prev + local.get $8 + i32.const 0 + call $~lib/rt/tlsf/Block#set:next + local.get $1 + i32.const 4 + i32.add + local.get $7 + i32.add + local.set $4 + local.get $4 + i32.const 0 + i32.const 2 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $0 + local.set $9 + local.get $4 + local.set $3 + local.get $9 + local.get $3 + i32.store offset=1568 + local.get $0 + local.get $8 + call $~lib/rt/tlsf/insertBlock + i32.const 1 + ) + (func $~lib/rt/tlsf/initialize + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + (local $12 i32) + i32.const 0 + drop + global.get $~lib/memory/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $0 + memory.size + local.set $1 + local.get $0 + i32.const 1572 + i32.add + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $2 + local.get $2 + local.get $1 + i32.gt_s + if (result i32) + local.get $2 + local.get $1 + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + local.get $0 + local.set $3 + local.get $3 + i32.const 0 + call $~lib/rt/tlsf/Root#set:flMap + local.get $3 + local.set $5 + i32.const 0 + local.set $4 + local.get $5 + local.get $4 + i32.store offset=1568 + i32.const 0 + local.set $5 + loop $for-loop|0 + local.get $5 + i32.const 23 + i32.lt_u + local.set $4 + local.get $4 + if + local.get $3 + local.set $8 + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + local.get $8 + local.get $7 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=4 + i32.const 0 + local.set $8 + loop $for-loop|1 + local.get $8 + i32.const 16 + i32.lt_u + local.set $7 + local.get $7 + if + local.get $3 + local.set $11 + local.get $5 + local.set $10 + local.get $8 + local.set $9 + i32.const 0 + local.set $6 + local.get $11 + local.get $10 + i32.const 4 + i32.shl + local.get $9 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=96 + local.get $8 + i32.const 1 + i32.add + local.set $8 + br $for-loop|1 + end + end + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $for-loop|0 + end + end + local.get $0 + i32.const 1572 + i32.add + local.set $12 + i32.const 0 + drop + local.get $3 + local.get $12 + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + drop + local.get $3 + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.const 4 + i32.sub + local.set $1 + local.get $0 + i32.const 0 + i32.ne + if (result i32) + local.get $0 + i32.const 15 + i32.and + i32.eqz + else + i32.const 0 + end + if (result i32) + local.get $1 + i32.load + i32.const 1 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 559 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $1 + ) + (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) + i32.const 0 + drop + local.get $1 + local.get $1 + i32.load + i32.const 1 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/__free (param $0 i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.lt_u + if + return + end + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/checkUsedBlock + call $~lib/rt/tlsf/freeBlock + ) + (func $~lib/rt/itcms/free (param $0 i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.lt_u + if + local.get $0 + i32.const 0 + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $0 + i32.const 0 + call $~lib/rt/itcms/Object#set:prev + else + global.get $~lib/rt/itcms/total + local.get $0 + call $~lib/rt/itcms/Object#get:size + i32.sub + global.set $~lib/rt/itcms/total + i32.const 0 + drop + local.get $0 + i32.const 4 + i32.add + call $~lib/rt/tlsf/__free + end + ) + (func $~lib/rt/itcms/step (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + block $break|0 + block $case2|0 + block $case1|0 + block $case0|0 + global.get $~lib/rt/itcms/state + local.set $1 + local.get $1 + i32.const 0 + i32.eq + br_if $case0|0 + local.get $1 + i32.const 1 + i32.eq + br_if $case1|0 + local.get $1 + i32.const 2 + i32.eq + br_if $case2|0 + br $break|0 + end + i32.const 1 + global.set $~lib/rt/itcms/state + i32.const 0 + global.set $~lib/rt/itcms/visitCount + i32.const 0 + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + global.get $~lib/rt/itcms/white + i32.eqz + local.set $1 + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $0 + loop $while-continue|1 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + local.set $2 + local.get $2 + if + local.get $0 + global.set $~lib/rt/itcms/iter + local.get $0 + call $~lib/rt/itcms/Object#get:color + local.get $1 + i32.ne + if + local.get $0 + local.get $1 + call $~lib/rt/itcms/Object#set:color + i32.const 0 + global.set $~lib/rt/itcms/visitCount + local.get $0 + i32.const 20 + i32.add + i32.const 0 + call $~lib/rt/__visit_members + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + local.get $0 + call $~lib/rt/itcms/Object#get:next + local.set $0 + br $while-continue|1 + end + end + i32.const 0 + global.set $~lib/rt/itcms/visitCount + i32.const 0 + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $0 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.eq + if + i32.const 0 + call $~lib/rt/itcms/visitStack + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $0 + loop $while-continue|2 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + local.set $2 + local.get $2 + if + local.get $0 + call $~lib/rt/itcms/Object#get:color + local.get $1 + i32.ne + if + local.get $0 + local.get $1 + call $~lib/rt/itcms/Object#set:color + local.get $0 + i32.const 20 + i32.add + i32.const 0 + call $~lib/rt/__visit_members + end + local.get $0 + call $~lib/rt/itcms/Object#get:next + local.set $0 + br $while-continue|2 + end + end + global.get $~lib/rt/itcms/fromSpace + local.set $2 + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/fromSpace + local.get $2 + global.set $~lib/rt/itcms/toSpace + local.get $1 + global.set $~lib/rt/itcms/white + local.get $2 + call $~lib/rt/itcms/Object#get:next + global.set $~lib/rt/itcms/iter + i32.const 2 + global.set $~lib/rt/itcms/state + end + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + global.get $~lib/rt/itcms/iter + local.set $0 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $0 + call $~lib/rt/itcms/Object#get:next + global.set $~lib/rt/itcms/iter + i32.const 1 + drop + local.get $0 + call $~lib/rt/itcms/Object#get:color + global.get $~lib/rt/itcms/white + i32.eqz + i32.eq + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 228 + i32.const 20 + call $~lib/builtins/abort + unreachable + end + local.get $0 + call $~lib/rt/itcms/free + i32.const 10 + return + end + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + call $~lib/rt/itcms/Object#set:nextWithColor + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + call $~lib/rt/itcms/Object#set:prev + i32.const 0 + global.set $~lib/rt/itcms/state + br $break|0 + end + i32.const 0 + ) + (func $~lib/rt/itcms/interrupt + (local $0 i32) + i32.const 0 + drop + i32.const 0 + drop + i32.const 1024 + i32.const 200 + i32.mul + i32.const 100 + i32.div_u + local.set $0 + loop $do-loop|0 + local.get $0 + call $~lib/rt/itcms/step + i32.sub + local.set $0 + global.get $~lib/rt/itcms/state + i32.const 0 + i32.eq + if + i32.const 0 + drop + global.get $~lib/rt/itcms/total + i64.extend_i32_u + i32.const 200 + i64.extend_i32_u + i64.mul + i64.const 100 + i64.div_u + i32.wrap_i64 + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + i32.const 0 + drop + return + end + local.get $0 + i32.const 0 + i32.gt_s + br_if $do-loop|0 + end + i32.const 0 + drop + global.get $~lib/rt/itcms/total + i32.const 1024 + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.sub + i32.const 1024 + i32.lt_u + i32.mul + i32.add + global.set $~lib/rt/itcms/threshold + i32.const 0 + drop + ) + (func $~lib/rt/tlsf/computeSize (param $0 i32) (result i32) + local.get $0 + i32.const 12 + i32.le_u + if (result i32) + i32.const 12 + else + local.get $0 + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + end + ) + (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) + local.get $0 + i32.const 1073741820 + i32.gt_u + if + i32.const 32 + i32.const 368 + i32.const 458 + i32.const 29 + call $~lib/builtins/abort + unreachable + end + local.get $0 + call $~lib/rt/tlsf/computeSize + ) + (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + local.get $1 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $2 + local.get $1 + i32.const 4 + i32.shr_u + local.set $3 + else + local.get $1 + i32.const 536870910 + i32.lt_u + if (result i32) + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + else + local.get $1 + end + local.set $4 + i32.const 31 + local.get $4 + i32.clz + i32.sub + local.set $2 + local.get $4 + local.get $2 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $3 + local.get $2 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $2 + end + i32.const 1 + drop + local.get $2 + i32.const 23 + i32.lt_u + if (result i32) + local.get $3 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 330 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $5 + local.get $2 + local.set $4 + local.get $5 + local.get $4 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const 0 + i32.const -1 + i32.xor + local.get $3 + i32.shl + i32.and + local.set $6 + i32.const 0 + local.set $7 + local.get $6 + i32.eqz + if + local.get $0 + i32.load + i32.const 0 + i32.const -1 + i32.xor + local.get $2 + i32.const 1 + i32.add + i32.shl + i32.and + local.set $5 + local.get $5 + i32.eqz + if + i32.const 0 + local.set $7 + else + local.get $5 + i32.ctz + local.set $2 + local.get $0 + local.set $8 + local.get $2 + local.set $4 + local.get $8 + local.get $4 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.set $6 + i32.const 1 + drop + local.get $6 + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 343 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $9 + local.get $2 + local.set $8 + local.get $6 + i32.ctz + local.set $4 + local.get $9 + local.get $8 + i32.const 4 + i32.shl + local.get $4 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $7 + end + else + local.get $0 + local.set $9 + local.get $2 + local.set $8 + local.get $6 + i32.ctz + local.set $4 + local.get $9 + local.get $8 + i32.const 4 + i32.shl + local.get $4 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $7 + end + local.get $7 + ) + (func $~lib/rt/tlsf/growMemory (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + i32.const 0 + drop + local.get $1 + i32.const 536870910 + i32.lt_u + if + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.const 1 + i32.sub + i32.add + local.set $1 + end + memory.size + local.set $2 + local.get $1 + i32.const 4 + local.get $2 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + local.get $0 + local.set $3 + local.get $3 + i32.load offset=1568 + i32.ne + i32.shl + i32.add + local.set $1 + local.get $1 + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $4 + local.get $2 + local.tee $3 + local.get $4 + local.tee $5 + local.get $3 + local.get $5 + i32.gt_s + select + local.set $6 + local.get $6 + memory.grow + i32.const 0 + i32.lt_s + if + local.get $4 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + memory.size + local.set $7 + local.get $0 + local.get $2 + i32.const 16 + i32.shl + local.get $7 + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + drop + ) + (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.set $3 + i32.const 1 + drop + local.get $2 + i32.const 4 + i32.add + i32.const 15 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 357 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.sub + local.set $4 + local.get $4 + i32.const 4 + i32.const 12 + i32.add + i32.ge_u + if + local.get $1 + local.get $2 + local.get $3 + i32.const 2 + i32.and + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $1 + i32.const 4 + i32.add + local.get $2 + i32.add + local.set $5 + local.get $5 + local.get $4 + i32.const 4 + i32.sub + i32.const 1 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $0 + local.get $5 + call $~lib/rt/tlsf/insertBlock + else + local.get $1 + local.get $3 + i32.const 1 + i32.const -1 + i32.xor + i32.and + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $1 + local.set $5 + local.get $5 + i32.const 4 + i32.add + local.get $5 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.get $1 + local.set $5 + local.get $5 + i32.const 4 + i32.add + local.get $5 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + i32.load + i32.const 2 + i32.const -1 + i32.xor + i32.and + call $~lib/rt/common/BLOCK#set:mmInfo + end + ) + (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $1 + call $~lib/rt/tlsf/prepareSize + local.set $2 + local.get $0 + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.set $3 + local.get $3 + i32.eqz + if + local.get $0 + local.get $2 + call $~lib/rt/tlsf/growMemory + local.get $0 + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.set $3 + i32.const 1 + drop + local.get $3 + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 496 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + i32.const 1 + drop + local.get $3 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 368 + i32.const 498 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $3 + call $~lib/rt/tlsf/removeBlock + local.get $0 + local.get $3 + local.get $2 + call $~lib/rt/tlsf/prepareBlock + i32.const 0 + drop + local.get $3 + ) + (func $~lib/rt/tlsf/__alloc (param $0 i32) (result i32) + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/allocateBlock + i32.const 4 + i32.add + ) + (func $~lib/rt/itcms/Object#set:rtId (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=12 + ) + (func $~lib/rt/itcms/Object#set:rtSize (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=16 + ) + (func $~lib/rt/itcms/__new (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.const 1073741804 + i32.ge_u + if + i32.const 32 + i32.const 96 + i32.const 260 + i32.const 31 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.ge_u + if + call $~lib/rt/itcms/interrupt + end + i32.const 16 + local.get $0 + i32.add + call $~lib/rt/tlsf/__alloc + i32.const 4 + i32.sub + local.set $2 + local.get $2 + local.get $1 + call $~lib/rt/itcms/Object#set:rtId + local.get $2 + local.get $0 + call $~lib/rt/itcms/Object#set:rtSize + local.get $2 + global.get $~lib/rt/itcms/fromSpace + global.get $~lib/rt/itcms/white + call $~lib/rt/itcms/Object#linkTo + global.get $~lib/rt/itcms/total + local.get $2 + call $~lib/rt/itcms/Object#get:size + i32.add + global.set $~lib/rt/itcms/total + local.get $2 + i32.const 20 + i32.add + local.set $3 + local.get $3 + i32.const 0 + local.get $0 + memory.fill + local.get $3 + ) + (func $~lib/rt/__instanceof (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.set $2 + global.get $~lib/rt/__rtti_base + local.set $3 + local.get $2 + local.get $3 + i32.load + i32.le_u + if + loop $do-loop|0 + local.get $2 + local.get $1 + i32.eq + if + i32.const 1 + return + end + local.get $3 + i32.const 4 + i32.add + local.get $2 + i32.const 8 + i32.mul + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|0 + end + end + i32.const 0 + ) + (func $typenarrow/B#foo (param $0 i32) + nop + ) + (func $~lib/rt/__visit_globals (param $0 i32) + (local $1 i32) + global.get $typenarrow/t + local.tee $1 + if + local.get $1 + local.get $0 + call $~lib/rt/itcms/__visit + end + i32.const 224 + local.get $0 + call $~lib/rt/itcms/__visit + i32.const 32 + local.get $0 + call $~lib/rt/itcms/__visit + ) + (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $0 + i32.load + local.tee $2 + if + local.get $2 + local.get $1 + call $~lib/rt/itcms/__visit + end + ) + (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) + block $invalid + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid + end + return + end + return + end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit + return + end + return + end + return + end + unreachable + ) + (func $~start + call $start:typenarrow + ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 16864 + i32.const 16912 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $start:typenarrow + (local $0 i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + memory.size + i32.const 16 + i32.shl + global.get $~lib/memory/__heap_base + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 144 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/pinSpace + i32.const 176 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/toSpace + i32.const 320 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/fromSpace + i32.const 0 + call $typenarrow/A#constructor + global.set $typenarrow/t + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if + global.get $typenarrow/t + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#foo + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $typenarrow/A#constructor (param $0 i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $0 + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 3 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + end + local.get $0 + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) +) diff --git a/tests/compiler/typenarrow.json b/tests/compiler/typenarrow.json new file mode 100644 index 0000000000..1bdd02b1be --- /dev/null +++ b/tests/compiler/typenarrow.json @@ -0,0 +1,4 @@ +{ + "asc_flags": [ + ] +} diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat new file mode 100644 index 0000000000..e7f6fccb8b --- /dev/null +++ b/tests/compiler/typenarrow.release.wat @@ -0,0 +1,1539 @@ +(module + (type $none_=>_none (func)) + (type $i32_i32_=>_none (func (param i32 i32))) + (type $none_=>_i32 (func (result i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $typenarrow/t (mut i32) (i32.const 0)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17868)) + (memory $0 1) + (data (i32.const 1036) "<") + (data (i32.const 1048) "\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") + (data (i32.const 1100) "<") + (data (i32.const 1112) "\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s") + (data (i32.const 1228) "<") + (data (i32.const 1240) "\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") + (data (i32.const 1292) ",") + (data (i32.const 1304) "\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") + (data (i32.const 1372) "<") + (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") + (data (i32.const 1440) "\05\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1468) " \00\00\00\00\00\00\00 \00\00\00\03") + (export "memory" (memory $0)) + (start $~start) + (func $~lib/rt/itcms/visitRoots + (local $0 i32) + (local $1 i32) + global.get $typenarrow/t + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + i32.const 1248 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + i32.const 1056 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + global.get $~lib/rt/itcms/pinSpace + local.tee $1 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|0 + local.get $0 + local.get $1 + i32.ne + if + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1120 + i32.const 159 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|0 + end + end + ) + (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.tee $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 268 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const -4 + i32.and + local.tee $2 + i32.const 12 + i32.lt_u + if + i32.const 0 + i32.const 1392 + i32.const 270 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 256 + i32.lt_u + if (result i32) + local.get $2 + i32.const 4 + i32.shr_u + else + i32.const 31 + local.get $2 + i32.const 1073741820 + local.get $2 + i32.const 1073741820 + i32.lt_u + select + local.tee $2 + i32.clz + i32.sub + local.tee $4 + i32.const 7 + i32.sub + local.set $3 + local.get $2 + local.get $4 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + end + local.tee $2 + i32.const 16 + i32.lt_u + local.get $3 + i32.const 23 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 284 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load offset=8 + local.set $5 + local.get $1 + i32.load offset=4 + local.tee $4 + if + local.get $4 + local.get $5 + i32.store offset=8 + end + local.get $5 + if + local.get $5 + local.get $4 + i32.store offset=4 + end + local.get $1 + local.get $0 + local.get $3 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + i32.eq + if + local.get $0 + local.get $3 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $5 + i32.store offset=96 + local.get $5 + i32.eqz + if + local.get $0 + local.get $3 + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load offset=4 + i32.const -2 + local.get $2 + i32.rotl + i32.and + local.set $2 + local.get $1 + local.get $2 + i32.store offset=4 + local.get $2 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const -2 + local.get $3 + i32.rotl + i32.and + i32.store + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 201 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.tee $3 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 203 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $4 + i32.load + local.tee $2 + i32.const 1 + i32.and + if + local.get $0 + local.get $4 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $3 + i32.const 4 + i32.add + local.get $2 + i32.const -4 + i32.and + i32.add + local.tee $3 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $4 + i32.load + local.set $2 + end + local.get $3 + i32.const 2 + i32.and + if + local.get $1 + i32.const 4 + i32.sub + i32.load + local.tee $1 + i32.load + local.tee $6 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 221 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $6 + i32.const 4 + i32.add + local.get $3 + i32.const -4 + i32.and + i32.add + local.tee $3 + i32.store + end + local.get $4 + local.get $2 + i32.const 2 + i32.or + i32.store + local.get $3 + i32.const -4 + i32.and + local.tee $2 + i32.const 12 + i32.lt_u + if + i32.const 0 + i32.const 1392 + i32.const 233 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + local.get $1 + i32.const 4 + i32.add + local.get $2 + i32.add + i32.ne + if + i32.const 0 + i32.const 1392 + i32.const 234 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $2 + i32.const 256 + i32.lt_u + if (result i32) + local.get $2 + i32.const 4 + i32.shr_u + else + i32.const 31 + local.get $2 + i32.const 1073741820 + local.get $2 + i32.const 1073741820 + i32.lt_u + select + local.tee $2 + i32.clz + i32.sub + local.tee $3 + i32.const 7 + i32.sub + local.set $5 + local.get $2 + local.get $3 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + end + local.tee $2 + i32.const 16 + i32.lt_u + local.get $5 + i32.const 23 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 251 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $5 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $3 + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + local.get $3 + i32.store offset=8 + local.get $3 + if + local.get $3 + local.get $1 + i32.store offset=4 + end + local.get $0 + local.get $5 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store offset=96 + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $5 + i32.shl + i32.or + i32.store + local.get $0 + local.get $5 + i32.const 2 + i32.shl + i32.add + local.tee $0 + local.get $0 + i32.load offset=4 + i32.const 1 + local.get $2 + i32.shl + i32.or + i32.store offset=4 + ) + (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + local.get $1 + local.get $2 + i32.gt_u + if + i32.const 0 + i32.const 1392 + i32.const 377 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 19 + i32.add + i32.const -16 + i32.and + i32.const 4 + i32.sub + local.set $1 + local.get $0 + i32.load offset=1568 + local.tee $4 + if + local.get $4 + i32.const 4 + i32.add + local.get $1 + i32.gt_u + if + i32.const 0 + i32.const 1392 + i32.const 384 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 16 + i32.sub + local.get $4 + i32.eq + if + local.get $4 + i32.load + local.set $3 + local.get $1 + i32.const 16 + i32.sub + local.set $1 + end + else + local.get $0 + i32.const 1572 + i32.add + local.get $1 + i32.gt_u + if + i32.const 0 + i32.const 1392 + i32.const 397 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + i32.const -16 + i32.and + local.get $1 + i32.sub + local.tee $2 + i32.const 20 + i32.lt_u + if + return + end + local.get $1 + local.get $3 + i32.const 2 + i32.and + local.get $2 + i32.const 8 + i32.sub + local.tee $2 + i32.const 1 + i32.or + i32.or + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 4 + i32.add + local.get $2 + i32.add + local.tee $2 + i32.const 2 + i32.store + local.get $0 + local.get $2 + i32.store offset=1568 + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/initialize + (local $0 i32) + (local $1 i32) + memory.size + local.tee $1 + i32.const 0 + i32.le_s + if (result i32) + i32.const 1 + local.get $1 + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + i32.const 17872 + i32.const 0 + i32.store + i32.const 19440 + i32.const 0 + i32.store + loop $for-loop|0 + local.get $0 + i32.const 23 + i32.lt_u + if + local.get $0 + i32.const 2 + i32.shl + i32.const 17872 + i32.add + i32.const 0 + i32.store offset=4 + i32.const 0 + local.set $1 + loop $for-loop|1 + local.get $1 + i32.const 16 + i32.lt_u + if + local.get $0 + i32.const 4 + i32.shl + local.get $1 + i32.add + i32.const 2 + i32.shl + i32.const 17872 + i32.add + i32.const 0 + i32.store offset=96 + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|1 + end + end + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|0 + end + end + i32.const 17872 + i32.const 19444 + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + i32.const 17872 + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/itcms/step (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + block $break|0 + block $case2|0 + block $case1|0 + block $case0|0 + global.get $~lib/rt/itcms/state + br_table $case0|0 $case1|0 $case2|0 $break|0 + end + i32.const 1 + global.set $~lib/rt/itcms/state + i32.const 0 + global.set $~lib/rt/itcms/visitCount + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/visitCount + return + end + global.get $~lib/rt/itcms/white + i32.eqz + local.set $1 + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|1 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $0 + global.set $~lib/rt/itcms/iter + local.get $1 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.ne + if + local.get $0 + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.get $1 + i32.or + i32.store offset=4 + i32.const 0 + global.set $~lib/rt/itcms/visitCount + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + global.get $~lib/rt/itcms/visitCount + return + end + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|1 + end + end + i32.const 0 + global.set $~lib/rt/itcms/visitCount + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + i32.eq + if + global.get $~lib/memory/__stack_pointer + local.set $0 + loop $while-continue|0 + local.get $0 + i32.const 17868 + i32.lt_u + if + local.get $0 + i32.load + local.tee $2 + if + local.get $2 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + local.get $0 + i32.const 4 + i32.add + local.set $0 + br $while-continue|0 + end + end + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|2 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $1 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.ne + if + local.get $0 + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.get $1 + i32.or + i32.store offset=4 + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + end + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|2 + end + end + global.get $~lib/rt/itcms/fromSpace + local.set $0 + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/fromSpace + local.get $0 + global.set $~lib/rt/itcms/toSpace + local.get $1 + global.set $~lib/rt/itcms/white + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + global.set $~lib/rt/itcms/iter + i32.const 2 + global.set $~lib/rt/itcms/state + end + global.get $~lib/rt/itcms/visitCount + return + end + global.get $~lib/rt/itcms/iter + local.tee $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $0 + i32.load offset=4 + local.tee $1 + i32.const -4 + i32.and + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/white + i32.eqz + local.get $1 + i32.const 3 + i32.and + i32.ne + if + i32.const 0 + i32.const 1120 + i32.const 228 + i32.const 20 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 17868 + i32.lt_u + if + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + else + global.get $~lib/rt/itcms/total + local.get $0 + i32.load + i32.const -4 + i32.and + i32.const 4 + i32.add + i32.sub + global.set $~lib/rt/itcms/total + local.get $0 + i32.const 4 + i32.add + local.tee $0 + i32.const 17868 + i32.ge_u + if + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + i32.const 4 + i32.sub + local.set $2 + local.get $0 + i32.const 15 + i32.and + i32.const 1 + local.get $0 + select + if (result i32) + i32.const 1 + else + local.get $2 + i32.load + i32.const 1 + i32.and + end + if + i32.const 0 + i32.const 1392 + i32.const 559 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $2 + local.get $2 + i32.load + i32.const 1 + i32.or + i32.store + local.get $2 + call $~lib/rt/tlsf/insertBlock + end + end + i32.const 10 + return + end + global.get $~lib/rt/itcms/toSpace + local.tee $0 + local.get $0 + i32.store offset=4 + local.get $0 + local.get $0 + i32.store offset=8 + i32.const 0 + global.set $~lib/rt/itcms/state + end + i32.const 0 + ) + (func $~lib/rt/tlsf/searchBlock (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + local.get $0 + i32.load offset=4 + i32.const -2 + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + i32.ctz + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + else + local.get $0 + i32.load + i32.const -2 + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + i32.ctz + local.tee $2 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.tee $1 + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 343 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.ctz + local.get $2 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + else + i32.const 0 + end + end + ) + (func $~lib/rt/itcms/__new (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.ge_u + if + block $__inlined_func$~lib/rt/itcms/interrupt + i32.const 2048 + local.set $0 + loop $do-loop|0 + local.get $0 + call $~lib/rt/itcms/step + i32.sub + local.set $0 + global.get $~lib/rt/itcms/state + i32.eqz + if + global.get $~lib/rt/itcms/total + i64.extend_i32_u + i64.const 200 + i64.mul + i64.const 100 + i64.div_u + i32.wrap_i64 + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + br $__inlined_func$~lib/rt/itcms/interrupt + end + local.get $0 + i32.const 0 + i32.gt_s + br_if $do-loop|0 + end + global.get $~lib/rt/itcms/total + local.tee $0 + global.get $~lib/rt/itcms/threshold + i32.sub + i32.const 1024 + i32.lt_u + i32.const 10 + i32.shl + local.get $0 + i32.add + global.set $~lib/rt/itcms/threshold + end + end + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.tee $2 + call $~lib/rt/tlsf/searchBlock + local.tee $0 + i32.eqz + if + memory.size + local.tee $0 + i32.const 4 + local.get $2 + i32.load offset=1568 + local.get $0 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + i32.ne + i32.shl + i32.const 65563 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $0 + local.get $1 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $1 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + local.get $2 + local.get $0 + i32.const 16 + i32.shl + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 496 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + local.get $0 + i32.load + i32.const -4 + i32.and + i32.const 28 + i32.lt_u + if + i32.const 0 + i32.const 1392 + i32.const 498 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + local.get $0 + call $~lib/rt/tlsf/removeBlock + local.get $0 + i32.load + local.tee $3 + i32.const -4 + i32.and + i32.const 28 + i32.sub + local.tee $1 + i32.const 16 + i32.ge_u + if + local.get $0 + local.get $3 + i32.const 2 + i32.and + i32.const 28 + i32.or + i32.store + local.get $0 + i32.const 32 + i32.add + local.tee $3 + local.get $1 + i32.const 4 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $2 + local.get $3 + call $~lib/rt/tlsf/insertBlock + else + local.get $0 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $0 + i32.const 4 + i32.add + local.get $0 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $1 + local.get $1 + i32.load + i32.const -3 + i32.and + i32.store + end + local.get $0 + i32.const 3 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + global.get $~lib/rt/itcms/fromSpace + local.tee $1 + i32.load offset=8 + local.set $2 + local.get $0 + local.get $1 + global.get $~lib/rt/itcms/white + i32.or + i32.store offset=4 + local.get $0 + local.get $2 + i32.store offset=8 + local.get $2 + local.get $0 + local.get $2 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + global.get $~lib/rt/itcms/total + local.get $0 + i32.load + i32.const -4 + i32.and + i32.const 4 + i32.add + i32.add + global.set $~lib/rt/itcms/total + local.get $0 + i32.const 20 + i32.add + local.tee $0 + i32.const 0 + i32.const 0 + memory.fill + local.get $0 + ) + (func $~lib/rt/__visit_members (param $0 i32) + block $invalid + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid + end + return + end + return + end + local.get $0 + i32.load + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return + end + return + end + return + end + unreachable + ) + (func $~start + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1484 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $0 + i64.const 0 + i64.store + memory.size + i32.const 16 + i32.shl + i32.const 17868 + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 1172 + i32.const 1168 + i32.store + i32.const 1176 + i32.const 1168 + i32.store + i32.const 1168 + global.set $~lib/rt/itcms/pinSpace + i32.const 1204 + i32.const 1200 + i32.store + i32.const 1208 + i32.const 1200 + i32.store + i32.const 1200 + global.set $~lib/rt/itcms/toSpace + i32.const 1348 + i32.const 1344 + i32.store + i32.const 1352 + i32.const 1344 + i32.store + i32.const 1344 + global.set $~lib/rt/itcms/fromSpace + local.get $0 + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1484 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $0 + i32.const 0 + i32.store + local.get $0 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + global.set $typenarrow/t + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1440 + i32.load + i32.le_u + if + loop $do-loop|0 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1444 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|0 + end + end + i32.const 0 + end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + return + end + i32.const 17888 + i32.const 17936 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) + (func $byn-split-outlined-A$~lib/rt/itcms/__visit (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + global.get $~lib/rt/itcms/white + local.get $0 + i32.const 20 + i32.sub + local.tee $1 + i32.load offset=4 + i32.const 3 + i32.and + i32.eq + if + local.get $1 + global.get $~lib/rt/itcms/iter + i32.eq + if + local.get $1 + i32.load offset=8 + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 1120 + i32.const 147 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + global.set $~lib/rt/itcms/iter + end + block $__inlined_func$~lib/rt/itcms/Object#unlink + local.get $1 + i32.load offset=4 + i32.const -4 + i32.and + local.tee $0 + i32.eqz + if + i32.const 0 + local.get $1 + i32.const 17868 + i32.lt_u + local.get $1 + i32.load offset=8 + select + i32.eqz + if + i32.const 0 + i32.const 1120 + i32.const 127 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + br $__inlined_func$~lib/rt/itcms/Object#unlink + end + local.get $1 + i32.load offset=8 + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 1120 + i32.const 131 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.store offset=8 + local.get $2 + local.get $0 + local.get $2 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + end + global.get $~lib/rt/itcms/toSpace + local.set $2 + local.get $1 + i32.load offset=12 + local.tee $0 + i32.const 1 + i32.le_u + if (result i32) + i32.const 1 + else + local.get $0 + i32.const 1440 + i32.load + i32.gt_u + if + i32.const 1248 + i32.const 1312 + i32.const 22 + i32.const 28 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 3 + i32.shl + i32.const 1444 + i32.add + i32.load + i32.const 32 + i32.and + end + if (result i32) + global.get $~lib/rt/itcms/white + i32.eqz + else + i32.const 2 + end + local.set $3 + local.get $2 + i32.load offset=8 + local.set $0 + local.get $1 + local.get $2 + local.get $3 + i32.or + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.store offset=8 + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.add + global.set $~lib/rt/itcms/visitCount + end + ) +) diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts new file mode 100644 index 0000000000..2c4fa28ad5 --- /dev/null +++ b/tests/compiler/typenarrow.ts @@ -0,0 +1,9 @@ +class A {} +class B extends A { + foo(): void {} +} + +let t = new A(); +if (t instanceof B) { + t.foo(); +} From f55e01a3d2160419da0b4762ba697f6c0cb9c5fb Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Fri, 1 Jul 2022 09:58:11 +0800 Subject: [PATCH 02/55] Revert "feat: add type narrow to support better type check" This reverts commit df38498d9b4c6f4ac98c4127963acb1273ec1f52. --- src/compiler.ts | 22 +--------------------- src/conditionInfo.ts | 31 ------------------------------- src/flow.ts | 20 -------------------- src/program.ts | 10 ---------- 4 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 src/conditionInfo.ts diff --git a/src/compiler.ts b/src/compiler.ts index e1e55efccf..98d9a42084 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -88,8 +88,7 @@ import { PropertyPrototype, IndexSignature, File, - mangleInternalName, - TypedElement + mangleInternalName } from "./program"; import { @@ -211,7 +210,6 @@ import { import { ShadowStackPass } from "./passes/shadowstack"; -import { TypeNarrowInfo } from "./conditionInfo"; /** Compiler options. */ export class Options { @@ -2667,17 +2665,12 @@ export class Compiler extends DiagnosticEmitter { // ) └┬─────────┴─┘ // ... ┌◄┘ - this.currentFlow.startCompileCondition(); - // Precompute the condition (always executes) var condExpr = this.makeIsTrueish( this.compileExpression(statement.condition, Type.bool), this.currentType, statement.condition ); - - const conditionInfoContainer = this.currentFlow.stopCompileCondition(); - var condKind = this.evaluateCondition(condExpr); // Shortcut if the condition is constant @@ -2707,13 +2700,11 @@ export class Compiler extends DiagnosticEmitter { var thenFlow = flow.fork(); this.currentFlow = thenFlow; thenFlow.inheritNonnullIfTrue(condExpr); - conditionInfoContainer.trueInfo.forEach((info) => info.apply()); if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); } else { thenStmts.push(this.compileStatement(ifTrue)); } - conditionInfoContainer.trueInfo.forEach((info) => info.recover()); var thenTerminates = thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS); if (thenTerminates) { thenStmts.push(module.unreachable()); @@ -2727,13 +2718,11 @@ export class Compiler extends DiagnosticEmitter { let elseFlow = flow.fork(); this.currentFlow = elseFlow; elseFlow.inheritNonnullIfFalse(condExpr); - conditionInfoContainer.falseInfo.forEach((info) => info.apply()); if (ifFalse.kind == NodeKind.BLOCK) { this.compileStatements((ifFalse).statements, false, elseStmts); } else { elseStmts.push(this.compileStatement(ifFalse)); } - conditionInfoContainer.falseInfo.forEach((info) => info.recover()); let elseTerminates = elseFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS); if (elseTerminates) { elseStmts.push(module.unreachable()); @@ -5857,7 +5846,6 @@ export class Compiler extends DiagnosticEmitter { var flow = this.currentFlow; var target = resolver.lookupExpression(expression, flow); // reports if (!target) return this.module.unreachable(); - if (target instanceof TypedElement) (target).recoverType(); var thisExpression = resolver.currentThisExpression; var elementExpression = resolver.currentElementExpression; @@ -7915,14 +7903,6 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.bool; return this.module.unreachable(); } - if (expression.expression.kind == NodeKind.IDENTIFIER) { - const identifier = expression.expression; - const element = flow.lookup(identifier.text); - if (element && element instanceof TypedElement) { - const typedElement = element; - flow.addConditionInfo(new TypeNarrowInfo(typedElement, expectedType)); - } - } return this.makeInstanceofType(expression, expectedType); } diff --git a/src/conditionInfo.ts b/src/conditionInfo.ts deleted file mode 100644 index a5d1cfb2b0..0000000000 --- a/src/conditionInfo.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TypedElement } from "./program"; -import { Type } from "./types"; - -export class ConditionInfoContainer { - trueInfo: ConditionInfo[] = []; - falseInfo: ConditionInfo[] = []; - - switchTrueAndFalse(): void { - let tmp = this.trueInfo; - this.trueInfo = this.falseInfo; - this.falseInfo = tmp; - } -} - -export abstract class ConditionInfo { - abstract apply(): void; - abstract recover(): void; -} - -export class TypeNarrowInfo extends ConditionInfo { - constructor(public typedElement: TypedElement, public narrowedType: Type) { - super(); - } - - apply(): void { - this.typedElement.narrowType(this.narrowedType); - } - recover(): void { - this.typedElement.recoverType(); - } -} diff --git a/src/flow.ts b/src/flow.ts index 76e6c55571..8e37112692 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -90,7 +90,6 @@ import { import { BuiltinNames } from "./builtins"; -import { ConditionInfo, ConditionInfoContainer } from "./conditionInfo"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -1222,25 +1221,6 @@ export class Flow { } } - conditionInfoStack: ConditionInfoContainer[] = []; - startCompileCondition(): void { - this.conditionInfoStack.push(new ConditionInfoContainer()); - } - stopCompileCondition(): ConditionInfoContainer { - return assert(this.conditionInfoStack.pop()); - } - addConditionInfo(conditionInfo: ConditionInfo, branch: bool = true): void { - if (this.conditionInfoStack.length > 0) { - const infoContainer = - this.conditionInfoStack[this.conditionInfoStack.length - 1]; - if (branch) { - infoContainer.trueInfo.push(conditionInfo); - } else { - infoContainer.falseInfo.push(conditionInfo); - } - } - } - /** * 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 diff --git a/src/program.ts b/src/program.ts index b2c4eccdde..2958241478 100644 --- a/src/program.ts +++ b/src/program.ts @@ -2992,7 +2992,6 @@ export abstract class TypedElement extends DeclaredElement { /** Resolved type. Set once `is(RESOLVED)`, otherwise void. */ type: Type = Type.void; - typeStack: Type[] = []; constructor( /** Specific element kind. */ @@ -3018,15 +3017,6 @@ export abstract class TypedElement extends DeclaredElement { this.type = type; this.set(CommonFlags.RESOLVED); } - narrowType(type: Type): void { - if (type.isClass) { - this.typeStack.push(this.type); - this.type = type; - } - } - recoverType(): void { - this.type = this.typeStack.length > 0 ? this.typeStack.pop() : this.type; - } } /** A file representing the implicit top-level namespace of a source. */ From aab3bfdeddbb745350eb6e11d9a4520fa98d32c6 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Fri, 1 Jul 2022 11:12:02 +0800 Subject: [PATCH 03/55] add narrow map in flow and use narrowedType if available [WIP] --- src/compiler.ts | 12 +- src/flow.ts | 136 ++++++++- src/resolver.ts | 2 + tests/compiler/typenarrow.debug.wat | 36 +++ tests/compiler/typenarrow.release.wat | 405 +++++++++++++++----------- tests/compiler/typenarrow.ts | 7 + 6 files changed, 424 insertions(+), 174 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 98d9a42084..1633241ba6 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2666,11 +2666,13 @@ export class Compiler extends DiagnosticEmitter { // ... ┌◄┘ // Precompute the condition (always executes) + this.currentFlow.startCondition(); var condExpr = this.makeIsTrueish( this.compileExpression(statement.condition, Type.bool), this.currentType, statement.condition ); + this.currentFlow.stopCondition(); var condKind = this.evaluateCondition(condExpr); // Shortcut if the condition is constant @@ -2700,6 +2702,7 @@ export class Compiler extends DiagnosticEmitter { var thenFlow = flow.fork(); this.currentFlow = thenFlow; thenFlow.inheritNonnullIfTrue(condExpr); + thenFlow.inheritLocalTypeIfTrue(flow); if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); } else { @@ -7756,7 +7759,8 @@ export class Compiler extends DiagnosticEmitter { switch (target.kind) { case ElementKind.LOCAL: { let local = target; - let localType = local.type; + let narrowedType = flow.getNarrowedType(local); + let localType = narrowedType ? narrowedType : local.type; assert(localType != Type.void); if (this.pendingElements.has(local)) { this.error( @@ -7793,7 +7797,8 @@ export class Compiler extends DiagnosticEmitter { if (!this.compileGlobal(global)) { // reports; not yet compiled if a static field return module.unreachable(); } - let globalType = global.type; + let narrowedType = flow.getNarrowedType(global); + let globalType = narrowedType ? narrowedType : global.type; if (this.pendingElements.has(global)) { this.error( DiagnosticCode.Variable_0_used_before_its_declaration, @@ -7913,6 +7918,9 @@ export class Compiler extends DiagnosticEmitter { var actualType = this.currentType; this.currentType = Type.bool; + let element = flow.lookupTypedElementByExpressionRef(expr); + if (element) flow.setConditionLocalType(element, expectedType); + // instanceof - must be exact if (expectedType.isValue) { return module.maybeDropCondition(expr, module.i32(actualType == expectedType ? 1 : 0)); diff --git a/src/flow.ts b/src/flow.ts index 8e37112692..878f2aa278 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -28,6 +28,7 @@ import { ElementKind, Field, Class, + Global, TypedElement } from "./program"; @@ -237,6 +238,13 @@ export class Flow { localFlags: LocalFlags[] = []; /** Field flags on `this`. Constructors only. */ thisFieldFlags: Map | null = null; + /** type narrow */ + narrowedTypes: Map = new Map(); + /** */ + inCondition: bool = false; + narrowedTypesIfTrue: Map = new Map(); + narrowedTypesIfFalse: Map = new Map(); + /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; /** The label we break to when encountering a return statement, when inlining. */ @@ -311,6 +319,11 @@ export class Flow { branch.breakLabel = this.breakLabel; } branch.localFlags = this.localFlags.slice(); + const narrowedTypes = this.narrowedTypes; + for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + branch.setNarrowedType(key, assert(narrowedTypes.get(key))); + } if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); branch.thisFieldFlags = uniqueMap(thisFieldFlags); @@ -555,6 +568,27 @@ export class Flow { } } + /** check if an expression is a local variant, return -1 means not, otherwise return local index */ + lookupTypedElementByExpressionRef(expr: ExpressionRef): TypedElement | null { + switch (getExpressionId(expr)) { + case ExpressionId.LocalSet: { + if (!isLocalTee(expr)) break; + return this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + } + case ExpressionId.LocalGet: { + return this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; + } + case ExpressionId.GlobalGet: { + let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr)))); + if (global.kind == ElementKind.GLOBAL) { + return global; + } + break; + } + } + return null; + } + /** Looks up the local of the specified name in the current scope. */ lookupLocal(name: string): Local | null { var current: Flow | null = this; @@ -605,6 +639,76 @@ export class Flow { localFlags[index] = flags & ~flag; } + setNarrowedType(element: TypedElement, type: Type | null): void { + if (type == null && this.narrowedTypes.has(element)) { + this.narrowedTypes.delete(element); + } else if (type) { + this.narrowedTypes.set(element, type); + } + } + getNarrowedType(element: TypedElement): Type | null { + return this.narrowedTypes.has(element) ? changetype(this.narrowedTypes.get(element)) : null + } + + static mergeLocalType(a: Type | null, b: Type | null): Type | null { + if (a == null || b == null) { + return null; + } else if (a.isAssignableTo(b)) { + return a; + } else if (b.isAssignableTo(a)) { + return b; + } else { + return null; + } + } + static updateLocalType(origin: Type | null, update: Type | null): Type | null{ + if (origin == null) { + return update; + } else if (update == null) { + return origin; + } else if (update.isAssignableTo(origin)) { + return update; + } else if (origin.isAssignableTo(update)) { + return origin; + } + assert(false, "cannot update type"); + return origin; + } + + startCondition(): void { + this.inCondition = true; + } + stopCondition(): void { + this.inCondition = false; + } + switchTrueFalse(): void { + let tmp = this.narrowedTypesIfTrue; + this.narrowedTypesIfTrue = this.narrowedTypesIfFalse; + this.narrowedTypesIfFalse = tmp; + } + setConditionLocalType(element: TypedElement, type: Type | null, condi:bool = true): void { + let potentialLocalTypes = condi ? this.narrowedTypesIfTrue : this.narrowedTypesIfFalse; + if (type == null && potentialLocalTypes.has(element)) { + potentialLocalTypes.delete(element); + } else if (type) { + potentialLocalTypes.set(element, type); + } + } + inheritLocalTypeIfTrue(flow: Flow): void { + let narrowedTypesConditional = flow.narrowedTypesIfTrue; + for (let _key = Map_keys(narrowedTypesConditional), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.updateLocalType(this.getNarrowedType(key), assert(narrowedTypesConditional.get(key)))); + } + } + inheritLocalTypeIfFalse(flow: Flow): void { + let narrowedTypesConditional = flow.narrowedTypesIfFalse; + for (let _key = Map_keys(narrowedTypesConditional), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.updateLocalType(this.getNarrowedType(key), assert(narrowedTypesConditional.get(key)))); + } + } + /** Initializes `this` field flags. */ initThisFieldFlags(): void { var actualFunction = this.actualFunction; @@ -706,6 +810,7 @@ export class Flow { this.flags = this.flags | otherFlags; // what happens before is still true this.localFlags = other.localFlags; + this.narrowedTypes = other.narrowedTypes; this.thisFieldFlags = other.thisFieldFlags; } @@ -809,6 +914,13 @@ export class Flow { ); } + // local types + const narrowedTypes = other.narrowedTypes + for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + } + // field flags do not matter here since there's only INITIALIZED, which can // only be set if it has been observed prior to entering the branch. } @@ -904,7 +1016,7 @@ export class Flow { this.flags = newFlags | (this.flags & (FlowFlags.UNCHECKED_CONTEXT | FlowFlags.CTORPARAM_CONTEXT)); - // local flags + // local flags and type var thisLocalFlags = this.localFlags; if (leftFlags & FlowFlags.TERMINATES) { if (!(rightFlags & FlowFlags.TERMINATES)) { @@ -912,12 +1024,22 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } + const narrowedTypes = right.narrowedTypes + for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + } } } else if (rightFlags & FlowFlags.TERMINATES) { let leftLocalFlags = left.localFlags; for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } + const narrowedTypes = left.narrowedTypes + for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + } } else { let leftLocalFlags = left.localFlags; let numLeftLocalFlags = leftLocalFlags.length; @@ -934,6 +1056,18 @@ export class Flow { LocalFlags.INITIALIZED ); } + + // local types + const leftNarrowedTypes = left.narrowedTypes; + for (let _key = Map_keys(leftNarrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); + } + const rightNarrowedTypes = right.narrowedTypes; + for (let _key = Map_keys(rightNarrowedTypes), i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); + } } // field flags (currently only INITIALIZED, so can simplify) diff --git a/src/resolver.ts b/src/resolver.ts index 2cdfe69a4f..9fd51a7b47 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -1257,6 +1257,8 @@ export class Resolver extends DiagnosticEmitter { case ElementKind.FIELD: { // someVar.prop let variableLikeElement = target; let type = variableLikeElement.type; + let narrowedType = ctxFlow.getNarrowedType(variableLikeElement); + if (narrowedType) type = narrowedType; assert(type != Type.void); let classReference = type.getClassOrWrapper(this.program); if (!classReference) { diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index e40b1be409..7e40511176 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -40,6 +40,7 @@ (data (i32.const 416) "\05\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00") (table $0 1 1 funcref) (elem $0 (i32.const 1)) + (export "test" (func $typenarrow/test)) (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/Object#set:nextWithColor (param $0 i32) (param $1 i32) @@ -2154,6 +2155,41 @@ (func $typenarrow/B#foo (param $0 i32) nop ) + (func $typenarrow/test + (local $0 i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 0 + call $typenarrow/A#constructor + local.tee $0 + i32.store + local.get $0 + local.tee $1 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $1 + i32.const 4 + call $~lib/rt/__instanceof + end + if + local.get $0 + call $typenarrow/B#foo + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $~lib/rt/__visit_globals (param $0 i32) (local $1 i32) global.get $typenarrow/t diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index e7f6fccb8b..862b311f29 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -32,6 +32,7 @@ (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") (data (i32.const 1440) "\05\00\00\00 \00\00\00\00\00\00\00 ") (data (i32.const 1468) " \00\00\00\00\00\00\00 \00\00\00\03") + (export "test" (func $typenarrow/test)) (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/visitRoots @@ -997,11 +998,223 @@ end end ) - (func $~lib/rt/itcms/__new (result i32) + (func $typenarrow/test + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1484 + i32.lt_s + if + i32.const 17888 + i32.const 17936 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + i32.const 0 + i32.store + local.get $0 + call $typenarrow/A#constructor + local.tee $0 + i32.store + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1440 + i32.load + i32.le_u + if + loop $do-loop|0 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1444 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|0 + end + end + end + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $~lib/rt/__visit_members (param $0 i32) + block $invalid + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid + end + return + end + return + end + local.get $0 + i32.load + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return + end + return + end + return + end + unreachable + ) + (func $~start + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1484 + i32.lt_s + if + i32.const 17888 + i32.const 17936 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + memory.size + i32.const 16 + i32.shl + i32.const 17868 + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 1172 + i32.const 1168 + i32.store + i32.const 1176 + i32.const 1168 + i32.store + i32.const 1168 + global.set $~lib/rt/itcms/pinSpace + i32.const 1204 + i32.const 1200 + i32.store + i32.const 1208 + i32.const 1200 + i32.store + i32.const 1200 + global.set $~lib/rt/itcms/toSpace + i32.const 1348 + i32.const 1344 + i32.store + i32.const 1352 + i32.const 1344 + i32.store + i32.const 1344 + global.set $~lib/rt/itcms/fromSpace + call $typenarrow/A#constructor + global.set $typenarrow/t + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1440 + i32.load + i32.le_u + if + loop $do-loop|0 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1444 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|0 + end + end + i32.const 0 + end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $typenarrow/A#constructor (result i32) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) + (local $4 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1484 + i32.lt_s + if + i32.const 17888 + i32.const 17936 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $2 + i32.const 0 + i32.store global.get $~lib/rt/itcms/total global.get $~lib/rt/itcms/threshold i32.ge_u @@ -1053,7 +1266,7 @@ call $~lib/rt/tlsf/initialize end global.get $~lib/rt/tlsf/ROOT - local.tee $2 + local.tee $3 call $~lib/rt/tlsf/searchBlock local.tee $0 i32.eqz @@ -1061,7 +1274,7 @@ memory.size local.tee $0 i32.const 4 - local.get $2 + local.get $3 i32.load offset=1568 local.get $0 i32.const 16 @@ -1093,7 +1306,7 @@ unreachable end end - local.get $2 + local.get $3 local.get $0 i32.const 16 i32.shl @@ -1101,7 +1314,7 @@ i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - local.get $2 + local.get $3 call $~lib/rt/tlsf/searchBlock local.tee $0 i32.eqz @@ -1128,12 +1341,12 @@ call $~lib/builtins/abort unreachable end - local.get $2 + local.get $3 local.get $0 call $~lib/rt/tlsf/removeBlock local.get $0 i32.load - local.tee $3 + local.tee $4 i32.const -4 i32.and i32.const 28 @@ -1143,7 +1356,7 @@ i32.ge_u if local.get $0 - local.get $3 + local.get $4 i32.const 2 i32.and i32.const 28 @@ -1152,19 +1365,19 @@ local.get $0 i32.const 32 i32.add - local.tee $3 + local.tee $4 local.get $1 i32.const 4 i32.sub i32.const 1 i32.or i32.store - local.get $2 local.get $3 + local.get $4 call $~lib/rt/tlsf/insertBlock else local.get $0 - local.get $3 + local.get $4 i32.const -2 i32.and i32.store @@ -1192,18 +1405,18 @@ global.get $~lib/rt/itcms/fromSpace local.tee $1 i32.load offset=8 - local.set $2 + local.set $3 local.get $0 - local.get $1 global.get $~lib/rt/itcms/white + local.get $1 i32.or i32.store offset=4 local.get $0 - local.get $2 + local.get $3 i32.store offset=8 - local.get $2 + local.get $3 local.get $0 - local.get $2 + local.get $3 i32.load offset=4 i32.const 3 i32.and @@ -1228,164 +1441,14 @@ i32.const 0 i32.const 0 memory.fill + local.get $2 local.get $0 - ) - (func $~lib/rt/__visit_members (param $0 i32) - block $invalid - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid - end - return - end - return - end - local.get $0 - i32.load - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - return - end - return - end - return - end - unreachable - ) - (func $~start - (local $0 i32) + i32.store global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.sub + i32.const 4 + i32.add global.set $~lib/memory/__stack_pointer - block $folding-inner0 - global.get $~lib/memory/__stack_pointer - i32.const 1484 - i32.lt_s - br_if $folding-inner0 - global.get $~lib/memory/__stack_pointer - local.tee $0 - i64.const 0 - i64.store - memory.size - i32.const 16 - i32.shl - i32.const 17868 - i32.sub - i32.const 1 - i32.shr_u - global.set $~lib/rt/itcms/threshold - i32.const 1172 - i32.const 1168 - i32.store - i32.const 1176 - i32.const 1168 - i32.store - i32.const 1168 - global.set $~lib/rt/itcms/pinSpace - i32.const 1204 - i32.const 1200 - i32.store - i32.const 1208 - i32.const 1200 - i32.store - i32.const 1200 - global.set $~lib/rt/itcms/toSpace - i32.const 1348 - i32.const 1344 - i32.store - i32.const 1352 - i32.const 1344 - i32.store - i32.const 1344 - global.set $~lib/rt/itcms/fromSpace - local.get $0 - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1484 - i32.lt_s - br_if $folding-inner0 - global.get $~lib/memory/__stack_pointer - local.tee $0 - i32.const 0 - i32.store - local.get $0 - call $~lib/rt/itcms/__new - local.tee $0 - i32.store - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - local.get $0 - global.set $typenarrow/t - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof (result i32) - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1440 - i32.load - i32.le_u - if - loop $do-loop|0 - i32.const 1 - local.get $0 - i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1444 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|0 - end - end - i32.const 0 - end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.add - global.set $~lib/memory/__stack_pointer - return - end - i32.const 17888 - i32.const 17936 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable + local.get $0 ) (func $byn-split-outlined-A$~lib/rt/itcms/__visit (param $0 i32) (local $1 i32) diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 2c4fa28ad5..3032ef703d 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -7,3 +7,10 @@ let t = new A(); if (t instanceof B) { t.foo(); } + +export function test(): void { + let t = new A(); + if (t instanceof B) { + t.foo(); + } +} From 5fd12b34b7913d55c542473ef4b66d637fb59a38 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Fri, 1 Jul 2022 23:56:37 +0800 Subject: [PATCH 04/55] fix: style --- src/flow.ts | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 878f2aa278..b3d63776ec 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -568,26 +568,26 @@ export class Flow { } } - /** check if an expression is a local variant, return -1 means not, otherwise return local index */ - lookupTypedElementByExpressionRef(expr: ExpressionRef): TypedElement | null { - switch (getExpressionId(expr)) { - case ExpressionId.LocalSet: { - if (!isLocalTee(expr)) break; - return this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - } - case ExpressionId.LocalGet: { - return this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - } - case ExpressionId.GlobalGet: { - let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr)))); - if (global.kind == ElementKind.GLOBAL) { - return global; - } - break; + /** check if an expression is a local variant, return -1 means not, otherwise return local index */ + lookupTypedElementByExpressionRef(expr: ExpressionRef): TypedElement | null { + switch (getExpressionId(expr)) { + case ExpressionId.LocalSet: { + if (!isLocalTee(expr)) break; + return this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + } + case ExpressionId.LocalGet: { + return this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; + } + case ExpressionId.GlobalGet: { + let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr)))); + if (global.kind == ElementKind.GLOBAL) { + return global; } + break; } - return null; } + return null; + } /** Looks up the local of the specified name in the current scope. */ lookupLocal(name: string): Local | null { @@ -647,7 +647,7 @@ export class Flow { } } getNarrowedType(element: TypedElement): Type | null { - return this.narrowedTypes.has(element) ? changetype(this.narrowedTypes.get(element)) : null + return this.narrowedTypes.has(element) ? changetype(this.narrowedTypes.get(element)) : null; } static mergeLocalType(a: Type | null, b: Type | null): Type | null { @@ -915,7 +915,7 @@ export class Flow { } // local types - const narrowedTypes = other.narrowedTypes + const narrowedTypes = other.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); @@ -1024,7 +1024,7 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } - const narrowedTypes = right.narrowedTypes + const narrowedTypes = right.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); @@ -1035,7 +1035,7 @@ export class Flow { for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } - const narrowedTypes = left.narrowedTypes + const narrowedTypes = left.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); From c62370967c49345b711f8e585fc941268ec1ef2c Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 3 Jul 2022 14:23:30 +0800 Subject: [PATCH 05/55] update --- src/compiler.ts | 32 +++- src/flow.ts | 89 +++------- src/narrow.ts | 244 ++++++++++++++++++++++++++ tests/compiler/typenarrow-error.json | 1 + tests/compiler/typenarrow-error.ts | 10 +- tests/compiler/typenarrow.debug.wat | 119 ++++++++++--- tests/compiler/typenarrow.release.wat | 196 ++++++++++++++++----- tests/compiler/typenarrow.ts | 11 ++ 8 files changed, 564 insertions(+), 138 deletions(-) create mode 100644 src/narrow.ts diff --git a/src/compiler.ts b/src/compiler.ts index 1633241ba6..8e89c2b661 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -88,7 +88,8 @@ import { PropertyPrototype, IndexSignature, File, - mangleInternalName + mangleInternalName, + TypedElement } from "./program"; import { @@ -2555,6 +2556,7 @@ export class Compiler extends DiagnosticEmitter { // Compile the body assuming the condition turned out true var bodyFlow = flow.fork(); bodyFlow.inheritNonnullIfTrue(condExpr); + bodyFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); var body = statement.statement; @@ -2666,13 +2668,11 @@ export class Compiler extends DiagnosticEmitter { // ... ┌◄┘ // Precompute the condition (always executes) - this.currentFlow.startCondition(); var condExpr = this.makeIsTrueish( this.compileExpression(statement.condition, Type.bool), this.currentType, statement.condition ); - this.currentFlow.stopCondition(); var condKind = this.evaluateCondition(condExpr); // Shortcut if the condition is constant @@ -2702,7 +2702,7 @@ export class Compiler extends DiagnosticEmitter { var thenFlow = flow.fork(); this.currentFlow = thenFlow; thenFlow.inheritNonnullIfTrue(condExpr); - thenFlow.inheritLocalTypeIfTrue(flow); + thenFlow.inheritNarrowedTypeIfTrue(condExpr); if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); } else { @@ -2721,6 +2721,7 @@ export class Compiler extends DiagnosticEmitter { let elseFlow = flow.fork(); this.currentFlow = elseFlow; elseFlow.inheritNonnullIfFalse(condExpr); + elseFlow.inheritNarrowedTypeIfFalse(condExpr); if (ifFalse.kind == NodeKind.BLOCK) { this.compileStatements((ifFalse).statements, false, elseStmts); } else { @@ -2744,6 +2745,9 @@ export class Compiler extends DiagnosticEmitter { ? null // thenFlow terminates: just inherit : thenFlow // must become nonnull in thenFlow otherwise ); + if (thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS)) { + flow.inheritNarrowedTypeIfFalse(condExpr); + } return module.if(condExpr, module.flatten(thenStmts) ); @@ -3239,6 +3243,7 @@ export class Compiler extends DiagnosticEmitter { // Compile the body assuming the condition turned out true var bodyFlow = flow.fork(); bodyFlow.inheritNonnullIfTrue(condExpr); + bodyFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); var body = statement.statement; @@ -4547,6 +4552,7 @@ export class Compiler extends DiagnosticEmitter { let rightFlow = flow.fork(); this.currentFlow = rightFlow; rightFlow.inheritNonnullIfTrue(leftExpr); + rightFlow.inheritNarrowedTypeIfTrue(leftExpr); // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { @@ -4611,6 +4617,7 @@ export class Compiler extends DiagnosticEmitter { let rightFlow = flow.fork(); this.currentFlow = rightFlow; rightFlow.inheritNonnullIfFalse(leftExpr); + rightFlow.inheritNarrowedTypeIfFalse(leftExpr); // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { @@ -5941,6 +5948,9 @@ export class Compiler extends DiagnosticEmitter { assert(targetType != Type.void); var valueExpr = this.compileExpression(valueExpression, targetType); var valueType = this.currentType; + if (target instanceof TypedElement) { + flow.setNarrowedType(target, null); + } return this.makeAssignment( target, this.convertExpression(valueExpr, valueType, targetType, false, valueExpression), @@ -7908,7 +7918,14 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.bool; return this.module.unreachable(); } - return this.makeInstanceofType(expression, expectedType); + let instanceExpression = this.makeInstanceofType(expression, expectedType); + if (expression.expression.kind == NodeKind.IDENTIFIER) { + let element = flow.lookup((expression.expression).text); + if (element instanceof TypedElement) { + flow.setConditionNarrowedType(instanceExpression, element, expectedType); + } + } + return instanceExpression; } private makeInstanceofType(expression: InstanceOfExpression, expectedType: Type): ExpressionRef { @@ -7918,9 +7935,6 @@ export class Compiler extends DiagnosticEmitter { var actualType = this.currentType; this.currentType = Type.bool; - let element = flow.lookupTypedElementByExpressionRef(expr); - if (element) flow.setConditionLocalType(element, expectedType); - // instanceof - must be exact if (expectedType.isValue) { return module.maybeDropCondition(expr, module.i32(actualType == expectedType ? 1 : 0)); @@ -9269,12 +9283,14 @@ export class Compiler extends DiagnosticEmitter { var outerFlow = this.currentFlow; var ifThenFlow = outerFlow.fork(); ifThenFlow.inheritNonnullIfTrue(condExpr); + ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = ifThenFlow; var ifThenExpr = this.compileExpression(ifThen, ctxType); var ifThenType = this.currentType; var ifElseFlow = outerFlow.fork(); ifElseFlow.inheritNonnullIfFalse(condExpr); + ifElseFlow.inheritNarrowedTypeIfFalse(condExpr); this.currentFlow = ifElseFlow; var ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType); var ifElseType = this.currentType; diff --git a/src/flow.ts b/src/flow.ts index b3d63776ec..c9b2ca5783 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -28,7 +28,6 @@ import { ElementKind, Field, Class, - Global, TypedElement } from "./program"; @@ -91,6 +90,7 @@ import { import { BuiltinNames } from "./builtins"; +import { conditionalNarrowedTypeChecker } from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -240,10 +240,6 @@ export class Flow { thisFieldFlags: Map | null = null; /** type narrow */ narrowedTypes: Map = new Map(); - /** */ - inCondition: bool = false; - narrowedTypesIfTrue: Map = new Map(); - narrowedTypesIfFalse: Map = new Map(); /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; @@ -568,27 +564,6 @@ export class Flow { } } - /** check if an expression is a local variant, return -1 means not, otherwise return local index */ - lookupTypedElementByExpressionRef(expr: ExpressionRef): TypedElement | null { - switch (getExpressionId(expr)) { - case ExpressionId.LocalSet: { - if (!isLocalTee(expr)) break; - return this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - } - case ExpressionId.LocalGet: { - return this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - } - case ExpressionId.GlobalGet: { - let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr)))); - if (global.kind == ElementKind.GLOBAL) { - return global; - } - break; - } - } - return null; - } - /** Looks up the local of the specified name in the current scope. */ lookupLocal(name: string): Local | null { var current: Flow | null = this; @@ -649,8 +624,18 @@ export class Flow { getNarrowedType(element: TypedElement): Type | null { return this.narrowedTypes.has(element) ? changetype(this.narrowedTypes.get(element)) : null; } + private updateNarrowedType(narrowedType: Map): void { + let _key = Map_keys(narrowedType); + for (let i = 0, k = _key.length; i < k; i++) { + let element = _key[i]; + let updatedType = assert(narrowedType.get(element)); + let originType = this.narrowedTypes.has(element) ? assert(this.narrowedTypes.get(element)) : null; + let type = Flow.updateType(originType, updatedType); + this.setNarrowedType(element, type); + } + } - static mergeLocalType(a: Type | null, b: Type | null): Type | null { + static mergeType(a: Type | null, b: Type | null): Type | null { if (a == null || b == null) { return null; } else if (a.isAssignableTo(b)) { @@ -661,7 +646,7 @@ export class Flow { return null; } } - static updateLocalType(origin: Type | null, update: Type | null): Type | null{ + static updateType(origin: Type | null, update: Type | null): Type | null{ if (origin == null) { return update; } else if (update == null) { @@ -675,38 +660,16 @@ export class Flow { return origin; } - startCondition(): void { - this.inCondition = true; + setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { + conditionalNarrowedTypeChecker.setConditionNarrowedType(expr, element, type); } - stopCondition(): void { - this.inCondition = false; + inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { + let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfTrue(condi); + this.updateNarrowedType(condiNarrow); } - switchTrueFalse(): void { - let tmp = this.narrowedTypesIfTrue; - this.narrowedTypesIfTrue = this.narrowedTypesIfFalse; - this.narrowedTypesIfFalse = tmp; - } - setConditionLocalType(element: TypedElement, type: Type | null, condi:bool = true): void { - let potentialLocalTypes = condi ? this.narrowedTypesIfTrue : this.narrowedTypesIfFalse; - if (type == null && potentialLocalTypes.has(element)) { - potentialLocalTypes.delete(element); - } else if (type) { - potentialLocalTypes.set(element, type); - } - } - inheritLocalTypeIfTrue(flow: Flow): void { - let narrowedTypesConditional = flow.narrowedTypesIfTrue; - for (let _key = Map_keys(narrowedTypesConditional), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, Flow.updateLocalType(this.getNarrowedType(key), assert(narrowedTypesConditional.get(key)))); - } - } - inheritLocalTypeIfFalse(flow: Flow): void { - let narrowedTypesConditional = flow.narrowedTypesIfFalse; - for (let _key = Map_keys(narrowedTypesConditional), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, Flow.updateLocalType(this.getNarrowedType(key), assert(narrowedTypesConditional.get(key)))); - } + inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { + let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfFalse(condi); + this.updateNarrowedType(condiNarrow); } /** Initializes `this` field flags. */ @@ -918,7 +881,7 @@ export class Flow { const narrowedTypes = other.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } // field flags do not matter here since there's only INITIALIZED, which can @@ -1027,7 +990,7 @@ export class Flow { const narrowedTypes = right.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } } } else if (rightFlags & FlowFlags.TERMINATES) { @@ -1038,7 +1001,7 @@ export class Flow { const narrowedTypes = left.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } } else { let leftLocalFlags = left.localFlags; @@ -1061,12 +1024,12 @@ export class Flow { const leftNarrowedTypes = left.narrowedTypes; for (let _key = Map_keys(leftNarrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); + this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); } const rightNarrowedTypes = right.narrowedTypes; for (let _key = Map_keys(rightNarrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeLocalType(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); + this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); } } diff --git a/src/narrow.ts b/src/narrow.ts new file mode 100644 index 0000000000..d51ae078d6 --- /dev/null +++ b/src/narrow.ts @@ -0,0 +1,244 @@ +import { Flow } from "./flow"; +import { + BinaryOp, + ExpressionId, + ExpressionRef, + getBinaryLeft, + getBinaryOp, + getBinaryRight, + getExpressionId, + getIfCondition, + getIfFalse, + getIfTrue, + getLocalSetValue, + getUnaryOp, + getUnaryValue, + isConstNonZero, + isConstZero, + isLocalTee, + UnaryOp, +} from "./module"; +import { TypedElement } from "./program"; +import { Type } from "./types"; + +function mergeTypeMap( + main: Map, + other: Map +): void { + let _key = Map_keys(other); + for (let i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + let otherType = assert(other.get(key)); + if (main.has(key)) { + let mainType = assert(main.get(key)); + let mergeType = Flow.mergeType(mainType, otherType); + if (mergeType) { + main.set(key, mergeType); + } else { + main.delete(key); + } + } else { + main.set(key, otherType); + } + } +} + +class ConditionalNarrowedType { + constructor(public expr: ExpressionRef, public type: Type) {} +} + +type ElementType = Map; +class TypeNarrowChecker { + elementMap: ElementType = new Map(); + expressionMap: Map> | null = null; + + private updateMap(): void { + let expressionMap = new Map>(); + let narrowedTypesConditional = this.elementMap; + let _key = Map_keys(narrowedTypesConditional); + for (let i = 0, k = _key.length; i < k; i++) { + let element = _key[i]; + let narrowedTypes = assert(narrowedTypesConditional.get(element)); + for (let i = 0, k = narrowedTypes.length; i < k; i++) { + let expr = narrowedTypes[i].expr; + let type = narrowedTypes[i].type; + if (!expressionMap.has(expr)) { + expressionMap.set(expr, new Map()); + } + let typedMap = assert(expressionMap.get(expr)); + typedMap.set(element, type); + } + } + this.expressionMap = expressionMap; + } + + setConditionNarrowedType( + expr: ExpressionRef, + element: TypedElement, + type: Type | null + ): void { + this.expressionMap = null; // reset map + let potential = this.elementMap; + // case1: add condition type, eg t instanceof B + if (expr > 0 && type) { + if (potential.has(element)) { + let conditionTypes = assert(potential.get(element)); + conditionTypes.push( + new ConditionalNarrowedType(expr, type) + ); + } else { + potential.set(element, [new ConditionalNarrowedType(expr, type)]); + } + } + // case 2: remove condition type, eg t = new A() + // TODO + } + + collectNarrowedTypeIfTrue(expr: ExpressionRef): Map { + let result = new Map(); + if (this.expressionMap == null) this.updateMap(); + let expressionMap = assert(this.expressionMap); + if (expressionMap.has(expr)) { + let typeMap = assert(expressionMap.get(expr)); + mergeTypeMap(result, typeMap); + } + switch (getExpressionId(expr)) { + case ExpressionId.LocalSet: { + if (!isLocalTee(expr)) break; + let subMap = this.collectNarrowedTypeIfTrue(getLocalSetValue(expr)); + mergeTypeMap(result, subMap); + break; + } + case ExpressionId.If: { + let ifFalse = getIfFalse(expr); + if (ifFalse && isConstZero(ifFalse)) { + // Logical AND: (if (condition ifTrue 0)) + // the only way this had become true is if condition and ifTrue are true + let subMapCondi = this.collectNarrowedTypeIfTrue( + getIfCondition(expr) + ); + mergeTypeMap(result, subMapCondi); + let subMapTrue = this.collectNarrowedTypeIfTrue(getIfTrue(expr)); + mergeTypeMap(result, subMapTrue); + } + break; + } + case ExpressionId.Unary: { + switch (getUnaryOp(expr)) { + case UnaryOp.EqzI32: + case UnaryOp.EqzI64: { + let subMap = this.collectNarrowedTypeIfFalse(getUnaryValue(expr)); // !value -> value must have been false + mergeTypeMap(result, subMap); + break; + } + } + break; + } + case ExpressionId.Binary: { + switch (getBinaryOp(expr)) { + case BinaryOp.EqI32: + case BinaryOp.EqI64: { + let left = getBinaryLeft(expr); + let right = getBinaryRight(expr); + if (isConstNonZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true + mergeTypeMap(result, subMap); + } else if (isConstNonZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left); // left == TRUE -> left must have been true + mergeTypeMap(result, subMap); + } + break; + } + case BinaryOp.NeI32: + case BinaryOp.NeI64: { + let left = getBinaryLeft(expr); + let right = getBinaryRight(expr); + if (isConstZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true + mergeTypeMap(result, subMap); + } else if (isConstZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left); // TRUE == right -> right must have been true + mergeTypeMap(result, subMap); + } + break; + } + } + break; + } + } + return result; + } + + collectNarrowedTypeIfFalse(expr: ExpressionRef): Map { + let result = new Map(); + if (this.expressionMap == null) this.updateMap(); + let expressionMap = assert(this.expressionMap); + if (expressionMap.has(expr)) { + let typeMap = assert(expressionMap.get(expr)); + mergeTypeMap(result, typeMap); + } + switch (getExpressionId(expr)) { + case ExpressionId.Unary: { + switch (getUnaryOp(expr)) { + case UnaryOp.EqzI32: + case UnaryOp.EqzI64: { + let subMap = this.collectNarrowedTypeIfTrue(getUnaryValue(expr)); // !value -> value must have been true + mergeTypeMap(result, subMap); + break; + } + } + break; + } + case ExpressionId.If: { + let ifTrue = getIfTrue(expr); + let ifFalse = getIfFalse(expr); + if (ifFalse && isConstNonZero(ifTrue)) { + // Logical OR: (if (condition 1 ifFalse)) + // the only way this had become false is if condition and ifFalse are false + let subMapCondi = this.collectNarrowedTypeIfFalse( + getIfCondition(expr) + ); + let subMapFalse = this.collectNarrowedTypeIfFalse(getIfFalse(expr)); + mergeTypeMap(result, subMapCondi); + mergeTypeMap(result, subMapFalse); + } + break; + } + case ExpressionId.Binary: { + switch (getBinaryOp(expr)) { + // remember: we want to know how the _entire_ expression became FALSE (!) + case BinaryOp.EqI32: + case BinaryOp.EqI64: { + let left = getBinaryLeft(expr); + let right = getBinaryRight(expr); + if (isConstZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right); // !(FALSE == right) -> right must have been true + mergeTypeMap(result, subMap); + } else if (isConstZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left); // !(left == FALSE) -> left must have been true + mergeTypeMap(result, subMap); + } + break; + } + case BinaryOp.NeI32: + case BinaryOp.NeI64: { + let left = getBinaryLeft(expr); + let right = getBinaryRight(expr); + if (isConstNonZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right); // !(TRUE != right) -> right must have been true + mergeTypeMap(result, subMap); + } else if (isConstNonZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left); // !(left != TRUE) -> left must have been true + mergeTypeMap(result, subMap); + } + break; + } + } + break; + } + } + return result; + } +} + +export let conditionalNarrowedTypeChecker = new TypeNarrowChecker(); diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index e6b94483bc..30f2992c67 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -2,6 +2,7 @@ "asc_flags": [ ], "stderr": [ + "TS2339: Property 'value' does not exist on type 'typenarrow-error/A", "TS2339: Property 'foo' does not exist on type 'typenarrow-error/A", "EOF" ] diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index f52fc5ab54..72fc7af0bf 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -1,13 +1,19 @@ class A {} class B extends A { + value: i32; foo(): void {} } let t = new A(); if (t instanceof B) { - t.foo(); t = new A(); + // TS2339: Property 'value' does not exist on type 'typenarrow-error/A + t.value; +} + +if (t instanceof B || true) { + // TS2339: Property 'foo' does not exist on type 'typenarrow-error/A t.foo(); } -ERROR("EOF"); \ No newline at end of file +ERROR("EOF"); diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index 7e40511176..667c2e72fb 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -24,10 +24,10 @@ (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) (global $typenarrow/t (mut i32) (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 416)) - (global $~lib/memory/__data_end i32 (i32.const 460)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16844)) - (global $~lib/memory/__heap_base i32 (i32.const 16844)) + (global $~lib/rt/__rtti_base i32 (i32.const 448)) + (global $~lib/memory/__data_end i32 (i32.const 500)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16884)) + (global $~lib/memory/__heap_base i32 (i32.const 16884)) (memory $0 1) (data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") (data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") @@ -37,9 +37,10 @@ (data (i32.const 268) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") (data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 416) "\05\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00") - (table $0 1 1 funcref) - (elem $0 (i32.const 1)) + (data (i32.const 412) "\1c\00\00\00\00\00\00\00\00\00\00\00\05\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 448) "\06\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $typenarrow/B#foo) (export "test" (func $typenarrow/test)) (export "memory" (memory $0)) (start $~start) @@ -2217,30 +2218,47 @@ call $~lib/rt/itcms/__visit end ) + (func $~lib/function/Function<%28this:typenarrow/B%29=>void>#__visit (param $0 i32) (param $1 i32) + local.get $0 + i32.load offset=4 + local.get $1 + call $~lib/rt/itcms/__visit + ) + (func $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/function/Function<%28this:typenarrow/B%29=>void>#__visit + ) (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) block $invalid - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid + block $~lib/function/Function<%28this:typenarrow/B%29=>void> + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $invalid + end + return end return end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit return end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit return end return end + local.get $0 + local.get $1 + call $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit return end unreachable @@ -2253,8 +2271,8 @@ global.get $~lib/memory/__data_end i32.lt_s if - i32.const 16864 i32.const 16912 + i32.const 16960 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -2315,6 +2333,65 @@ call $typenarrow/B#foo end global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + i32.const 1 + else + i32.const 0 + end + if + i32.const 432 + drop + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if + i32.const 432 + drop + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + if + nop + else + i32.const 432 + drop + end + global.get $~lib/memory/__stack_pointer i32.const 8 i32.add global.set $~lib/memory/__stack_pointer diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index 862b311f29..25fd890d00 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -18,7 +18,7 @@ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $typenarrow/t (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17868)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17908)) (memory $0 1) (data (i32.const 1036) "<") (data (i32.const 1048) "\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") @@ -30,8 +30,10 @@ (data (i32.const 1304) "\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") (data (i32.const 1372) "<") (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") - (data (i32.const 1440) "\05\00\00\00 \00\00\00\00\00\00\00 ") - (data (i32.const 1468) " \00\00\00\00\00\00\00 \00\00\00\03") + (data (i32.const 1436) "\1c") + (data (i32.const 1448) "\05\00\00\00\08\00\00\00\01") + (data (i32.const 1472) "\06\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1500) " \00\00\00\00\00\00\00 \00\00\00\03") (export "test" (func $typenarrow/test)) (export "memory" (memory $0)) (start $~start) @@ -617,10 +619,10 @@ if unreachable end - i32.const 17872 + i32.const 17920 i32.const 0 i32.store - i32.const 19440 + i32.const 19488 i32.const 0 i32.store loop $for-loop|0 @@ -631,7 +633,7 @@ local.get $0 i32.const 2 i32.shl - i32.const 17872 + i32.const 17920 i32.add i32.const 0 i32.store offset=4 @@ -649,7 +651,7 @@ i32.add i32.const 2 i32.shl - i32.const 17872 + i32.const 17920 i32.add i32.const 0 i32.store offset=96 @@ -667,13 +669,13 @@ br $for-loop|0 end end - i32.const 17872 - i32.const 19444 + i32.const 17920 + i32.const 19492 memory.size i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - i32.const 17872 + i32.const 17920 global.set $~lib/rt/tlsf/ROOT ) (func $~lib/rt/itcms/step (result i32) @@ -758,7 +760,7 @@ local.set $0 loop $while-continue|0 local.get $0 - i32.const 17868 + i32.const 17908 i32.lt_u if local.get $0 @@ -858,7 +860,7 @@ unreachable end local.get $0 - i32.const 17868 + i32.const 17908 i32.lt_u if local.get $0 @@ -881,7 +883,7 @@ i32.const 4 i32.add local.tee $0 - i32.const 17868 + i32.const 17908 i32.ge_u if global.get $~lib/rt/tlsf/ROOT @@ -1005,11 +1007,11 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1484 + i32.const 1524 i32.lt_s if - i32.const 17888 i32.const 17936 + i32.const 17984 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -1030,7 +1032,7 @@ i32.sub i32.load offset=12 local.tee $0 - i32.const 1440 + i32.const 1472 i32.load i32.le_u if @@ -1042,7 +1044,7 @@ local.get $0 i32.const 3 i32.shl - i32.const 1444 + i32.const 1476 i32.add i32.load offset=4 local.tee $0 @@ -1058,32 +1060,42 @@ ) (func $~lib/rt/__visit_members (param $0 i32) block $invalid - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $invalid + block $~lib/function/Function<%28this:typenarrow/B%29=>void> + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $invalid + end + return end return end - return - end - local.get $0 - i32.load - local.tee $0 - if local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit + i32.load + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return end return end return end + local.get $0 + i32.load offset=4 + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end return end unreachable @@ -1095,11 +1107,11 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1484 + i32.const 1524 i32.lt_s if - i32.const 17888 i32.const 17936 + i32.const 17984 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -1111,7 +1123,7 @@ memory.size i32.const 16 i32.shl - i32.const 17868 + i32.const 17908 i32.sub i32.const 1 i32.shr_u @@ -1154,7 +1166,7 @@ i32.sub i32.load offset=12 local.tee $0 - i32.const 1440 + i32.const 1472 i32.load i32.le_u if @@ -1168,7 +1180,7 @@ local.get $0 i32.const 3 i32.shl - i32.const 1444 + i32.const 1476 i32.add i32.load offset=4 local.tee $0 @@ -1186,6 +1198,102 @@ i32.store offset=4 end global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|08 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|08 + end + end + end + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|011 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|011 + end + end + end + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/t + local.tee $0 + i32.store + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|014 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|014 + end + end + end + end + global.get $~lib/memory/__stack_pointer i32.const 8 i32.add global.set $~lib/memory/__stack_pointer @@ -1201,11 +1309,11 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1484 + i32.const 1524 i32.lt_s if - i32.const 17888 i32.const 17936 + i32.const 17984 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -1493,7 +1601,7 @@ if i32.const 0 local.get $1 - i32.const 17868 + i32.const 17908 i32.lt_u local.get $1 i32.load offset=8 @@ -1544,7 +1652,7 @@ i32.const 1 else local.get $0 - i32.const 1440 + i32.const 1472 i32.load i32.gt_u if @@ -1558,7 +1666,7 @@ local.get $0 i32.const 3 i32.shl - i32.const 1444 + i32.const 1476 i32.add i32.load i32.const 32 diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 3032ef703d..fef48ff38f 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -8,6 +8,17 @@ if (t instanceof B) { t.foo(); } +if (t instanceof B && true) { + t.foo; +} +if (true && t instanceof B) { + t.foo; +} +if (!(t instanceof B)) { +} else { + t.foo; +} + export function test(): void { let t = new A(); if (t instanceof B) { From 999ce39e7a909d0e6581e593d5d30c5c37f11c39 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 3 Jul 2022 14:47:35 +0800 Subject: [PATCH 06/55] add testcase --- tests/compiler/typenarrow-error.json | 6 +- tests/compiler/typenarrow-error.ts | 38 +- tests/compiler/typenarrow.debug.wat | 391 +++++++++++++++-- tests/compiler/typenarrow.release.wat | 576 ++++++++++++++++++++++---- tests/compiler/typenarrow.ts | 102 ++++- 5 files changed, 969 insertions(+), 144 deletions(-) diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index 30f2992c67..bbc283eb49 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -2,8 +2,10 @@ "asc_flags": [ ], "stderr": [ - "TS2339: Property 'value' does not exist on type 'typenarrow-error/A", - "TS2339: Property 'foo' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b1' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A", "EOF" ] } diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index 72fc7af0bf..87ba337309 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -1,19 +1,37 @@ class A {} class B extends A { - value: i32; - foo(): void {} + b1: i32; + b2: i32; + b3: i32; +} +class C extends A { + c1: i32; +} + +let value = new A(); +let condi = true; + +// or +if (condi || value instanceof B) { + // TS2339: Property 'b1' does not exist on type 'typenarrow-error/A'. + value.b1; +} + +if (value instanceof B) { + value = new A(); + // TS2339: Property 'b2' does not exist on type 'typenarrow-error/A + value.b2; } -let t = new A(); -if (t instanceof B) { - t = new A(); - // TS2339: Property 'value' does not exist on type 'typenarrow-error/A - t.value; +if (value instanceof B || true) { + // TS2339: Property 'b3' does not exist on type 'typenarrow-error/A + value.b3; } -if (t instanceof B || true) { - // TS2339: Property 'foo' does not exist on type 'typenarrow-error/A - t.foo(); +// incompatibility +if (value instanceof B && value instanceof C) { + // TS2339: Property 'c1' does not exist on type 'typenarrow-error/A + value.c1; } ERROR("EOF"); diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index 667c2e72fb..a8b2a6ecaf 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -23,11 +23,12 @@ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) - (global $typenarrow/t (mut i32) (i32.const 0)) + (global $typenarrow/value (mut i32) (i32.const 0)) + (global $typenarrow/condi (mut i32) (i32.const 1)) (global $~lib/rt/__rtti_base i32 (i32.const 448)) - (global $~lib/memory/__data_end i32 (i32.const 500)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16884)) - (global $~lib/memory/__heap_base i32 (i32.const 16884)) + (global $~lib/memory/__data_end i32 (i32.const 508)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16892)) + (global $~lib/memory/__heap_base i32 (i32.const 16892)) (memory $0 1) (data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") (data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") @@ -38,10 +39,10 @@ (data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 412) "\1c\00\00\00\00\00\00\00\00\00\00\00\05\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 448) "\06\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 448) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\04\00\00\00") (table $0 2 2 funcref) - (elem $0 (i32.const 1) $typenarrow/B#foo) - (export "test" (func $typenarrow/test)) + (elem $0 (i32.const 1) $typenarrow/B#b1) + (export "testlocal" (func $typenarrow/testlocal)) (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/Object#set:nextWithColor (param $0 i32) (param $1 i32) @@ -2153,12 +2154,16 @@ end i32.const 0 ) - (func $typenarrow/B#foo (param $0 i32) + (func $typenarrow/B#b1 (param $0 i32) nop ) - (func $typenarrow/test + (func $typenarrow/B#check (param $0 i32) (result i32) + i32.const 1 + ) + (func $typenarrow/testlocal (local $0 i32) (local $1 i32) + (local $2 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub @@ -2172,19 +2177,159 @@ call $typenarrow/A#constructor local.tee $0 i32.store + i32.const 1 + local.set $1 local.get $0 - local.tee $1 + local.tee $2 i32.eqz if (result i32) i32.const 0 else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if + local.get $0 + call $typenarrow/B#b1 + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + if + nop + else + local.get $0 + call $typenarrow/B#b1 + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) local.get $1 + else + i32.const 0 + end + if + local.get $0 + call $typenarrow/B#b1 + end + local.get $1 + if (result i32) + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + if + local.get $0 + call $typenarrow/B#b1 + end + local.get $1 + if (result i32) + i32.const 1 + else + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + end + if + nop + else + i32.const 432 + drop + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + if (result i32) + i32.const 1 + else + local.get $1 + end + if + nop + else + i32.const 432 + drop + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 i32.const 4 call $~lib/rt/__instanceof end + if (result i32) + local.get $0 + call $typenarrow/B#check + else + i32.const 0 + end if + nop + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + if (result i32) + i32.const 1 + else local.get $0 - call $typenarrow/B#foo + call $typenarrow/B#check + end + if + nop end global.get $~lib/memory/__stack_pointer i32.const 4 @@ -2193,7 +2338,7 @@ ) (func $~lib/rt/__visit_globals (param $0 i32) (local $1 i32) - global.get $typenarrow/t + global.get $typenarrow/value local.tee $1 if local.get $1 @@ -2231,34 +2376,37 @@ ) (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) block $invalid - block $~lib/function/Function<%28this:typenarrow/B%29=>void> - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $invalid + block $typenarrow/C + block $~lib/function/Function<%28this:typenarrow/B%29=>void> + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid + end + return end return end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit return end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit return end return end + local.get $0 + local.get $1 + call $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit return end - local.get $0 - local.get $1 - call $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit return end unreachable @@ -2309,9 +2457,9 @@ global.set $~lib/rt/itcms/fromSpace i32.const 0 call $typenarrow/A#constructor - global.set $typenarrow/t + global.set $typenarrow/value global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -2324,16 +2472,16 @@ call $~lib/rt/__instanceof end if - global.get $typenarrow/t + global.get $typenarrow/value local.set $1 global.get $~lib/memory/__stack_pointer local.get $1 i32.store offset=4 local.get $1 - call $typenarrow/B#foo + call $typenarrow/B#b1 end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -2345,17 +2493,99 @@ i32.const 4 call $~lib/rt/__instanceof end + i32.eqz + if + nop + else + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz if (result i32) - i32.const 1 + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + global.get $typenarrow/condi else i32.const 0 end if + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 + end + global.get $typenarrow/condi + if (result i32) + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + if + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 + end + global.get $typenarrow/condi + if (result i32) + i32.const 1 + else + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + end + if + nop + else i32.const 432 drop end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -2367,12 +2597,47 @@ i32.const 4 call $~lib/rt/__instanceof end + i32.eqz + if (result i32) + i32.const 1 + else + global.get $typenarrow/condi + end if + nop + else i32.const 432 drop end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#check + else + i32.const 0 + end + if + nop + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -2385,11 +2650,61 @@ call $~lib/rt/__instanceof end i32.eqz + if (result i32) + i32.const 1 + else + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#check + end if nop + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 else - i32.const 432 + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 6 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + if + global.get $typenarrow/value + i32.load drop + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 end global.get $~lib/memory/__stack_pointer i32.const 8 diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index 25fd890d00..a72afb43b1 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -17,8 +17,8 @@ (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $typenarrow/t (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17908)) + (global $typenarrow/value (mut i32) (i32.const 0)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17916)) (memory $0 1) (data (i32.const 1036) "<") (data (i32.const 1048) "\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") @@ -32,15 +32,16 @@ (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") (data (i32.const 1436) "\1c") (data (i32.const 1448) "\05\00\00\00\08\00\00\00\01") - (data (i32.const 1472) "\06\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1472) "\07\00\00\00 \00\00\00\00\00\00\00 ") (data (i32.const 1500) " \00\00\00\00\00\00\00 \00\00\00\03") - (export "test" (func $typenarrow/test)) + (data (i32.const 1524) " \00\00\00\04") + (export "testlocal" (func $typenarrow/testlocal)) (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/visitRoots (local $0 i32) (local $1 i32) - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 if local.get $0 @@ -760,7 +761,7 @@ local.set $0 loop $while-continue|0 local.get $0 - i32.const 17908 + i32.const 17916 i32.lt_u if local.get $0 @@ -860,7 +861,7 @@ unreachable end local.get $0 - i32.const 17908 + i32.const 17916 i32.lt_u if local.get $0 @@ -883,7 +884,7 @@ i32.const 4 i32.add local.tee $0 - i32.const 17908 + i32.const 17916 i32.ge_u if global.get $~lib/rt/tlsf/ROOT @@ -1000,14 +1001,15 @@ end end ) - (func $typenarrow/test + (func $typenarrow/testlocal (local $0 i32) + (local $1 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1524 + i32.const 1532 i32.lt_s if i32.const 17936 @@ -1023,11 +1025,11 @@ i32.store local.get $0 call $typenarrow/A#constructor - local.tee $0 + local.tee $1 i32.store - local.get $0 + local.get $1 if - local.get $0 + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1053,6 +1055,174 @@ end end end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|02 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|02 + end + end + end + end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|06 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|06 + end + end + end + end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|010 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|010 + end + end + end + end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|017 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|017 + end + end + end + end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|020 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|020 + end + end + end + end + local.get $1 + if + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|023 + local.get $0 + i32.const 4 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|023 + end + end + end + end global.get $~lib/memory/__stack_pointer i32.const 4 i32.add @@ -1060,54 +1230,60 @@ ) (func $~lib/rt/__visit_members (param $0 i32) block $invalid - block $~lib/function/Function<%28this:typenarrow/B%29=>void> - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $invalid + block $typenarrow/C + block $~lib/function/Function<%28this:typenarrow/B%29=>void> + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid + end + return end return end - return - end - local.get $0 - i32.load - local.tee $0 - if local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit + i32.load + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return end return end return end - return - end - local.get $0 - i32.load offset=4 - local.tee $0 - if local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit + i32.load offset=4 + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return end return end unreachable ) (func $~start + call $start:typenarrow + ) + (func $start:typenarrow (local $0 i32) global.get $~lib/memory/__stack_pointer i32.const 8 i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1524 + i32.const 1532 i32.lt_s if i32.const 17936 @@ -1123,7 +1299,7 @@ memory.size i32.const 16 i32.shl - i32.const 17908 + i32.const 17916 i32.sub i32.const 1 i32.shr_u @@ -1153,9 +1329,9 @@ i32.const 1344 global.set $~lib/rt/itcms/fromSpace call $typenarrow/A#constructor - global.set $typenarrow/t + global.set $typenarrow/value global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -1194,29 +1370,75 @@ end if global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value i32.store offset=4 end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof0 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|02 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof0 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|02 + end + end + i32.const 0 + end + else + i32.const 0 + end if - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|08 - local.get $0 - i32.const 4 - i32.ne - if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof4 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|06 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof4 + drop local.get $0 i32.const 3 i32.shl @@ -1224,13 +1446,64 @@ i32.add i32.load offset=4 local.tee $0 - br_if $do-loop|08 + br_if $do-loop|06 end end + i32.const 0 end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof8 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|010 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof8 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|010 + end + end + i32.const 0 + end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value local.tee $0 i32.store local.get $0 @@ -1244,7 +1517,7 @@ i32.load i32.le_u if - loop $do-loop|011 + loop $do-loop|017 local.get $0 i32.const 4 i32.ne @@ -1256,31 +1529,120 @@ i32.add i32.load offset=4 local.tee $0 - br_if $do-loop|011 + br_if $do-loop|017 end end end end global.get $~lib/memory/__stack_pointer - global.get $typenarrow/t + global.get $typenarrow/value local.tee $0 i32.store local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof18 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|020 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof18 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|020 + end + end + i32.const 0 + end + else + i32.const 0 + end if - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|014 - local.get $0 - i32.const 4 - i32.ne - if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof21 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|023 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof21 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|023 + end + end + i32.const 0 + end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof25 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|027 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof25 + drop local.get $0 i32.const 3 i32.shl @@ -1288,10 +1650,64 @@ i32.add i32.load offset=4 local.tee $0 - br_if $do-loop|014 + br_if $do-loop|027 + end + end + i32.const 0 + end + else + i32.const 0 + end + if (result i32) + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof28 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|030 + i32.const 1 + local.get $0 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof28 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|030 + end end + i32.const 0 end + else + i32.const 0 end + else + i32.const 0 + end + if + global.get $typenarrow/value + local.tee $0 + i32.load + drop + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store offset=4 end global.get $~lib/memory/__stack_pointer i32.const 8 @@ -1309,7 +1725,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1524 + i32.const 1532 i32.lt_s if i32.const 17936 @@ -1601,7 +2017,7 @@ if i32.const 0 local.get $1 - i32.const 17908 + i32.const 17916 i32.lt_u local.get $1 i32.load offset=8 diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index fef48ff38f..a959bf8ef2 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -1,27 +1,101 @@ class A {} + class B extends A { - foo(): void {} + b1(): void {} + check(): bool { + return true; + } +} +class C extends B { + c1: i32; +} + +let value = new A(); +let condi = true; + +// noraml +if (value instanceof B) { + value.b1(); } -let t = new A(); -if (t instanceof B) { - t.foo(); +// not +if (!(value instanceof B)) { +} else { + value.b1(); } -if (t instanceof B && true) { - t.foo; +// and +if (value instanceof B && condi) { + value.b1(); } -if (true && t instanceof B) { - t.foo; +if (condi && value instanceof B) { + value.b1(); } -if (!(t instanceof B)) { + +// or +if (condi || !(value instanceof B)) { } else { - t.foo; + value.b1; } -export function test(): void { - let t = new A(); - if (t instanceof B) { - t.foo(); +if (!(value instanceof B) || condi) { +} else { + value.b1; +} + +// in condition check for logic operator +if (value instanceof B && value.check()) { +} +if (!(value instanceof B) || value.check()) { +} + +// compatibiltiy +if (value instanceof B && value instanceof C) { + value.c1; + value.b1(); +} +// TODO +// if (value instanceof B || value instanceof C) { +// value.b1(); +// } + +export function testlocal(): void { + let value = new A(); + let condi = true; + + // noraml + if (value instanceof B) { + value.b1(); + } + + // not + if (!(value instanceof B)) { + } else { + value.b1(); + } + + // and + if (value instanceof B && condi) { + value.b1(); + } + if (condi && value instanceof B) { + value.b1(); + } + + // or + if (condi || !(value instanceof B)) { + } else { + value.b1; + } + + if (!(value instanceof B) || condi) { + } else { + value.b1; + } + + // in condition check for logic operator + if (value instanceof B && value.check()) { + } + if (!(value instanceof B) || value.check()) { } } From 9bfac7ebd55ac4aaf9829c5e7a63154524cd4f38 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 3 Jul 2022 15:04:25 +0800 Subject: [PATCH 07/55] fix: or operator --- src/narrow.ts | 7 +------ tests/compiler/typenarrow-error.json | 1 + tests/compiler/typenarrow-error.ts | 7 ++++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/narrow.ts b/src/narrow.ts index d51ae078d6..f1edfcad29 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -90,7 +90,7 @@ class TypeNarrowChecker { potential.set(element, [new ConditionalNarrowedType(expr, type)]); } } - // case 2: remove condition type, eg t = new A() + // case 2: remove condition type, eg t = new A() in some condition // TODO } @@ -172,11 +172,6 @@ class TypeNarrowChecker { collectNarrowedTypeIfFalse(expr: ExpressionRef): Map { let result = new Map(); if (this.expressionMap == null) this.updateMap(); - let expressionMap = assert(this.expressionMap); - if (expressionMap.has(expr)) { - let typeMap = assert(expressionMap.get(expr)); - mergeTypeMap(result, typeMap); - } switch (getExpressionId(expr)) { case ExpressionId.Unary: { switch (getUnaryOp(expr)) { diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index bbc283eb49..229bb232f6 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -6,6 +6,7 @@ "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", "EOF" ] } diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index 87ba337309..e2c082b8d9 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -3,6 +3,7 @@ class B extends A { b1: i32; b2: i32; b3: i32; + b4: i32; } class C extends A { c1: i32; @@ -23,7 +24,7 @@ if (value instanceof B) { value.b2; } -if (value instanceof B || true) { +if (value instanceof B || condi) { // TS2339: Property 'b3' does not exist on type 'typenarrow-error/A value.b3; } @@ -34,4 +35,8 @@ if (value instanceof B && value instanceof C) { value.c1; } +// TS2339: Property 'b4' does not exist on type 'typenarrow-error/A +if (value instanceof B || value.b4) { +} + ERROR("EOF"); From 2ccd59b56d06e830b82891d255506f47445332e0 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 3 Jul 2022 19:09:58 +0800 Subject: [PATCH 08/55] rename function --- src/flow.ts | 39 +++------------ src/narrow.ts | 71 +++++++++++++++++---------- tests/compiler/typenarrow.debug.wat | 16 +----- tests/compiler/typenarrow.release.wat | 44 +---------------- tests/compiler/typenarrow.ts | 4 +- 5 files changed, 56 insertions(+), 118 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index c9b2ca5783..87fb13056f 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -90,7 +90,7 @@ import { import { BuiltinNames } from "./builtins"; -import { conditionalNarrowedTypeChecker } from "./narrow"; +import { conditionalNarrowedTypeChecker, typeAnd, typeOr } from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -630,35 +630,10 @@ export class Flow { let element = _key[i]; let updatedType = assert(narrowedType.get(element)); let originType = this.narrowedTypes.has(element) ? assert(this.narrowedTypes.get(element)) : null; - let type = Flow.updateType(originType, updatedType); + let type = typeOr(originType, updatedType); this.setNarrowedType(element, type); } } - - static mergeType(a: Type | null, b: Type | null): Type | null { - if (a == null || b == null) { - return null; - } else if (a.isAssignableTo(b)) { - return a; - } else if (b.isAssignableTo(a)) { - return b; - } else { - return null; - } - } - static updateType(origin: Type | null, update: Type | null): Type | null{ - if (origin == null) { - return update; - } else if (update == null) { - return origin; - } else if (update.isAssignableTo(origin)) { - return update; - } else if (origin.isAssignableTo(update)) { - return origin; - } - assert(false, "cannot update type"); - return origin; - } setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { conditionalNarrowedTypeChecker.setConditionNarrowedType(expr, element, type); @@ -881,7 +856,7 @@ export class Flow { const narrowedTypes = other.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } // field flags do not matter here since there's only INITIALIZED, which can @@ -990,7 +965,7 @@ export class Flow { const narrowedTypes = right.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } } } else if (rightFlags & FlowFlags.TERMINATES) { @@ -1001,7 +976,7 @@ export class Flow { const narrowedTypes = left.narrowedTypes; for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); + this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); } } else { let leftLocalFlags = left.localFlags; @@ -1024,12 +999,12 @@ export class Flow { const leftNarrowedTypes = left.narrowedTypes; for (let _key = Map_keys(leftNarrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); + this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); } const rightNarrowedTypes = right.narrowedTypes; for (let _key = Map_keys(rightNarrowedTypes), i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - this.setNarrowedType(key, Flow.mergeType(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); + this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); } } diff --git a/src/narrow.ts b/src/narrow.ts index f1edfcad29..caa6f0d57c 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -1,4 +1,3 @@ -import { Flow } from "./flow"; import { BinaryOp, ExpressionId, @@ -21,24 +20,47 @@ import { import { TypedElement } from "./program"; import { Type } from "./types"; +export function typeAnd(a: Type | null, b: Type | null): Type | null { + if (a == null || b == null) { + return null; + } else if (a.isAssignableTo(b)) { + return b; + } else if (b.isAssignableTo(a)) { + return a; + } else { + return null; + } +} +export function typeOr(a: Type | null, b: Type | null): Type | null { + if (a == null) { + return b; + } else if (b == null) { + return a; + } else if (a.isAssignableTo(b)) { + // a extends b + return a; + } else if (b.isAssignableTo(a)) { + // b extends a + return b; + } else { + return null; + } +} + function mergeTypeMap( - main: Map, - other: Map + mainMap: Map, + otherMap: Map ): void { - let _key = Map_keys(other); + let _key = Map_keys(otherMap); for (let i = 0, k = _key.length; i < k; i++) { let key = _key[i]; - let otherType = assert(other.get(key)); - if (main.has(key)) { - let mainType = assert(main.get(key)); - let mergeType = Flow.mergeType(mainType, otherType); - if (mergeType) { - main.set(key, mergeType); - } else { - main.delete(key); - } + let main = mainMap.has(key) ? assert(mainMap.get(key)) : null; + let other = assert(otherMap.get(key)); + let merged = typeOr(main, other); + if (merged) { + mainMap.set(key, merged); } else { - main.set(key, otherType); + mainMap.delete(key); } } } @@ -53,6 +75,7 @@ class TypeNarrowChecker { expressionMap: Map> | null = null; private updateMap(): void { + // TODO could update when change, maintain 2 map both let expressionMap = new Map>(); let narrowedTypesConditional = this.elementMap; let _key = Map_keys(narrowedTypesConditional); @@ -83,9 +106,7 @@ class TypeNarrowChecker { if (expr > 0 && type) { if (potential.has(element)) { let conditionTypes = assert(potential.get(element)); - conditionTypes.push( - new ConditionalNarrowedType(expr, type) - ); + conditionTypes.push(new ConditionalNarrowedType(expr, type)); } else { potential.set(element, [new ConditionalNarrowedType(expr, type)]); } @@ -114,12 +135,10 @@ class TypeNarrowChecker { if (ifFalse && isConstZero(ifFalse)) { // Logical AND: (if (condition ifTrue 0)) // the only way this had become true is if condition and ifTrue are true - let subMapCondi = this.collectNarrowedTypeIfTrue( - getIfCondition(expr) - ); - mergeTypeMap(result, subMapCondi); + let subMap = this.collectNarrowedTypeIfTrue(getIfCondition(expr)); let subMapTrue = this.collectNarrowedTypeIfTrue(getIfTrue(expr)); - mergeTypeMap(result, subMapTrue); + mergeTypeMap(subMap, subMapTrue); + mergeTypeMap(result, subMap); } break; } @@ -190,12 +209,10 @@ class TypeNarrowChecker { if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMapCondi = this.collectNarrowedTypeIfFalse( - getIfCondition(expr) - ); + let subMap = this.collectNarrowedTypeIfFalse(getIfCondition(expr)); let subMapFalse = this.collectNarrowedTypeIfFalse(getIfFalse(expr)); - mergeTypeMap(result, subMapCondi); - mergeTypeMap(result, subMapFalse); + mergeTypeMap(subMap, subMapFalse); + mergeTypeMap(result, subMap); } break; } diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index a8b2a6ecaf..5cb27b644b 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -2674,23 +2674,11 @@ i32.const 0 else local.get $0 - i32.const 4 + i32.const 6 call $~lib/rt/__instanceof end if (result i32) - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 6 - call $~lib/rt/__instanceof - end + i32.const 1 else i32.const 0 end diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index a72afb43b1..1830bfa32c 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -1639,7 +1639,7 @@ loop $do-loop|027 i32.const 1 local.get $0 - i32.const 4 + i32.const 6 i32.eq br_if $__inlined_func$~lib/rt/__instanceof25 drop @@ -1658,48 +1658,6 @@ else i32.const 0 end - if (result i32) - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof28 (result i32) - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|030 - i32.const 1 - local.get $0 - i32.const 6 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof28 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|030 - end - end - i32.const 0 - end - else - i32.const 0 - end - else - i32.const 0 - end if global.get $typenarrow/value local.tee $0 diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index a959bf8ef2..0e722dd4c5 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -50,11 +50,11 @@ if (!(value instanceof B) || value.check()) { } // compatibiltiy -if (value instanceof B && value instanceof C) { +if (value instanceof C && value instanceof B) { value.c1; value.b1(); } -// TODO + // if (value instanceof B || value instanceof C) { // value.b1(); // } From 3c7d0dd20d59cc4c3f8b830c50e3dd86c89087a3 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 3 Jul 2022 23:17:44 +0800 Subject: [PATCH 09/55] support logic and / or in condition --- src/narrow.ts | 51 ++++++-- tests/compiler/typenarrow.debug.wat | 81 ++++++++++++ tests/compiler/typenarrow.release.wat | 170 ++++++++++++++++++++++++++ tests/compiler/typenarrow.ts | 11 +- 4 files changed, 297 insertions(+), 16 deletions(-) diff --git a/src/narrow.ts b/src/narrow.ts index caa6f0d57c..99b6e5d6b7 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -47,20 +47,35 @@ export function typeOr(a: Type | null, b: Type | null): Type | null { } } +enum Mode { + AND, + OR, +} + function mergeTypeMap( - mainMap: Map, - otherMap: Map + aMap: Map, + bMap: Map, + mode: Mode = Mode.OR ): void { - let _key = Map_keys(otherMap); - for (let i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - let main = mainMap.has(key) ? assert(mainMap.get(key)) : null; - let other = assert(otherMap.get(key)); - let merged = typeOr(main, other); - if (merged) { - mainMap.set(key, merged); + let bKeys = Map_keys(bMap); + if (mode == Mode.AND) { + let aKeys = Map_keys(aMap); + for(let i = 0, k = aKeys.length; i < k; i++) { + let akey = aKeys[i]; + if (!bKeys.includes(akey)) { + aMap.delete(akey); + } + } + } + for (let i = 0, k = bKeys.length; i < k; i++) { + let key = bKeys[i]; + let aType = aMap.has(key) ? assert(aMap.get(key)) : null; + let bType = assert(bMap.get(key)); + let mergedType = mode == Mode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + if (mergedType) { + aMap.set(key, mergedType); } else { - mainMap.delete(key); + aMap.delete(key); } } } @@ -131,15 +146,25 @@ class TypeNarrowChecker { break; } case ExpressionId.If: { + let condition = getIfCondition(expr); + let ifTrue = getIfTrue(expr); let ifFalse = getIfFalse(expr); if (ifFalse && isConstZero(ifFalse)) { // Logical AND: (if (condition ifTrue 0)) // the only way this had become true is if condition and ifTrue are true - let subMap = this.collectNarrowedTypeIfTrue(getIfCondition(expr)); - let subMapTrue = this.collectNarrowedTypeIfTrue(getIfTrue(expr)); + let subMap = this.collectNarrowedTypeIfTrue(condition); + let subMapTrue = this.collectNarrowedTypeIfTrue(ifTrue); mergeTypeMap(subMap, subMapTrue); mergeTypeMap(result, subMap); } + if (ifFalse && isConstNonZero(ifTrue)) { + // Logical OR: (if (condition 1 ifFalse)) + // the only way this had become false is if condition and ifFalse are false + let subMap = this.collectNarrowedTypeIfTrue(condition); + let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse); + mergeTypeMap(subMap, subMapFalse, Mode.AND); + mergeTypeMap(result, subMap); + } break; } case ExpressionId.Unary: { diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index 5cb27b644b..1f197ca433 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -2695,6 +2695,87 @@ call $typenarrow/B#b1 end global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + i32.const 1 + else + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 6 + call $~lib/rt/__instanceof + end + end + if + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $0 + i32.const 6 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + i32.eqz + if + nop + else + global.get $typenarrow/value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $typenarrow/B#b1 + end + global.get $~lib/memory/__stack_pointer i32.const 8 i32.add global.set $~lib/memory/__stack_pointer diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index 1830bfa32c..1efc140887 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -1668,6 +1668,176 @@ i32.store offset=4 end global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof29 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|031 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof29 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|031 + end + end + i32.const 0 + end + else + i32.const 0 + end + if (result i32) + i32.const 1 + else + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof32 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|034 + i32.const 1 + local.get $0 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof32 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|034 + end + end + i32.const 0 + end + else + i32.const 0 + end + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof36 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|038 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof36 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|038 + end + end + i32.const 0 + end + else + i32.const 0 + end + if (result i32) + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof39 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|041 + i32.const 1 + local.get $0 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof39 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|041 + end + end + i32.const 0 + end + else + i32.const 0 + end + else + i32.const 0 + end + if + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + i32.store offset=4 + end + global.get $~lib/memory/__stack_pointer i32.const 8 i32.add global.set $~lib/memory/__stack_pointer diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 0e722dd4c5..0d7eca9956 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -55,9 +55,14 @@ if (value instanceof C && value instanceof B) { value.b1(); } -// if (value instanceof B || value instanceof C) { -// value.b1(); -// } +if (value instanceof B || value instanceof C) { + value.b1(); +} + +if (!(value instanceof B && value instanceof C)) { +} else { + value.b1(); +} export function testlocal(): void { let value = new A(); From bbc3fe1a38d18fdc3519267d69635ffb9226d704 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Mon, 4 Jul 2022 09:14:01 +0800 Subject: [PATCH 10/55] test: add test case for #2359 --- tests/compiler/nullable.json | 1 + tests/compiler/nullable.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/tests/compiler/nullable.json b/tests/compiler/nullable.json index fdef0a1a3b..4aaa25669e 100644 --- a/tests/compiler/nullable.json +++ b/tests/compiler/nullable.json @@ -2,6 +2,7 @@ "asc_flags": [ ], "stderr": [ + "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", "EOF" ] diff --git a/tests/compiler/nullable.ts b/tests/compiler/nullable.ts index 3cffa68d67..87de994605 100644 --- a/tests/compiler/nullable.ts +++ b/tests/compiler/nullable.ts @@ -4,4 +4,14 @@ function notNullable(a: Example): void {} notNullable(null); +export function test(): void { + let value: Example | null = new Example(); + if (value != null) { + // value = null; + true && (value = null); + // "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", + notNullable(value); + } +} + ERROR("EOF"); From 86f7bcecfe45c580269faf3061dfbfbc68ab2435 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Mon, 4 Jul 2022 09:48:17 +0800 Subject: [PATCH 11/55] fix: Fix `||` and `&&` will not inherit branch status --- src/compiler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler.ts b/src/compiler.ts index 98d9a42084..504c5616c4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -4597,6 +4597,7 @@ export class Compiler extends DiagnosticEmitter { } this.currentType = leftType; } + this.currentFlow.inheritBranch(rightFlow); break; } case Token.BAR_BAR: { // left || right -> ((t = left) ? t : right) @@ -4661,6 +4662,7 @@ export class Compiler extends DiagnosticEmitter { } this.currentType = leftType; } + this.currentFlow.inheritBranch(rightFlow); break; } default: { From 4ea7f9e1ce6b722aea97a2edcbbca87a8874cc4b Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Mon, 4 Jul 2022 09:52:18 +0800 Subject: [PATCH 12/55] update test --- tests/compiler/nullable.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/compiler/nullable.ts b/tests/compiler/nullable.ts index 87de994605..9c2eda2744 100644 --- a/tests/compiler/nullable.ts +++ b/tests/compiler/nullable.ts @@ -4,7 +4,7 @@ function notNullable(a: Example): void {} notNullable(null); -export function test(): void { +function test(): void { let value: Example | null = new Example(); if (value != null) { // value = null; @@ -14,4 +14,6 @@ export function test(): void { } } +test(); + ERROR("EOF"); From 58d53a4115a70e391e6d63e5f0e17fee2564c318 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Mon, 4 Jul 2022 14:01:58 +0800 Subject: [PATCH 13/55] distinguish condi kind --- src/compiler.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 504c5616c4..2780b163ae 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -4567,6 +4567,7 @@ export class Compiler extends DiagnosticEmitter { } } this.currentFlow = flow; + this.currentFlow.inheritBranch(rightFlow, condKind); this.currentType = Type.bool; } else { @@ -4596,8 +4597,8 @@ export class Compiler extends DiagnosticEmitter { flow.freeTempLocal(tempLocal); } this.currentType = leftType; + this.currentFlow.inheritBranch(rightFlow); } - this.currentFlow.inheritBranch(rightFlow); break; } case Token.BAR_BAR: { // left || right -> ((t = left) ? t : right) @@ -4631,6 +4632,13 @@ export class Compiler extends DiagnosticEmitter { expr = module.if(leftExpr, module.i32(1), rightExpr); } } + let inheritCondi = + condKind == ConditionKind.TRUE + ? ConditionKind.FALSE + : condKind == ConditionKind.FALSE + ? ConditionKind.TRUE + : ConditionKind.UNKNOWN; + flow.inheritBranch(rightFlow, inheritCondi); this.currentFlow = flow; this.currentType = Type.bool; @@ -4638,6 +4646,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = this.currentType; rightFlow.freeScopedLocals(); + flow.inheritBranch(rightFlow); this.currentFlow = flow; // simplify if copying left is trivial @@ -4662,7 +4671,6 @@ export class Compiler extends DiagnosticEmitter { } this.currentType = leftType; } - this.currentFlow.inheritBranch(rightFlow); break; } default: { From e6b9291b1c2ea3e7fb8922853c87dfc1898d4b6d Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Mon, 4 Jul 2022 19:51:24 +0800 Subject: [PATCH 14/55] update --- src/compiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 2780b163ae..9d5b6c4b6e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -4566,14 +4566,15 @@ export class Compiler extends DiagnosticEmitter { expr = module.if(leftExpr, rightExpr, module.i32(0)); } } + flow.inheritBranch(rightFlow, condKind); this.currentFlow = flow; - this.currentFlow.inheritBranch(rightFlow, condKind); this.currentType = Type.bool; } else { rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = this.currentType; rightFlow.freeScopedLocals(); + flow.inheritBranch(rightFlow); this.currentFlow = flow; // simplify if copying left is trivial @@ -4597,7 +4598,6 @@ export class Compiler extends DiagnosticEmitter { flow.freeTempLocal(tempLocal); } this.currentType = leftType; - this.currentFlow.inheritBranch(rightFlow); } break; } From a9d22eaffa53fba51375cba17e6659e1be90a5d7 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 00:59:24 +0800 Subject: [PATCH 15/55] refactory: use class replace map --- src/flow.ts | 68 ++++++++---------------------- src/narrow.ts | 114 ++++++++++++++++++++++++++++---------------------- 2 files changed, 83 insertions(+), 99 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 87fb13056f..421653a12b 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -90,7 +90,7 @@ import { import { BuiltinNames } from "./builtins"; -import { conditionalNarrowedTypeChecker, typeAnd, typeOr } from "./narrow"; +import { conditionalNarrowedTypeChecker, NarrowedTypeMap, TypeMergeMode } from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -239,7 +239,7 @@ export class Flow { /** Field flags on `this`. Constructors only. */ thisFieldFlags: Map | null = null; /** type narrow */ - narrowedTypes: Map = new Map(); + narrowedTypes: NarrowedTypeMap = new NarrowedTypeMap(); /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; @@ -315,11 +315,7 @@ export class Flow { branch.breakLabel = this.breakLabel; } branch.localFlags = this.localFlags.slice(); - const narrowedTypes = this.narrowedTypes; - for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - branch.setNarrowedType(key, assert(narrowedTypes.get(key))); - } + branch.narrowedTypes = this.narrowedTypes.clone(); if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); branch.thisFieldFlags = uniqueMap(thisFieldFlags); @@ -615,24 +611,15 @@ export class Flow { } setNarrowedType(element: TypedElement, type: Type | null): void { - if (type == null && this.narrowedTypes.has(element)) { - this.narrowedTypes.delete(element); + const typeMap = this.narrowedTypes.typeMap; + if (type == null && typeMap.has(element)) { + typeMap.delete(element); } else if (type) { - this.narrowedTypes.set(element, type); + typeMap.set(element, type); } } getNarrowedType(element: TypedElement): Type | null { - return this.narrowedTypes.has(element) ? changetype(this.narrowedTypes.get(element)) : null; - } - private updateNarrowedType(narrowedType: Map): void { - let _key = Map_keys(narrowedType); - for (let i = 0, k = _key.length; i < k; i++) { - let element = _key[i]; - let updatedType = assert(narrowedType.get(element)); - let originType = this.narrowedTypes.has(element) ? assert(this.narrowedTypes.get(element)) : null; - let type = typeOr(originType, updatedType); - this.setNarrowedType(element, type); - } + return this.narrowedTypes.typeMap.has(element) ? changetype(this.narrowedTypes.typeMap.get(element)) : null; } setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { @@ -640,11 +627,11 @@ export class Flow { } inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfTrue(condi); - this.updateNarrowedType(condiNarrow); + this.narrowedTypes.merge(condiNarrow); } inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfFalse(condi); - this.updateNarrowedType(condiNarrow); + this.narrowedTypes.merge(condiNarrow); } /** Initializes `this` field flags. */ @@ -852,12 +839,9 @@ export class Flow { ); } - // local types - const narrowedTypes = other.narrowedTypes; - for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); - } + // narrowed types + + this.narrowedTypes.merge(other.narrowedTypes, TypeMergeMode.AND); // field flags do not matter here since there's only INITIALIZED, which can // only be set if it has been observed prior to entering the branch. @@ -962,22 +946,14 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } - const narrowedTypes = right.narrowedTypes; - for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); - } + this.narrowedTypes.merge(right.narrowedTypes, TypeMergeMode.AND); } } else if (rightFlags & FlowFlags.TERMINATES) { let leftLocalFlags = left.localFlags; for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } - const narrowedTypes = left.narrowedTypes; - for (let _key = Map_keys(narrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(narrowedTypes.get(key)))); - } + this.narrowedTypes.merge(left.narrowedTypes, TypeMergeMode.AND); } else { let leftLocalFlags = left.localFlags; let numLeftLocalFlags = leftLocalFlags.length; @@ -995,17 +971,9 @@ export class Flow { ); } - // local types - const leftNarrowedTypes = left.narrowedTypes; - for (let _key = Map_keys(leftNarrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(leftNarrowedTypes.get(key)))); - } - const rightNarrowedTypes = right.narrowedTypes; - for (let _key = Map_keys(rightNarrowedTypes), i = 0, k = _key.length; i < k; i++) { - let key = _key[i]; - this.setNarrowedType(key, typeAnd(this.getNarrowedType(key), assert(rightNarrowedTypes.get(key)))); - } + // narrow type + this.narrowedTypes = right.narrowedTypes.clone(); + this.narrowedTypes.merge(left.narrowedTypes, TypeMergeMode.AND); } // field flags (currently only INITIALIZED, so can simplify) diff --git a/src/narrow.ts b/src/narrow.ts index 99b6e5d6b7..4cdab4517e 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -47,35 +47,51 @@ export function typeOr(a: Type | null, b: Type | null): Type | null { } } -enum Mode { +export enum TypeMergeMode { AND, OR, } -function mergeTypeMap( - aMap: Map, - bMap: Map, - mode: Mode = Mode.OR -): void { - let bKeys = Map_keys(bMap); - if (mode == Mode.AND) { - let aKeys = Map_keys(aMap); - for(let i = 0, k = aKeys.length; i < k; i++) { - let akey = aKeys[i]; - if (!bKeys.includes(akey)) { - aMap.delete(akey); - } +export class NarrowedTypeMap { + typeMap: Map; + constructor() { + this.typeMap = new Map(); + } + clone(): NarrowedTypeMap { + let map = this.typeMap; + let other = new NarrowedTypeMap(); + let _key = Map_keys(map); + for (let i = 0, k = _key.length; i < k; i++) { + let key = _key[i]; + let value = assert(map.get(key)); + other.typeMap.set(key, value); } + return other; } - for (let i = 0, k = bKeys.length; i < k; i++) { - let key = bKeys[i]; - let aType = aMap.has(key) ? assert(aMap.get(key)) : null; - let bType = assert(bMap.get(key)); - let mergedType = mode == Mode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); - if (mergedType) { - aMap.set(key, mergedType); - } else { - aMap.delete(key); + merge(other: NarrowedTypeMap, mode: TypeMergeMode = TypeMergeMode.OR): void { + let aMap = this.typeMap; + let bMap = other.typeMap; + let bKeys = Map_keys(bMap); + if (mode == TypeMergeMode.AND) { + let aKeys = Map_keys(aMap); + for (let i = 0, k = aKeys.length; i < k; i++) { + let akey = aKeys[i]; + if (!bKeys.includes(akey)) { + aMap.delete(akey); + } + } + } + for (let i = 0, k = bKeys.length; i < k; i++) { + let key = bKeys[i]; + let aType = aMap.has(key) ? assert(aMap.get(key)) : null; + let bType = assert(bMap.get(key)); + let mergedType = + mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + if (mergedType) { + aMap.set(key, mergedType); + } else { + aMap.delete(key); + } } } } @@ -87,11 +103,11 @@ class ConditionalNarrowedType { type ElementType = Map; class TypeNarrowChecker { elementMap: ElementType = new Map(); - expressionMap: Map> | null = null; + expressionMap: Map | null = null; private updateMap(): void { // TODO could update when change, maintain 2 map both - let expressionMap = new Map>(); + let expressionMap = new Map(); let narrowedTypesConditional = this.elementMap; let _key = Map_keys(narrowedTypesConditional); for (let i = 0, k = _key.length; i < k; i++) { @@ -101,10 +117,10 @@ class TypeNarrowChecker { let expr = narrowedTypes[i].expr; let type = narrowedTypes[i].type; if (!expressionMap.has(expr)) { - expressionMap.set(expr, new Map()); + expressionMap.set(expr, new NarrowedTypeMap()); } let typedMap = assert(expressionMap.get(expr)); - typedMap.set(element, type); + typedMap.typeMap.set(element, type); } } this.expressionMap = expressionMap; @@ -130,19 +146,19 @@ class TypeNarrowChecker { // TODO } - collectNarrowedTypeIfTrue(expr: ExpressionRef): Map { - let result = new Map(); + collectNarrowedTypeIfTrue(expr: ExpressionRef): NarrowedTypeMap { + let result = new NarrowedTypeMap(); if (this.expressionMap == null) this.updateMap(); let expressionMap = assert(this.expressionMap); if (expressionMap.has(expr)) { let typeMap = assert(expressionMap.get(expr)); - mergeTypeMap(result, typeMap); + result.merge(typeMap); } switch (getExpressionId(expr)) { case ExpressionId.LocalSet: { if (!isLocalTee(expr)) break; let subMap = this.collectNarrowedTypeIfTrue(getLocalSetValue(expr)); - mergeTypeMap(result, subMap); + result.merge(subMap); break; } case ExpressionId.If: { @@ -154,16 +170,16 @@ class TypeNarrowChecker { // the only way this had become true is if condition and ifTrue are true let subMap = this.collectNarrowedTypeIfTrue(condition); let subMapTrue = this.collectNarrowedTypeIfTrue(ifTrue); - mergeTypeMap(subMap, subMapTrue); - mergeTypeMap(result, subMap); + subMap.merge(subMapTrue); + result.merge(subMap); } if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false let subMap = this.collectNarrowedTypeIfTrue(condition); let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse); - mergeTypeMap(subMap, subMapFalse, Mode.AND); - mergeTypeMap(result, subMap); + subMap.merge(subMapFalse, TypeMergeMode.AND); + result.merge(subMap); } break; } @@ -172,7 +188,7 @@ class TypeNarrowChecker { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { let subMap = this.collectNarrowedTypeIfFalse(getUnaryValue(expr)); // !value -> value must have been false - mergeTypeMap(result, subMap); + result.merge(subMap); break; } } @@ -186,10 +202,10 @@ class TypeNarrowChecker { let right = getBinaryRight(expr); if (isConstNonZero(left)) { let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } else if (isConstNonZero(right)) { let subMap = this.collectNarrowedTypeIfTrue(left); // left == TRUE -> left must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } break; } @@ -199,10 +215,10 @@ class TypeNarrowChecker { let right = getBinaryRight(expr); if (isConstZero(left)) { let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } else if (isConstZero(right)) { let subMap = this.collectNarrowedTypeIfTrue(left); // TRUE == right -> right must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } break; } @@ -213,8 +229,8 @@ class TypeNarrowChecker { return result; } - collectNarrowedTypeIfFalse(expr: ExpressionRef): Map { - let result = new Map(); + collectNarrowedTypeIfFalse(expr: ExpressionRef): NarrowedTypeMap { + let result = new NarrowedTypeMap(); if (this.expressionMap == null) this.updateMap(); switch (getExpressionId(expr)) { case ExpressionId.Unary: { @@ -222,7 +238,7 @@ class TypeNarrowChecker { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { let subMap = this.collectNarrowedTypeIfTrue(getUnaryValue(expr)); // !value -> value must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); break; } } @@ -236,8 +252,8 @@ class TypeNarrowChecker { // the only way this had become false is if condition and ifFalse are false let subMap = this.collectNarrowedTypeIfFalse(getIfCondition(expr)); let subMapFalse = this.collectNarrowedTypeIfFalse(getIfFalse(expr)); - mergeTypeMap(subMap, subMapFalse); - mergeTypeMap(result, subMap); + subMap.merge(subMapFalse); + result.merge(subMap); } break; } @@ -250,10 +266,10 @@ class TypeNarrowChecker { let right = getBinaryRight(expr); if (isConstZero(left)) { let subMap = this.collectNarrowedTypeIfTrue(right); // !(FALSE == right) -> right must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } else if (isConstZero(right)) { let subMap = this.collectNarrowedTypeIfTrue(left); // !(left == FALSE) -> left must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } break; } @@ -263,10 +279,10 @@ class TypeNarrowChecker { let right = getBinaryRight(expr); if (isConstNonZero(left)) { let subMap = this.collectNarrowedTypeIfTrue(right); // !(TRUE != right) -> right must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } else if (isConstNonZero(right)) { let subMap = this.collectNarrowedTypeIfTrue(left); // !(left != TRUE) -> left must have been true - mergeTypeMap(result, subMap); + result.merge(subMap); } break; } From 53d5811d7433e7f89fb73f752cc840d35eb98cce Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 12:46:46 +0800 Subject: [PATCH 16/55] fix: assign in condition does not reset element type correctly --- src/compiler.ts | 13 +++++++++---- src/flow.ts | 6 +++--- src/narrow.ts | 43 ++++++++++++++++++++++++++++++------------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 88e438155b..20fcfca9e0 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5958,10 +5958,8 @@ export class Compiler extends DiagnosticEmitter { assert(targetType != Type.void); var valueExpr = this.compileExpression(valueExpression, targetType); var valueType = this.currentType; - if (target instanceof TypedElement) { - flow.setNarrowedType(target, null); - } - return this.makeAssignment( + + let assignmentExpression = this.makeAssignment( target, this.convertExpression(valueExpr, valueType, targetType, false, valueExpression), valueType, @@ -5970,6 +5968,13 @@ export class Compiler extends DiagnosticEmitter { elementExpression, contextualType != Type.void ); + + if (target instanceof TypedElement) { + let typedTarget = target; + flow.setNarrowedType(typedTarget, null); + flow.setConditionNarrowedType(assignmentExpression, typedTarget, null); + } + return assignmentExpression; } /** Makes an assignment expression or block, assigning a value to a target. */ diff --git a/src/flow.ts b/src/flow.ts index 421653a12b..a980c357b5 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -611,15 +611,15 @@ export class Flow { } setNarrowedType(element: TypedElement, type: Type | null): void { - const typeMap = this.narrowedTypes.typeMap; - if (type == null && typeMap.has(element)) { + const typeMap = this.narrowedTypes; + if (type == null && typeMap.get(element) == null) { typeMap.delete(element); } else if (type) { typeMap.set(element, type); } } getNarrowedType(element: TypedElement): Type | null { - return this.narrowedTypes.typeMap.has(element) ? changetype(this.narrowedTypes.typeMap.get(element)) : null; + return this.narrowedTypes.get(element); } setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { diff --git a/src/narrow.ts b/src/narrow.ts index 4cdab4517e..9a8d0454fe 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -53,9 +53,20 @@ export enum TypeMergeMode { } export class NarrowedTypeMap { - typeMap: Map; - constructor() { - this.typeMap = new Map(); + private typeMap: Map = new Map(); + static readonly INVAILD_TYPE: Type = Type.void; + get(key: TypedElement): Type | null { + if (this.typeMap.has(key)) { + return assert(this.typeMap.get(key)); + } else { + return null; + } + } + set(key: TypedElement, value: Type): void { + this.typeMap.set(key, value); + } + delete(key: TypedElement): bool { + return this.typeMap.delete(key); } clone(): NarrowedTypeMap { let map = this.typeMap; @@ -100,9 +111,8 @@ class ConditionalNarrowedType { constructor(public expr: ExpressionRef, public type: Type) {} } -type ElementType = Map; class TypeNarrowChecker { - elementMap: ElementType = new Map(); + elementMap: Map = new Map(); expressionMap: Map | null = null; private updateMap(): void { @@ -120,7 +130,7 @@ class TypeNarrowChecker { expressionMap.set(expr, new NarrowedTypeMap()); } let typedMap = assert(expressionMap.get(expr)); - typedMap.typeMap.set(element, type); + typedMap.set(element, type); } } this.expressionMap = expressionMap; @@ -132,18 +142,25 @@ class TypeNarrowChecker { type: Type | null ): void { this.expressionMap = null; // reset map - let potential = this.elementMap; + let elementMap = this.elementMap; // case1: add condition type, eg t instanceof B if (expr > 0 && type) { - if (potential.has(element)) { - let conditionTypes = assert(potential.get(element)); - conditionTypes.push(new ConditionalNarrowedType(expr, type)); - } else { - potential.set(element, [new ConditionalNarrowedType(expr, type)]); + if (!elementMap.has(element)) { + elementMap.set(element, new Array()); } + let conditionTypes = assert(elementMap.get(element)); + conditionTypes.push(new ConditionalNarrowedType(expr, type)); } // case 2: remove condition type, eg t = new A() in some condition - // TODO + if (expr > 0 && type == null) { + if (!elementMap.has(element)) { + elementMap.set(element, new Array()); + } + let conditionTypes = assert(elementMap.get(element)); + conditionTypes.push( + new ConditionalNarrowedType(expr, NarrowedTypeMap.INVAILD_TYPE) + ); + } } collectNarrowedTypeIfTrue(expr: ExpressionRef): NarrowedTypeMap { From df62468635825d5a0d4d2212d6a82357cc5a5738 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 13:17:38 +0800 Subject: [PATCH 17/55] refactory: clean useless code --- src/flow.ts | 2 +- src/narrow.ts | 65 ++++++++++++++------------------------------------- 2 files changed, 18 insertions(+), 49 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index a980c357b5..73d601393b 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -612,7 +612,7 @@ export class Flow { setNarrowedType(element: TypedElement, type: Type | null): void { const typeMap = this.narrowedTypes; - if (type == null && typeMap.get(element) == null) { + if (type == null && typeMap.get(element) != null) { typeMap.delete(element); } else if (type) { typeMap.set(element, type); diff --git a/src/narrow.ts b/src/narrow.ts index 9a8d0454fe..4745f84123 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -54,10 +54,13 @@ export enum TypeMergeMode { export class NarrowedTypeMap { private typeMap: Map = new Map(); - static readonly INVAILD_TYPE: Type = Type.void; get(key: TypedElement): Type | null { if (this.typeMap.has(key)) { - return assert(this.typeMap.get(key)); + let type = assert(this.typeMap.get(key)); + if (type == Type.void) { + return null; + } + return type; } else { return null; } @@ -107,65 +110,32 @@ export class NarrowedTypeMap { } } -class ConditionalNarrowedType { - constructor(public expr: ExpressionRef, public type: Type) {} -} - class TypeNarrowChecker { - elementMap: Map = new Map(); - expressionMap: Map | null = null; - - private updateMap(): void { - // TODO could update when change, maintain 2 map both - let expressionMap = new Map(); - let narrowedTypesConditional = this.elementMap; - let _key = Map_keys(narrowedTypesConditional); - for (let i = 0, k = _key.length; i < k; i++) { - let element = _key[i]; - let narrowedTypes = assert(narrowedTypesConditional.get(element)); - for (let i = 0, k = narrowedTypes.length; i < k; i++) { - let expr = narrowedTypes[i].expr; - let type = narrowedTypes[i].type; - if (!expressionMap.has(expr)) { - expressionMap.set(expr, new NarrowedTypeMap()); - } - let typedMap = assert(expressionMap.get(expr)); - typedMap.set(element, type); - } - } - this.expressionMap = expressionMap; - } + expressionMap: Map = new Map(); setConditionNarrowedType( expr: ExpressionRef, element: TypedElement, type: Type | null ): void { - this.expressionMap = null; // reset map - let elementMap = this.elementMap; - // case1: add condition type, eg t instanceof B - if (expr > 0 && type) { - if (!elementMap.has(element)) { - elementMap.set(element, new Array()); + let expressionMap = this.expressionMap; + if (expr > 0) { + if (!expressionMap.has(expr)) { + expressionMap.set(expr, new NarrowedTypeMap()); } - let conditionTypes = assert(elementMap.get(element)); - conditionTypes.push(new ConditionalNarrowedType(expr, type)); - } - // case 2: remove condition type, eg t = new A() in some condition - if (expr > 0 && type == null) { - if (!elementMap.has(element)) { - elementMap.set(element, new Array()); + let narrowMap = assert(expressionMap.get(expr)); + if (type) { + // case 1: add condition type, eg t instanceof B + narrowMap.set(element, type); + } else { + // case 2: remove condition type, eg t = new A() in some condition + narrowMap.set(element, Type.void); } - let conditionTypes = assert(elementMap.get(element)); - conditionTypes.push( - new ConditionalNarrowedType(expr, NarrowedTypeMap.INVAILD_TYPE) - ); } } collectNarrowedTypeIfTrue(expr: ExpressionRef): NarrowedTypeMap { let result = new NarrowedTypeMap(); - if (this.expressionMap == null) this.updateMap(); let expressionMap = assert(this.expressionMap); if (expressionMap.has(expr)) { let typeMap = assert(expressionMap.get(expr)); @@ -248,7 +218,6 @@ class TypeNarrowChecker { collectNarrowedTypeIfFalse(expr: ExpressionRef): NarrowedTypeMap { let result = new NarrowedTypeMap(); - if (this.expressionMap == null) this.updateMap(); switch (getExpressionId(expr)) { case ExpressionId.Unary: { switch (getUnaryOp(expr)) { From 5cac0925f31c9453ab2d2ee0a4bf464573c219d1 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 13:21:31 +0800 Subject: [PATCH 18/55] test: add assign in condition testcase --- tests/compiler/typenarrow-error.json | 1 + tests/compiler/typenarrow-error.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index 229bb232f6..1e04c02c17 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -7,6 +7,7 @@ "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b5' does not exist on type 'typenarrow-error/A", "EOF" ] } diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index e2c082b8d9..20b517eb98 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -39,4 +39,9 @@ if (value instanceof B && value instanceof C) { if (value instanceof B || value.b4) { } +if (value instanceof B && (value = new A())) { + // TS2339: Property 'b5' does not exist on type 'typenarrow-error/A + value.b5; +} + ERROR("EOF"); From 67ce5bdf0d59bb69a88f9db163bed3df542ac02d Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 13:43:03 +0800 Subject: [PATCH 19/55] fix: reduce memory usage --- src/flow.ts | 11 +++++++---- src/narrow.ts | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 73d601393b..b754846cdb 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -90,7 +90,7 @@ import { import { BuiltinNames } from "./builtins"; -import { conditionalNarrowedTypeChecker, NarrowedTypeMap, TypeMergeMode } from "./narrow"; +import { NarrowedTypeMap, TypeMergeMode, TypeNarrowChecker } from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -240,6 +240,8 @@ export class Flow { thisFieldFlags: Map | null = null; /** type narrow */ narrowedTypes: NarrowedTypeMap = new NarrowedTypeMap(); + /** conditional type narrow, eg if (condi) */ + conditionalNarrowedType: TypeNarrowChecker = new TypeNarrowChecker(); /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; @@ -316,6 +318,7 @@ export class Flow { } branch.localFlags = this.localFlags.slice(); branch.narrowedTypes = this.narrowedTypes.clone(); + branch.conditionalNarrowedType = this.conditionalNarrowedType; if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); branch.thisFieldFlags = uniqueMap(thisFieldFlags); @@ -623,14 +626,14 @@ export class Flow { } setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { - conditionalNarrowedTypeChecker.setConditionNarrowedType(expr, element, type); + this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); } inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { - let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfTrue(condi); + let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi); this.narrowedTypes.merge(condiNarrow); } inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { - let condiNarrow = conditionalNarrowedTypeChecker.collectNarrowedTypeIfFalse(condi); + let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi); this.narrowedTypes.merge(condiNarrow); } diff --git a/src/narrow.ts b/src/narrow.ts index 4745f84123..74143cb7da 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -110,7 +110,7 @@ export class NarrowedTypeMap { } } -class TypeNarrowChecker { +export class TypeNarrowChecker { expressionMap: Map = new Map(); setConditionNarrowedType( @@ -280,4 +280,3 @@ class TypeNarrowChecker { } } -export let conditionalNarrowedTypeChecker = new TypeNarrowChecker(); From 450d4901155c682f337a6f4d5835d7a49c919a63 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 23:14:16 +0800 Subject: [PATCH 20/55] refactory: add invertedCondition --- src/compiler.ts | 11 +++-------- src/flow.ts | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 20fcfca9e0..1189c3f9ea 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -98,7 +98,8 @@ import { LocalFlags, FieldFlags, ConditionKind, - findUsedLocals + findUsedLocals, + invertedCondition } from "./flow"; import { @@ -4642,13 +4643,7 @@ export class Compiler extends DiagnosticEmitter { expr = module.if(leftExpr, module.i32(1), rightExpr); } } - let inheritCondi = - condKind == ConditionKind.TRUE - ? ConditionKind.FALSE - : condKind == ConditionKind.FALSE - ? ConditionKind.TRUE - : ConditionKind.UNKNOWN; - flow.inheritBranch(rightFlow, inheritCondi); + flow.inheritBranch(rightFlow, invertedCondition(condKind)); this.currentFlow = flow; this.currentType = Type.bool; diff --git a/src/flow.ts b/src/flow.ts index b754846cdb..bda44af75b 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -192,6 +192,12 @@ export const enum ConditionKind { FALSE } +export function invertedCondition(kind: ConditionKind): ConditionKind { + if (kind == ConditionKind.TRUE) return ConditionKind.FALSE; + if (kind == ConditionKind.FALSE) return ConditionKind.TRUE; + return kind; +} + /** A control flow evaluator. */ export class Flow { From b0f3aef2fd4852247b42df164bf16b3b9979000e Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 23:18:49 +0800 Subject: [PATCH 21/55] test: add unknown condition testcase --- tests/compiler/typenarrow-error.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index 20b517eb98..d34b92d577 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -4,6 +4,8 @@ class B extends A { b2: i32; b3: i32; b4: i32; + b5: i32; + b6: i32; } class C extends A { c1: i32; @@ -44,4 +46,12 @@ if (value instanceof B && (value = new A())) { value.b5; } +declare function externalBool(): bool; + +// externalBool may return `true` while `value` isn't instanceof C +if (externalBool() || value instanceof B) { + // TS2339: Property 'b1' does not exist on type 'typenarrow-error/A'. + value.b6; +} + ERROR("EOF"); From 3ae97bde2dba8adae82880fa9528d44e56caed14 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 5 Jul 2022 23:26:58 +0800 Subject: [PATCH 22/55] test: add assign testcase --- tests/compiler/typenarrow.debug.wat | 24 ++++++++++++++++++++++++ tests/compiler/typenarrow.release.wat | 21 +++++++++++++++++++++ tests/compiler/typenarrow.ts | 6 ++++++ 3 files changed, 51 insertions(+) diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index 1f197ca433..e0dad05922 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -2479,6 +2479,10 @@ i32.store offset=4 local.get $1 call $typenarrow/B#b1 + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -2504,6 +2508,10 @@ i32.store offset=4 local.get $1 call $typenarrow/B#b1 + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -2531,6 +2539,10 @@ i32.store offset=4 local.get $1 call $typenarrow/B#b1 + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $typenarrow/condi if (result i32) @@ -2558,6 +2570,10 @@ i32.store offset=4 local.get $1 call $typenarrow/B#b1 + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $typenarrow/condi if (result i32) @@ -2583,6 +2599,10 @@ else i32.const 432 drop + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -2608,6 +2628,10 @@ else i32.const 432 drop + global.get $~lib/memory/__stack_pointer + global.get $typenarrow/value + local.tee $0 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index 1efc140887..0df86decda 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -1278,6 +1278,7 @@ ) (func $start:typenarrow (local $0 i32) + (local $1 i32) global.get $~lib/memory/__stack_pointer i32.const 8 i32.sub @@ -1370,8 +1371,13 @@ end if global.get $~lib/memory/__stack_pointer + local.tee $0 global.get $typenarrow/value + local.tee $1 i32.store offset=4 + local.get $0 + local.get $1 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -1413,8 +1419,13 @@ end if global.get $~lib/memory/__stack_pointer + local.tee $0 global.get $typenarrow/value + local.tee $1 i32.store offset=4 + local.get $0 + local.get $1 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -1456,8 +1467,13 @@ end if global.get $~lib/memory/__stack_pointer + local.tee $0 global.get $typenarrow/value + local.tee $1 i32.store offset=4 + local.get $0 + local.get $1 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value @@ -1499,8 +1515,13 @@ end if global.get $~lib/memory/__stack_pointer + local.tee $0 global.get $typenarrow/value + local.tee $1 i32.store offset=4 + local.get $0 + local.get $1 + i32.store end global.get $~lib/memory/__stack_pointer global.get $typenarrow/value diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 0d7eca9956..257cac0bb9 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -16,31 +16,37 @@ let condi = true; // noraml if (value instanceof B) { value.b1(); + let t: B = value; } // not if (!(value instanceof B)) { } else { value.b1(); + let t: B = value; } // and if (value instanceof B && condi) { value.b1(); + let t: B = value; } if (condi && value instanceof B) { value.b1(); + let t: B = value; } // or if (condi || !(value instanceof B)) { } else { value.b1; + let t: B = value; } if (!(value instanceof B) || condi) { } else { value.b1; + let t: B = value; } // in condition check for logic operator From 3277d930f7a4643e90e344154d6ae409a4cf8799 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 6 Jul 2022 00:03:13 +0800 Subject: [PATCH 23/55] update testcase --- tests/compiler/typenarrow-error.json | 1 + tests/compiler/typenarrow-error.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index 1e04c02c17..1d74cbdee9 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -8,6 +8,7 @@ "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b5' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b6' does not exist on type 'typenarrow-error/A", "EOF" ] } diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index d34b92d577..a8154f398f 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -50,7 +50,7 @@ declare function externalBool(): bool; // externalBool may return `true` while `value` isn't instanceof C if (externalBool() || value instanceof B) { - // TS2339: Property 'b1' does not exist on type 'typenarrow-error/A'. + // TS2339: Property 'b6' does not exist on type 'typenarrow-error/A' value.b6; } From c5d94fa860aca8c93d7c5547741c02be90f1f5da Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 6 Jul 2022 07:02:43 +0800 Subject: [PATCH 24/55] Update src/narrow.ts Co-authored-by: Max Graey --- src/narrow.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/narrow.ts b/src/narrow.ts index 74143cb7da..40d7c73673 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -99,8 +99,9 @@ export class NarrowedTypeMap { let key = bKeys[i]; let aType = aMap.has(key) ? assert(aMap.get(key)) : null; let bType = assert(bMap.get(key)); - let mergedType = - mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + let mergedType = mode == TypeMergeMode.OR + ? typeOr(aType, bType) + : typeAnd(aType, bType); if (mergedType) { aMap.set(key, mergedType); } else { From b1ea921dae22ce9ee9f87585c5378b36c2c75c11 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 6 Jul 2022 07:03:12 +0800 Subject: [PATCH 25/55] Update src/narrow.ts Co-authored-by: Max Graey --- src/narrow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/narrow.ts b/src/narrow.ts index 40d7c73673..385096f4e1 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -90,7 +90,7 @@ export class NarrowedTypeMap { let aKeys = Map_keys(aMap); for (let i = 0, k = aKeys.length; i < k; i++) { let akey = aKeys[i]; - if (!bKeys.includes(akey)) { + if (!bMap.has(akey)) { aMap.delete(akey); } } From 8979ac81bc1e8d05ef50eef3882e203507f00929 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Wed, 6 Jul 2022 13:15:42 +0800 Subject: [PATCH 26/55] lazy init narrowedType --- src/flow.ts | 66 +++++++++++++++++++++++++++++++++++++++------------ src/narrow.ts | 14 ++++------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index bda44af75b..170d06a594 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -245,7 +245,7 @@ export class Flow { /** Field flags on `this`. Constructors only. */ thisFieldFlags: Map | null = null; /** type narrow */ - narrowedTypes: NarrowedTypeMap = new NarrowedTypeMap(); + narrowedTypes: NarrowedTypeMap | null = null; /** conditional type narrow, eg if (condi) */ conditionalNarrowedType: TypeNarrowChecker = new TypeNarrowChecker(); @@ -323,7 +323,8 @@ export class Flow { branch.breakLabel = this.breakLabel; } branch.localFlags = this.localFlags.slice(); - branch.narrowedTypes = this.narrowedTypes.clone(); + let narrowedTypes = this.narrowedTypes; + branch.narrowedTypes = narrowedTypes ? narrowedTypes.clone(): null; branch.conditionalNarrowedType = this.conditionalNarrowedType; if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); @@ -620,15 +621,22 @@ export class Flow { } setNarrowedType(element: TypedElement, type: Type | null): void { - const typeMap = this.narrowedTypes; - if (type == null && typeMap.get(element) != null) { - typeMap.delete(element); + if (this.narrowedTypes == null) { + this.narrowedTypes = new NarrowedTypeMap(); + } + let narrowedTypes = assert(this.narrowedTypes); + if (type == null && narrowedTypes.get(element) != null) { + narrowedTypes.delete(element); } else if (type) { - typeMap.set(element, type); + narrowedTypes.set(element, type); } } getNarrowedType(element: TypedElement): Type | null { - return this.narrowedTypes.get(element); + if (this.narrowedTypes == null) { + this.narrowedTypes = new NarrowedTypeMap(); + } + let narrowedTypes = assert(this.narrowedTypes); + return narrowedTypes.get(element); } setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { @@ -636,11 +644,23 @@ export class Flow { } inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi); - this.narrowedTypes.merge(condiNarrow); + if (condiNarrow.size != 0) { + if (this.narrowedTypes == null) { + this.narrowedTypes = new NarrowedTypeMap(); + } + let narrowedTypes = assert(this.narrowedTypes); + narrowedTypes.merge(condiNarrow); + } } inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi); - this.narrowedTypes.merge(condiNarrow); + if (condiNarrow.size != 0) { + if (this.narrowedTypes == null) { + this.narrowedTypes = new NarrowedTypeMap(); + } + let narrowedTypes = assert(this.narrowedTypes); + narrowedTypes.merge(condiNarrow); + } } /** Initializes `this` field flags. */ @@ -849,8 +869,11 @@ export class Flow { } // narrowed types - - this.narrowedTypes.merge(other.narrowedTypes, TypeMergeMode.AND); + let thisNarrowedTypes = this.narrowedTypes; + let otherNarrowedTypes = other.narrowedTypes; + if (thisNarrowedTypes && otherNarrowedTypes) { + thisNarrowedTypes.merge(otherNarrowedTypes, TypeMergeMode.AND); + } // field flags do not matter here since there's only INITIALIZED, which can // only be set if it has been observed prior to entering the branch. @@ -955,14 +978,22 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } - this.narrowedTypes.merge(right.narrowedTypes, TypeMergeMode.AND); + let thisNarrowedTypes = this.narrowedTypes; + let rightNarrowedTypes = right.narrowedTypes; + if (thisNarrowedTypes && rightNarrowedTypes) { + thisNarrowedTypes.merge(rightNarrowedTypes, TypeMergeMode.AND); + } } } else if (rightFlags & FlowFlags.TERMINATES) { let leftLocalFlags = left.localFlags; for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } - this.narrowedTypes.merge(left.narrowedTypes, TypeMergeMode.AND); + let thisNarrowedTypes = this.narrowedTypes; + let leftNarrowedTypes = left.narrowedTypes; + if (thisNarrowedTypes && leftNarrowedTypes) { + thisNarrowedTypes.merge(leftNarrowedTypes, TypeMergeMode.AND); + } } else { let leftLocalFlags = left.localFlags; let numLeftLocalFlags = leftLocalFlags.length; @@ -981,8 +1012,13 @@ export class Flow { } // narrow type - this.narrowedTypes = right.narrowedTypes.clone(); - this.narrowedTypes.merge(left.narrowedTypes, TypeMergeMode.AND); + let leftNarrowedTypes = left.narrowedTypes; + let rightNarrowedTypes = right.narrowedTypes; + if (leftNarrowedTypes && rightNarrowedTypes) { + let narrowedTypes = rightNarrowedTypes.clone(); + narrowedTypes.merge(leftNarrowedTypes, TypeMergeMode.AND); + this.narrowedTypes = narrowedTypes; + } } // field flags (currently only INITIALIZED, so can simplify) diff --git a/src/narrow.ts b/src/narrow.ts index 385096f4e1..72153fa972 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -54,6 +54,9 @@ export enum TypeMergeMode { export class NarrowedTypeMap { private typeMap: Map = new Map(); + get size(): i32 { + return this.typeMap.size; + } get(key: TypedElement): Type | null { if (this.typeMap.has(key)) { let type = assert(this.typeMap.get(key)); @@ -99,9 +102,7 @@ export class NarrowedTypeMap { let key = bKeys[i]; let aType = aMap.has(key) ? assert(aMap.get(key)) : null; let bType = assert(bMap.get(key)); - let mergedType = mode == TypeMergeMode.OR - ? typeOr(aType, bType) - : typeAnd(aType, bType); + let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); if (mergedType) { aMap.set(key, mergedType); } else { @@ -114,11 +115,7 @@ export class NarrowedTypeMap { export class TypeNarrowChecker { expressionMap: Map = new Map(); - setConditionNarrowedType( - expr: ExpressionRef, - element: TypedElement, - type: Type | null - ): void { + setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { let expressionMap = this.expressionMap; if (expr > 0) { if (!expressionMap.has(expr)) { @@ -280,4 +277,3 @@ export class TypeNarrowChecker { return result; } } - From 2abc9a4a81be4e30c1fb3386e598626406c3f3e5 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 10:16:27 +0800 Subject: [PATCH 27/55] refactory: merge nonnull flag and narrowedTypeMap --- src/compiler.ts | 68 ++- src/flow.ts | 271 ++-------- src/narrow.ts | 201 ++++++-- tests/compiler/class-overloading.debug.wat | 199 ++++++-- tests/compiler/class-overloading.release.wat | 490 ++++++++----------- tests/compiler/for.debug.wat | 9 +- tests/compiler/for.release.wat | 12 +- tests/compiler/std/math.release.wat | 36 +- tests/compiler/while.debug.wat | 12 +- tests/compiler/while.release.wat | 12 +- 10 files changed, 664 insertions(+), 646 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 9540cc6f8d..a1422369b4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2556,7 +2556,6 @@ export class Compiler extends DiagnosticEmitter { // Compile the body assuming the condition turned out true var bodyFlow = flow.fork(); - bodyFlow.inheritNonnullIfTrue(condExpr); bodyFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); @@ -2702,8 +2701,8 @@ export class Compiler extends DiagnosticEmitter { var thenStmts = new Array(); var thenFlow = flow.fork(); this.currentFlow = thenFlow; - thenFlow.inheritNonnullIfTrue(condExpr); thenFlow.inheritNarrowedTypeIfTrue(condExpr); + if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); } else { @@ -2721,7 +2720,6 @@ export class Compiler extends DiagnosticEmitter { let elseStmts = new Array(); let elseFlow = flow.fork(); this.currentFlow = elseFlow; - elseFlow.inheritNonnullIfFalse(condExpr); elseFlow.inheritNarrowedTypeIfFalse(condExpr); if (ifFalse.kind == NodeKind.BLOCK) { this.compileStatements((ifFalse).statements, false, elseStmts); @@ -2740,12 +2738,8 @@ export class Compiler extends DiagnosticEmitter { module.flatten(elseStmts) ); } else { + flow.inheritNarrowedTypeIfFalse(condExpr); flow.inheritBranch(thenFlow); - flow.inheritNonnullIfFalse(condExpr, - thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS) - ? null // thenFlow terminates: just inherit - : thenFlow // must become nonnull in thenFlow otherwise - ); if (thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS)) { flow.inheritNarrowedTypeIfFalse(condExpr); } @@ -2980,6 +2974,7 @@ export class Compiler extends DiagnosticEmitter { let name = declaration.name.text; let type: Type | null = null; let initExpr: ExpressionRef = 0; + let initType: Type | null = null; // Resolve type if annotated let typeNode = declaration.type; @@ -3002,6 +2997,8 @@ export class Compiler extends DiagnosticEmitter { ); pendingElements.delete(dummy); flow.freeScopedDummyLocal(name); + + initType = this.currentType; } // Otherwise infer type from initializer @@ -3021,6 +3018,7 @@ export class Compiler extends DiagnosticEmitter { continue; } type = this.currentType; + initType = this.currentType; // Error if there's neither a type nor an initializer } else { @@ -3143,7 +3141,7 @@ export class Compiler extends DiagnosticEmitter { } if (initExpr) { initializers.push( - this.makeLocalAssignment(local, initExpr, type, false) + this.makeLocalAssignment(local, initExpr, initType ? initType : type, false) ); } else { // no need to assign zero @@ -3243,7 +3241,6 @@ export class Compiler extends DiagnosticEmitter { // Compile the body assuming the condition turned out true var bodyFlow = flow.fork(); - bodyFlow.inheritNonnullIfTrue(condExpr); bodyFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); @@ -4552,7 +4549,6 @@ export class Compiler extends DiagnosticEmitter { let rightFlow = flow.fork(); this.currentFlow = rightFlow; - rightFlow.inheritNonnullIfTrue(leftExpr); rightFlow.inheritNarrowedTypeIfTrue(leftExpr); // simplify if only interested in true or false @@ -4599,7 +4595,7 @@ export class Compiler extends DiagnosticEmitter { } else { let tempLocal = flow.getTempLocal(leftType); if (!flow.canOverflow(leftExpr, leftType)) flow.setLocalFlag(tempLocal.index, LocalFlags.WRAPPED); - if (flow.isNonnull(leftExpr, leftType)) flow.setLocalFlag(tempLocal.index, LocalFlags.NONNULL); + if (flow.isNonnull(leftExpr, leftType)) flow.setNarrowedType(tempLocal, leftType.nonNullableType); expr = module.if( this.makeIsTrueish(module.local_tee(tempLocal.index, leftExpr, leftType.isManaged), leftType, left), rightExpr, @@ -4619,7 +4615,6 @@ export class Compiler extends DiagnosticEmitter { let rightFlow = flow.fork(); this.currentFlow = rightFlow; - rightFlow.inheritNonnullIfFalse(leftExpr); rightFlow.inheritNarrowedTypeIfFalse(leftExpr); // simplify if only interested in true or false @@ -4666,7 +4661,7 @@ export class Compiler extends DiagnosticEmitter { } else { let temp = flow.getTempLocal(leftType); if (!flow.canOverflow(leftExpr, leftType)) flow.setLocalFlag(temp.index, LocalFlags.WRAPPED); - if (flow.isNonnull(leftExpr, leftType)) flow.setLocalFlag(temp.index, LocalFlags.NONNULL); + if (flow.isNonnull(leftExpr, leftType)) flow.setNarrowedType(temp, leftType.nonNullableType); expr = module.if( this.makeIsTrueish(module.local_tee(temp.index, leftExpr, leftType.isManaged), leftType, left), module.local_get(temp.index, leftType.toRef()), @@ -5963,12 +5958,6 @@ export class Compiler extends DiagnosticEmitter { elementExpression, contextualType != Type.void ); - - if (target instanceof TypedElement) { - let typedTarget = target; - flow.setNarrowedType(typedTarget, null); - flow.setConditionNarrowedType(assignmentExpression, typedTarget, null); - } return assignmentExpression; } @@ -6178,16 +6167,13 @@ export class Compiler extends DiagnosticEmitter { /** Whether to tee the value. */ tee: bool ): ExpressionRef { + let expressionRef: ExpressionRef; var module = this.module; var flow = this.currentFlow; var type = local.type; assert(type != Type.void); var localIndex = local.index; - if (type.isNullableReference) { - if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) flow.setLocalFlag(localIndex, LocalFlags.NONNULL); - else flow.unsetLocalFlag(localIndex, LocalFlags.NONNULL); - } flow.setLocalFlag(localIndex, LocalFlags.INITIALIZED); if (type.isShortIntegerValue) { if (!flow.canOverflow(valueExpr, type)) flow.setLocalFlag(localIndex, LocalFlags.WRAPPED); @@ -6195,11 +6181,20 @@ export class Compiler extends DiagnosticEmitter { } if (tee) { // local = value this.currentType = type; - return module.local_tee(localIndex, valueExpr, type.isManaged); + expressionRef = module.local_tee(localIndex, valueExpr, type.isManaged); } else { // void(local = value) this.currentType = Type.void; - return module.local_set(localIndex, valueExpr, type.isManaged); + expressionRef = module.local_set(localIndex, valueExpr, type.isManaged); } + if (type.isReference) { + let narrowedType = valueType; + if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) { + narrowedType = valueType.nonNullableType; + } + flow.setNarrowedType(local, narrowedType); + flow.setConditionNarrowedType(expressionRef, local, null); + } + return expressionRef; } /** Makes an assignment to a global. */ @@ -6213,24 +6208,28 @@ export class Compiler extends DiagnosticEmitter { /** Whether to tee the value. */ tee: bool ): ExpressionRef { + let expressionRef: ExpressionRef; var module = this.module; var type = global.type; + var flow = this.currentFlow; assert(type != Type.void); var typeRef = type.toRef(); - valueExpr = this.ensureSmallIntegerWrap(valueExpr, type); // globals must be wrapped if (tee) { // (global = value), global this.currentType = type; - return module.block(null, [ + expressionRef = module.block(null, [ module.global_set(global.internalName, valueExpr), module.global_get(global.internalName, typeRef) ], typeRef); } else { // global = value this.currentType = Type.void; - return module.global_set(global.internalName, + expressionRef = module.global_set(global.internalName, valueExpr ); } + flow.setNarrowedType(global, valueType); + flow.setConditionNarrowedType(expressionRef, global, null); + return expressionRef; } /** Makes an assignment to a field. */ @@ -6791,7 +6790,7 @@ export class Compiler extends DiagnosticEmitter { findUsedLocals(paramExpr, usedLocals); // inlining is aware of wrap/nonnull states: if (!previousFlow.canOverflow(paramExpr, paramType)) flow.setLocalFlag(argumentLocal.index, LocalFlags.WRAPPED); - if (flow.isNonnull(paramExpr, paramType)) flow.setLocalFlag(argumentLocal.index, LocalFlags.NONNULL); + if (flow.isNonnull(paramExpr, paramType) && paramType.isNullableReference) flow.setNarrowedType(argumentLocal, paramType.nonNullableType); body.unshift( module.local_set(argumentLocal.index, paramExpr, paramType.isManaged) ); @@ -7801,9 +7800,6 @@ export class Compiler extends DiagnosticEmitter { } let localIndex = local.index; assert(localIndex >= 0); - if (localType.isNullableReference && flow.isLocalFlag(localIndex, LocalFlags.NONNULL, false)) { - localType = localType.nonNullableType; - } this.currentType = localType; if (target.parent != flow.parentFunction) { @@ -9296,14 +9292,12 @@ export class Compiler extends DiagnosticEmitter { var outerFlow = this.currentFlow; var ifThenFlow = outerFlow.fork(); - ifThenFlow.inheritNonnullIfTrue(condExpr); - ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); + ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = ifThenFlow; var ifThenExpr = this.compileExpression(ifThen, ctxType); var ifThenType = this.currentType; var ifElseFlow = outerFlow.fork(); - ifElseFlow.inheritNonnullIfFalse(condExpr); ifElseFlow.inheritNarrowedTypeIfFalse(condExpr); this.currentFlow = ifElseFlow; var ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType); @@ -10621,7 +10615,7 @@ export class Compiler extends DiagnosticEmitter { var flow = this.currentFlow; var temp = flow.getTempLocal(type); if (!flow.canOverflow(expr, type)) flow.setLocalFlag(temp.index, LocalFlags.WRAPPED); - flow.setLocalFlag(temp.index, LocalFlags.NONNULL); + flow.setNarrowedType(temp, type); var staticAbortCallExpr = this.makeStaticAbort( this.ensureStaticString("unexpected null"), diff --git a/src/flow.ts b/src/flow.ts index 0f2d46cc04..f481d464fb 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -92,6 +92,7 @@ import { BuiltinNames } from "./builtins"; import { NarrowedTypeMap, TypeMergeMode, TypeNarrowChecker } from "./narrow"; +import { _BinaryenExpressionPrint } from "./glue/binaryen"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -172,7 +173,7 @@ export enum LocalFlags { /** Local is properly wrapped. Relevant for small integers. */ WRAPPED = 1 << 1, /** Local is non-null. */ - NONNULL = 1 << 2, + // NONNULL = 1 << 2, /** Local is initialized. */ INITIALIZED = 1 << 3 } @@ -325,7 +326,7 @@ export class Flow { } branch.localFlags = this.localFlags.slice(); let narrowedTypes = this.narrowedTypes; - branch.narrowedTypes = narrowedTypes ? narrowedTypes.clone(): null; + branch.narrowedTypes = narrowedTypes ? narrowedTypes.clone() : null; branch.conditionalNarrowedType = this.conditionalNarrowedType; if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); @@ -367,6 +368,7 @@ export class Flow { temps.length = k; local.type = type; local.flags = CommonFlags.NONE; + this.removeNarrowedType(local); this.unsetLocalFlag(local.index, ~0); return local; } @@ -382,6 +384,7 @@ export class Flow { local = parentFunction.addLocal(type); } } + this.removeNarrowedType(local); this.unsetLocalFlag(local.index, ~0); return local; } @@ -621,16 +624,18 @@ export class Flow { localFlags[index] = flags & ~flag; } - setNarrowedType(element: TypedElement, type: Type | null): void { + removeNarrowedType(element: TypedElement): void { + let thisNarrowedTypes = this.narrowedTypes; + if (thisNarrowedTypes) thisNarrowedTypes.delete(element); + this.conditionalNarrowedType.removeConditionNarrowedType(element); + } + setNarrowedType(element: TypedElement, type: Type): void { + if (!type.isReference) return; if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); } let narrowedTypes = assert(this.narrowedTypes); - if (type == null && narrowedTypes.get(element) != null) { - narrowedTypes.delete(element); - } else if (type) { - narrowedTypes.set(element, type); - } + narrowedTypes.set(element, type); } getNarrowedType(element: TypedElement): Type | null { if (this.narrowedTypes == null) { @@ -640,11 +645,13 @@ export class Flow { return narrowedTypes.get(element); } + /** type == null means this expr is a assign expression and will insert a toxic and disable the other check */ setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { + if (type && !type.isReference) return; this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); } inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { - let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi); + let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi, this); if (condiNarrow.size != 0) { if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); @@ -654,7 +661,7 @@ export class Flow { } } inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { - let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi); + let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi, this); if (condiNarrow.size != 0) { if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); @@ -864,7 +871,6 @@ export class Flow { thisLocalFlags[i] = thisFlags & otherFlags & ( LocalFlags.CONSTANT | LocalFlags.WRAPPED | - LocalFlags.NONNULL | LocalFlags.INITIALIZED ); } @@ -979,22 +985,14 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } - let thisNarrowedTypes = this.narrowedTypes; - let rightNarrowedTypes = right.narrowedTypes; - if (thisNarrowedTypes && rightNarrowedTypes) { - thisNarrowedTypes.merge(rightNarrowedTypes, TypeMergeMode.AND); - } + this.narrowedTypes = right.narrowedTypes; } } else if (rightFlags & FlowFlags.TERMINATES) { let leftLocalFlags = left.localFlags; for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } - let thisNarrowedTypes = this.narrowedTypes; - let leftNarrowedTypes = left.narrowedTypes; - if (thisNarrowedTypes && leftNarrowedTypes) { - thisNarrowedTypes.merge(leftNarrowedTypes, TypeMergeMode.AND); - } + this.narrowedTypes = left.narrowedTypes; } else { let leftLocalFlags = left.localFlags; let numLeftLocalFlags = leftLocalFlags.length; @@ -1007,7 +1005,6 @@ export class Flow { thisLocalFlags[i] = leftFlags & rightFlags & ( LocalFlags.CONSTANT | LocalFlags.WRAPPED | - LocalFlags.NONNULL | LocalFlags.INITIALIZED ); } @@ -1059,8 +1056,14 @@ export class Flow { return true; } } + let beforeNarrowedTypes = before.narrowedTypes; + let afterNarrowedTypes = after.narrowedTypes; + let beforeType = beforeNarrowedTypes ? beforeNarrowedTypes.get(local) : null; + beforeType = beforeType ? beforeType : local.type; + let afterType = afterNarrowedTypes ? afterNarrowedTypes.get(local) : null; + afterType = afterType ? afterType : local.type; if (type.isNullableReference) { - if (before.isLocalFlag(i, LocalFlags.NONNULL) && !after.isLocalFlag(i, LocalFlags.NONNULL)) { + if (beforeType != afterType) { return true; } } @@ -1076,15 +1079,18 @@ export class Flow { if (this.isLocalFlag(i, LocalFlags.WRAPPED) != other.isLocalFlag(i, LocalFlags.WRAPPED)) { this.unsetLocalFlag(i, LocalFlags.WRAPPED); // assume not wrapped } - if (this.isLocalFlag(i, LocalFlags.NONNULL) != other.isLocalFlag(i, LocalFlags.NONNULL)) { - this.unsetLocalFlag(i, LocalFlags.NONNULL); // assume possibly null - } + } + let thisNarrowedTypes = this.narrowedTypes; + let otherNarrowedTypes = other.narrowedTypes; + if (thisNarrowedTypes && otherNarrowedTypes) { + thisNarrowedTypes.merge(otherNarrowedTypes, TypeMergeMode.AND); } } /** Checks if an expression of the specified type is known to be non-null, even if the type might be nullable. */ isNonnull(expr: ExpressionRef, type: Type): bool { if (!type.isNullableReference) return true; + let thisNarrowedTypes = this.narrowedTypes; // below, only teeLocal/getLocal are relevant because these are the only expressions that // depend on a dynamic nullable state (flag = LocalFlags.NONNULL), while everything else // has already been handled by the nullable type check above. @@ -1092,221 +1098,20 @@ export class Flow { case ExpressionId.LocalSet: { if (!isLocalTee(expr)) break; let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - return !local.type.isNullableReference || this.isLocalFlag(local.index, LocalFlags.NONNULL, false); + let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; + localType = localType ? localType : local.type; + return !localType.isNullableReference; } case ExpressionId.LocalGet: { let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - return !local.type.isNullableReference || this.isLocalFlag(local.index, LocalFlags.NONNULL, false); + let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; + localType = localType ? localType : local.type; + return !localType.isNullableReference; } } return false; } - /** Updates local states to reflect that this branch is only taken when `expr` is true-ish. */ - inheritNonnullIfTrue( - /** Expression being true. */ - expr: ExpressionRef, - /** If specified, only set the flag if also nonnull in this flow. */ - iff: Flow | null = null - ): void { - // A: `expr` is true-ish -> Q: how did that happen? - - // The iff argument is useful in situations like - // - // if (!ref) { - // ref = new Ref(); - // } - // // inheritNonnullIfFalse(`!ref`, thenFlow) -> ref != null - // - - switch (getExpressionId(expr)) { - case ExpressionId.LocalSet: { - if (!isLocalTee(expr)) break; - let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - if (!iff || iff.isLocalFlag(local.index, LocalFlags.NONNULL)) { - this.setLocalFlag(local.index, LocalFlags.NONNULL); - } - this.inheritNonnullIfTrue(getLocalSetValue(expr), iff); // must have been true-ish as well - break; - } - case ExpressionId.LocalGet: { - let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - if (!iff || iff.isLocalFlag(local.index, LocalFlags.NONNULL)) { - this.setLocalFlag(local.index, LocalFlags.NONNULL); - } - break; - } - case ExpressionId.If: { - let ifFalse = getIfFalse(expr); - if (ifFalse && isConstZero(ifFalse)) { - // Logical AND: (if (condition ifTrue 0)) - // the only way this had become true is if condition and ifTrue are true - this.inheritNonnullIfTrue(getIfCondition(expr), iff); - this.inheritNonnullIfTrue(getIfTrue(expr), iff); - } - break; - } - case ExpressionId.Unary: { - switch (getUnaryOp(expr)) { - case UnaryOp.EqzI32: - case UnaryOp.EqzI64: { - this.inheritNonnullIfFalse(getUnaryValue(expr), iff); // !value -> value must have been false - break; - } - } - break; - } - case ExpressionId.Binary: { - switch (getBinaryOp(expr)) { - case BinaryOp.EqI32: - case BinaryOp.EqI64: { - let left = getBinaryLeft(expr); - let right = getBinaryRight(expr); - if (isConstNonZero(left)) { - this.inheritNonnullIfTrue(right, iff); // TRUE == right -> right must have been true - } else if (isConstNonZero(right)) { - this.inheritNonnullIfTrue(left, iff); // left == TRUE -> left must have been true - } - break; - } - case BinaryOp.NeI32: - case BinaryOp.NeI64: { - let left = getBinaryLeft(expr); - let right = getBinaryRight(expr); - if (isConstZero(left)) { - this.inheritNonnullIfTrue(right, iff); // FALSE != right -> right must have been true - } else if (isConstZero(right)) { - this.inheritNonnullIfTrue(left, iff); // left != FALSE -> left must have been true - } - break; - } - } - break; - } - case ExpressionId.Call: { - // handle string eq/ne/not overloads - let name = getCallTarget(expr); - if (name == BuiltinNames.String_eq) { - assert(getCallOperandCount(expr) == 2); - let left = getCallOperandAt(expr, 0); - let right = getCallOperandAt(expr, 1); - if (isConstNonZero(left)) { - this.inheritNonnullIfTrue(right, iff); // TRUE == right -> right must have been true - } else if (isConstNonZero(right)) { - this.inheritNonnullIfTrue(left, iff); // left == TRUE -> left must have been true - } - } else if (name == BuiltinNames.String_ne) { - assert(getCallOperandCount(expr) == 2); - let left = getCallOperandAt(expr, 0); - let right = getCallOperandAt(expr, 1); - if (isConstZero(left)) { - this.inheritNonnullIfTrue(right, iff); // FALSE != right -> right must have been true - } else if (isConstZero(right)) { - this.inheritNonnullIfTrue(left, iff); // left != FALSE -> left must have been true - } - } else if (name == BuiltinNames.String_not) { - assert(getCallOperandCount(expr) == 1); - this.inheritNonnullIfFalse(getCallOperandAt(expr, 0), iff); // !value -> value must have been false - } else if (name == BuiltinNames.tostack) { - assert(getCallOperandCount(expr) == 1); - this.inheritNonnullIfTrue(getCallOperandAt(expr, 0), iff); - } - break; - } - } - } - - /** Updates local states to reflect that this branch is only taken when `expr` is false-ish. */ - inheritNonnullIfFalse( - /** Expression being false. */ - expr: ExpressionRef, - /** If specified, only set the flag if also nonnull in this flow. */ - iff: Flow | null = null - ): void { - // A: `expr` is false-ish -> Q: how did that happen? - switch (getExpressionId(expr)) { - case ExpressionId.Unary: { - switch (getUnaryOp(expr)) { - case UnaryOp.EqzI32: - case UnaryOp.EqzI64: { - this.inheritNonnullIfTrue(getUnaryValue(expr), iff); // !value -> value must have been true - break; - } - } - break; - } - case ExpressionId.If: { - let ifTrue = getIfTrue(expr); - let ifFalse = getIfFalse(expr); - if (ifFalse && isConstNonZero(ifTrue)) { - // Logical OR: (if (condition 1 ifFalse)) - // the only way this had become false is if condition and ifFalse are false - this.inheritNonnullIfFalse(getIfCondition(expr), iff); - this.inheritNonnullIfFalse(getIfFalse(expr), iff); - } - break; - } - case ExpressionId.Binary: { - switch (getBinaryOp(expr)) { - // remember: we want to know how the _entire_ expression became FALSE (!) - case BinaryOp.EqI32: - case BinaryOp.EqI64: { - let left = getBinaryLeft(expr); - let right = getBinaryRight(expr); - if (isConstZero(left)) { - this.inheritNonnullIfTrue(right, iff); // !(FALSE == right) -> right must have been true - } else if (isConstZero(right)) { - this.inheritNonnullIfTrue(left, iff); // !(left == FALSE) -> left must have been true - } - break; - } - case BinaryOp.NeI32: - case BinaryOp.NeI64: { - let left = getBinaryLeft(expr); - let right = getBinaryRight(expr); - if (isConstNonZero(left)) { - this.inheritNonnullIfTrue(right, iff); // !(TRUE != right) -> right must have been true - } else if (isConstNonZero(right)) { - this.inheritNonnullIfTrue(left, iff); // !(left != TRUE) -> left must have been true - } - break; - } - } - break; - } - case ExpressionId.Call: { - // handle string eq/ne/not overloads - let name = getCallTarget(expr); - if (name == BuiltinNames.String_eq) { - assert(getCallOperandCount(expr) == 2); - let left = getCallOperandAt(expr, 0); - let right = getCallOperandAt(expr, 1); - if (isConstZero(left)) { - this.inheritNonnullIfTrue(right, iff); // !(FALSE == right) -> right must have been true - } else if (isConstZero(right)) { - this.inheritNonnullIfTrue(left, iff); // !(left == FALSE) -> left must have been true - } - } else if (name == BuiltinNames.String_ne) { - assert(getCallOperandCount(expr) == 2); - let left = getCallOperandAt(expr, 0); - let right = getCallOperandAt(expr, 1); - if (isConstNonZero(left)) { - this.inheritNonnullIfTrue(right, iff); // !(TRUE != right) -> right must have been true - } else if (isConstNonZero(right)) { - this.inheritNonnullIfTrue(left, iff); // !(left != TRUE) -> left must have been true - } - } else if (name == BuiltinNames.String_not) { - assert(getCallOperandCount(expr) == 1); - this.inheritNonnullIfTrue(getCallOperandAt(expr, 0), iff); // !(!value) -> value must have been true - } else if (name == BuiltinNames.tostack) { - assert(getCallOperandCount(expr) == 1); - this.inheritNonnullIfFalse(getCallOperandAt(expr, 0), iff); - } - break; - } - } - } - /** * 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 diff --git a/src/narrow.ts b/src/narrow.ts index 72153fa972..6fdbc6a50d 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -1,3 +1,5 @@ +import { BuiltinNames } from "./builtins"; +import { Flow } from "./flow"; import { BinaryOp, ExpressionId, @@ -5,10 +7,15 @@ import { getBinaryLeft, getBinaryOp, getBinaryRight, + getCallOperandAt, + getCallOperandCount, + getCallTarget, getExpressionId, getIfCondition, getIfFalse, getIfTrue, + getLocalGetIndex, + getLocalSetIndex, getLocalSetValue, getUnaryOp, getUnaryValue, @@ -57,9 +64,9 @@ export class NarrowedTypeMap { get size(): i32 { return this.typeMap.size; } - get(key: TypedElement): Type | null { - if (this.typeMap.has(key)) { - let type = assert(this.typeMap.get(key)); + get(element: TypedElement): Type | null { + if (this.typeMap.has(element)) { + let type = assert(this.typeMap.get(element)); if (type == Type.void) { return null; } @@ -68,8 +75,17 @@ export class NarrowedTypeMap { return null; } } - set(key: TypedElement, value: Type): void { - this.typeMap.set(key, value); + set(element: TypedElement, type: Type): void { + this.typeMap.set(element, type); + } + setNonnull(typedElement: TypedElement): void { + let typeMap = this.typeMap; + if (typeMap.has(typedElement)) { + let type = assert(typeMap.get(typedElement)); + typeMap.set(typedElement, type.nonNullableType); + } else { + typeMap.set(typedElement, typedElement.type.nonNullableType); + } } delete(key: TypedElement): bool { return this.typeMap.delete(key); @@ -102,7 +118,8 @@ export class NarrowedTypeMap { let key = bKeys[i]; let aType = aMap.has(key) ? assert(aMap.get(key)) : null; let bType = assert(bMap.get(key)); - let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + let mergedType = + mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); if (mergedType) { aMap.set(key, mergedType); } else { @@ -110,12 +127,37 @@ export class NarrowedTypeMap { } } } + + toString(): string { + let key = Map_keys(this.typeMap); + let value = Map_values(this.typeMap); + let str = new Array(); + for (let i = 0, k = key.length; i < k; i++) { + str.push(`${key[i].internalName}: ${value[i]}`); + } + return "narrowedTypes: " + str.join("; "); + } } export class TypeNarrowChecker { expressionMap: Map = new Map(); - setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { + removeConditionNarrowedType(element: TypedElement): void { + let maps = Map_values(this.expressionMap); + for (let i = 0, k = maps.length; i < k; i++) { + maps[i].delete(element); + } + } + + /** + * case 1: type is nonnull, add condition type, eg t instanceof B + * case 2: type is null, remove condition type, eg t = new A() in some condition + */ + setConditionNarrowedType( + expr: ExpressionRef, + element: TypedElement, + type: Type | null + ): void { let expressionMap = this.expressionMap; if (expr > 0) { if (!expressionMap.has(expr)) { @@ -123,16 +165,14 @@ export class TypeNarrowChecker { } let narrowMap = assert(expressionMap.get(expr)); if (type) { - // case 1: add condition type, eg t instanceof B narrowMap.set(element, type); } else { - // case 2: remove condition type, eg t = new A() in some condition narrowMap.set(element, Type.void); } } } - collectNarrowedTypeIfTrue(expr: ExpressionRef): NarrowedTypeMap { + collectNarrowedTypeIfTrue(expr: ExpressionRef, flow: Flow): NarrowedTypeMap { let result = new NarrowedTypeMap(); let expressionMap = assert(this.expressionMap); if (expressionMap.has(expr)) { @@ -142,8 +182,18 @@ export class TypeNarrowChecker { switch (getExpressionId(expr)) { case ExpressionId.LocalSet: { if (!isLocalTee(expr)) break; - let subMap = this.collectNarrowedTypeIfTrue(getLocalSetValue(expr)); + let subMap = this.collectNarrowedTypeIfTrue( + getLocalSetValue(expr), + flow + ); result.merge(subMap); + let local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + result.setNonnull(local); + break; + } + case ExpressionId.LocalGet: { + let local = flow.parentFunction.localsByIndex[getLocalGetIndex(expr)]; + result.setNonnull(local); break; } case ExpressionId.If: { @@ -153,16 +203,16 @@ export class TypeNarrowChecker { if (ifFalse && isConstZero(ifFalse)) { // Logical AND: (if (condition ifTrue 0)) // the only way this had become true is if condition and ifTrue are true - let subMap = this.collectNarrowedTypeIfTrue(condition); - let subMapTrue = this.collectNarrowedTypeIfTrue(ifTrue); + let subMap = this.collectNarrowedTypeIfTrue(condition, flow); + let subMapTrue = this.collectNarrowedTypeIfTrue(ifTrue, flow); subMap.merge(subMapTrue); result.merge(subMap); } if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMap = this.collectNarrowedTypeIfTrue(condition); - let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse); + let subMap = this.collectNarrowedTypeIfTrue(condition, flow); + let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse, flow); subMap.merge(subMapFalse, TypeMergeMode.AND); result.merge(subMap); } @@ -172,7 +222,10 @@ export class TypeNarrowChecker { switch (getUnaryOp(expr)) { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { - let subMap = this.collectNarrowedTypeIfFalse(getUnaryValue(expr)); // !value -> value must have been false + let subMap = this.collectNarrowedTypeIfFalse( + getUnaryValue(expr), + flow + ); // !value -> value must have been false result.merge(subMap); break; } @@ -186,10 +239,10 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true result.merge(subMap); } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left); // left == TRUE -> left must have been true + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left == TRUE -> left must have been true result.merge(subMap); } break; @@ -199,10 +252,10 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right); // TRUE == right -> right must have been true + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true result.merge(subMap); } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left); // TRUE == right -> right must have been true + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // TRUE == right -> right must have been true result.merge(subMap); } break; @@ -210,18 +263,63 @@ export class TypeNarrowChecker { } break; } + case ExpressionId.Call: { + // handle string eq/ne/not overloads + let name = getCallTarget(expr); + if (name == BuiltinNames.String_eq) { + assert(getCallOperandCount(expr) == 2); + let left = getCallOperandAt(expr, 0); + let right = getCallOperandAt(expr, 1); + if (isConstNonZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true + result.merge(subMap); + } else if (isConstNonZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left == TRUE -> left must have been true + result.merge(subMap); + } + } else if (name == BuiltinNames.String_ne) { + assert(getCallOperandCount(expr) == 2); + let left = getCallOperandAt(expr, 0); + let right = getCallOperandAt(expr, 1); + if (isConstZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // FALSE != right -> right must have been true + result.merge(subMap); + } else if (isConstZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left != FALSE -> left must have been true + result.merge(subMap); + } + } else if (name == BuiltinNames.String_not) { + assert(getCallOperandCount(expr) == 1); + let subMap = this.collectNarrowedTypeIfFalse( + getCallOperandAt(expr, 0), + flow + ); // !value -> value must have been false + result.merge(subMap); + } else if (name == BuiltinNames.tostack) { + assert(getCallOperandCount(expr) == 1); + let subMap = this.collectNarrowedTypeIfTrue( + getCallOperandAt(expr, 0), + flow + ); + result.merge(subMap); + } + break; + } } return result; } - collectNarrowedTypeIfFalse(expr: ExpressionRef): NarrowedTypeMap { + collectNarrowedTypeIfFalse(expr: ExpressionRef, flow: Flow): NarrowedTypeMap { let result = new NarrowedTypeMap(); switch (getExpressionId(expr)) { case ExpressionId.Unary: { switch (getUnaryOp(expr)) { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { - let subMap = this.collectNarrowedTypeIfTrue(getUnaryValue(expr)); // !value -> value must have been true + let subMap = this.collectNarrowedTypeIfTrue( + getUnaryValue(expr), + flow + ); // !value -> value must have been true result.merge(subMap); break; } @@ -234,8 +332,14 @@ export class TypeNarrowChecker { if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMap = this.collectNarrowedTypeIfFalse(getIfCondition(expr)); - let subMapFalse = this.collectNarrowedTypeIfFalse(getIfFalse(expr)); + let subMap = this.collectNarrowedTypeIfFalse( + getIfCondition(expr), + flow + ); + let subMapFalse = this.collectNarrowedTypeIfFalse( + getIfFalse(expr), + flow + ); subMap.merge(subMapFalse); result.merge(subMap); } @@ -249,10 +353,10 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right); // !(FALSE == right) -> right must have been true + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(FALSE == right) -> right must have been true result.merge(subMap); } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left); // !(left == FALSE) -> left must have been true + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left == FALSE) -> left must have been true result.merge(subMap); } break; @@ -262,10 +366,10 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right); // !(TRUE != right) -> right must have been true + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(TRUE != right) -> right must have been true result.merge(subMap); } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left); // !(left != TRUE) -> left must have been true + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left != TRUE) -> left must have been true result.merge(subMap); } break; @@ -273,6 +377,47 @@ export class TypeNarrowChecker { } break; } + case ExpressionId.Call: { + // handle string eq/ne/not overloads + let name = getCallTarget(expr); + if (name == BuiltinNames.String_eq) { + assert(getCallOperandCount(expr) == 2); + let left = getCallOperandAt(expr, 0); + let right = getCallOperandAt(expr, 1); + if (isConstZero(left)) { + this.collectNarrowedTypeIfTrue(right, flow); // !(FALSE == right) -> right must have been true + } else if (isConstZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left == FALSE) -> left must have been true + result.merge(subMap); + } + } else if (name == BuiltinNames.String_ne) { + assert(getCallOperandCount(expr) == 2); + let left = getCallOperandAt(expr, 0); + let right = getCallOperandAt(expr, 1); + if (isConstNonZero(left)) { + let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(TRUE != right) -> right must have been true + result.merge(subMap); + } else if (isConstNonZero(right)) { + let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left != TRUE) -> left must have been true + result.merge(subMap); + } + } else if (name == BuiltinNames.String_not) { + assert(getCallOperandCount(expr) == 1); + let subMap = this.collectNarrowedTypeIfTrue( + getCallOperandAt(expr, 0), + flow + ); // !(!value) -> value must have been true + result.merge(subMap); + } else if (name == BuiltinNames.tostack) { + assert(getCallOperandCount(expr) == 1); + let subMap = this.collectNarrowedTypeIfFalse( + getCallOperandAt(expr, 0), + flow + ); + result.merge(subMap); + } + break; + } } return result; } diff --git a/tests/compiler/class-overloading.debug.wat b/tests/compiler/class-overloading.debug.wat index 44d8f98102..4e597e53d8 100644 --- a/tests/compiler/class-overloading.debug.wat +++ b/tests/compiler/class-overloading.debug.wat @@ -2320,47 +2320,47 @@ i32.const 592 global.set $class-overloading/which ) - (func $class-overloading/IA#foo (param $0 i32) - unreachable - ) - (func $class-overloading/A2#foo (param $0 i32) (result i32) - i32.const 720 - i32.const 528 - i32.const 198 - i32.const 5 - call $~lib/builtins/abort - unreachable + (func $class-overloading/B#b (param $0 i32) (param $1 i32) + i32.const 496 + global.set $class-overloading/which ) - (func $class-overloading/F#a (param $0 i32) (param $1 i32) - i32.const 624 + (func $class-overloading/B#get:c (param $0 i32) (result i32) + i32.const 496 global.set $class-overloading/which + i32.const 0 ) - (func $class-overloading/B#b (param $0 i32) (param $1 i32) + (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) i32.const 496 global.set $class-overloading/which ) - (func $class-overloading/F#b (param $0 i32) (param $1 i32) + (func $class-overloading/F#a (param $0 i32) (param $1 i32) i32.const 624 global.set $class-overloading/which ) - (func $class-overloading/B#get:c (param $0 i32) (result i32) - i32.const 496 + (func $class-overloading/F#b (param $0 i32) (param $1 i32) + i32.const 624 global.set $class-overloading/which - i32.const 0 ) (func $class-overloading/F#get:c (param $0 i32) (result i32) i32.const 624 global.set $class-overloading/which i32.const 0 ) - (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) - i32.const 496 - global.set $class-overloading/which - ) (func $class-overloading/F#set:c (param $0 i32) (param $1 i32) i32.const 624 global.set $class-overloading/which ) + (func $class-overloading/IA#foo (param $0 i32) + unreachable + ) + (func $class-overloading/A2#foo (param $0 i32) (result i32) + i32.const 720 + i32.const 528 + i32.const 198 + i32.const 5 + call $~lib/builtins/abort + unreachable + ) (func $class-overloading/CA#foo (param $0 i32) i32.const 656 global.set $class-overloading/which @@ -2588,6 +2588,139 @@ local.get $1 call $class-overloading/A#set:c ) + (func $class-overloading/B#a@virtual (param $0 i32) (param $1 i32) + (local $2 i32) + block $default + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $2 + local.get $2 + i32.const 5 + i32.eq + br_if $case0 + local.get $2 + i32.const 8 + i32.eq + br_if $case1 + br $default + end + local.get $0 + local.get $1 + call $class-overloading/C#a + return + end + local.get $0 + local.get $1 + call $class-overloading/F#a + return + end + local.get $0 + local.get $1 + call $class-overloading/B#a + ) + (func $class-overloading/B#b@virtual (param $0 i32) (param $1 i32) + (local $2 i32) + block $default + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $2 + local.get $2 + i32.const 5 + i32.eq + br_if $case0 + local.get $2 + i32.const 8 + i32.eq + br_if $case1 + br $default + end + local.get $0 + local.get $1 + call $class-overloading/C#b + return + end + local.get $0 + local.get $1 + call $class-overloading/F#b + return + end + local.get $0 + local.get $1 + call $class-overloading/B#b + ) + (func $class-overloading/B#get:c@virtual (param $0 i32) (result i32) + (local $1 i32) + block $default + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $1 + local.get $1 + i32.const 5 + i32.eq + br_if $case0 + local.get $1 + i32.const 8 + i32.eq + br_if $case1 + br $default + end + local.get $0 + call $class-overloading/C#get:c + return + end + local.get $0 + call $class-overloading/F#get:c + return + end + local.get $0 + call $class-overloading/B#get:c + ) + (func $class-overloading/B#set:c@virtual (param $0 i32) (param $1 i32) + (local $2 i32) + block $default + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $2 + local.get $2 + i32.const 5 + i32.eq + br_if $case0 + local.get $2 + i32.const 8 + i32.eq + br_if $case1 + br $default + end + local.get $0 + local.get $1 + call $class-overloading/C#set:c + return + end + local.get $0 + local.get $1 + call $class-overloading/F#set:c + return + end + local.get $0 + local.get $1 + call $class-overloading/B#set:c + ) (func $class-overloading/IA#foo@virtual (param $0 i32) (local $1 i32) block $default @@ -3149,7 +3282,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#a@virtual + call $class-overloading/B#a@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3181,7 +3314,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#b@virtual + call $class-overloading/B#b@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3212,7 +3345,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/A#get:c@virtual + call $class-overloading/B#get:c@virtual drop global.get $class-overloading/which local.set $0 @@ -3243,7 +3376,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#set:c@virtual + call $class-overloading/B#set:c@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3278,7 +3411,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#a@virtual + call $class-overloading/B#a@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3310,7 +3443,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#b@virtual + call $class-overloading/B#b@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3341,7 +3474,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/A#get:c@virtual + call $class-overloading/B#get:c@virtual drop global.get $class-overloading/which local.set $0 @@ -3372,7 +3505,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#set:c@virtual + call $class-overloading/B#set:c@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3407,7 +3540,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#a@virtual + call $class-overloading/F#a global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3439,7 +3572,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#b@virtual + call $class-overloading/F#b global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3470,7 +3603,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/A#get:c@virtual + call $class-overloading/F#get:c drop global.get $class-overloading/which local.set $0 @@ -3503,7 +3636,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/A#set:c@virtual + call $class-overloading/F#set:c global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer diff --git a/tests/compiler/class-overloading.release.wat b/tests/compiler/class-overloading.release.wat index ff4df62b21..ba054d3473 100644 --- a/tests/compiler/class-overloading.release.wat +++ b/tests/compiler/class-overloading.release.wat @@ -1402,22 +1402,22 @@ local.get $3 i32.eqz ) - (func $class-overloading/A#a@virtual (param $0 i32) + (func $class-overloading/B#a@virtual (param $0 i32) block $default - block $case2 - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case0 $case1 $case0 $case0 $case2 $default - end - i32.const 1520 - global.set $class-overloading/which - return + block $case1 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case1 + br $default end call $class-overloading/C#a return @@ -1426,7 +1426,7 @@ global.set $class-overloading/which return end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which ) (func $~lib/rt/__visit_members (param $0 i32) @@ -1608,16 +1608,42 @@ global.get $class-overloading/a local.tee $0 i32.store - local.get $0 - call $class-overloading/A#a@virtual + block $__inlined_func$class-overloading/A#a@virtual + block $default + block $case2 + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case0 $case1 $case0 $case0 $case2 $default + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#a@virtual + end + call $class-overloading/C#a + br $__inlined_func$class-overloading/A#a@virtual + end + i32.const 1648 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#a@virtual + end + i32.const 1488 + global.set $class-overloading/which + end global.get $~lib/memory/__stack_pointer - global.get $class-overloading/which local.tee $0 + global.get $class-overloading/which + local.tee $1 i32.store - global.get $~lib/memory/__stack_pointer + local.get $0 i32.const 1520 i32.store offset=4 - local.get $0 + local.get $1 i32.const 1520 call $~lib/string/String.__eq i32.eqz @@ -1636,17 +1662,17 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/A#b@virtual - block $default - block $case2 - block $case1 - block $case0 + block $default6 + block $case27 + block $case18 + block $case09 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case0 $case1 $case0 $case0 $case2 $default + br_table $case09 $case18 $case09 $case09 $case27 $default6 end i32.const 1520 global.set $class-overloading/which @@ -1690,17 +1716,17 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/A#get:c@virtual - block $default6 - block $case27 - block $case18 - block $case09 + block $default13 + block $case214 + block $case115 + block $case016 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case09 $case18 $case09 $case09 $case27 $default6 + br_table $case016 $case115 $case016 $case016 $case214 $default13 end i32.const 1520 global.set $class-overloading/which @@ -1743,30 +1769,30 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual10 - block $default11 - block $case212 - block $case113 - block $case014 + block $__inlined_func$class-overloading/A#b@virtual17 + block $default18 + block $case219 + block $case120 + block $case021 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case014 $case113 $case014 $case014 $case212 $default11 + br_table $case021 $case120 $case021 $case021 $case219 $default18 end i32.const 1520 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual10 + br $__inlined_func$class-overloading/A#b@virtual17 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual10 + br $__inlined_func$class-overloading/A#b@virtual17 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual10 + br $__inlined_func$class-overloading/A#b@virtual17 end i32.const 1488 global.set $class-overloading/which @@ -1930,7 +1956,7 @@ local.tee $0 i32.store local.get $0 - call $class-overloading/A#a@virtual + call $class-overloading/B#a@virtual global.get $~lib/memory/__stack_pointer global.get $class-overloading/which local.tee $0 @@ -1956,32 +1982,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual22 - block $default23 - block $case224 - block $case125 - block $case026 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case026 $case125 $case026 $case026 $case224 $default23 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual22 + block $__inlined_func$class-overloading/B#b@virtual + block $default29 + block $case130 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case130 + br $default29 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual22 + br $__inlined_func$class-overloading/B#b@virtual end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual22 + br $__inlined_func$class-overloading/B#b@virtual end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2010,32 +2036,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#get:c@virtual31 - block $default32 - block $case233 - block $case134 - block $case035 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case035 $case134 $case035 $case035 $case233 $default32 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual31 + block $__inlined_func$class-overloading/B#get:c@virtual + block $default35 + block $case136 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case136 + br $default35 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual31 + br $__inlined_func$class-overloading/B#get:c@virtual end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual31 + br $__inlined_func$class-overloading/B#get:c@virtual end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2062,32 +2088,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual40 - block $default41 - block $case242 - block $case143 - block $case044 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case044 $case143 $case044 $case044 $case242 $default41 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual40 + block $__inlined_func$class-overloading/B#b@virtual41 + block $default42 + block $case143 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case143 + br $default42 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual40 + br $__inlined_func$class-overloading/B#b@virtual41 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual40 + br $__inlined_func$class-overloading/B#b@virtual41 end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2120,7 +2146,7 @@ local.tee $0 i32.store local.get $0 - call $class-overloading/A#a@virtual + call $class-overloading/B#a@virtual global.get $~lib/memory/__stack_pointer global.get $class-overloading/which local.tee $0 @@ -2146,32 +2172,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual49 - block $default50 - block $case251 - block $case152 - block $case053 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case053 $case152 $case053 $case053 $case251 $default50 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual49 + block $__inlined_func$class-overloading/B#b@virtual48 + block $default49 + block $case150 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case150 + br $default49 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual49 + br $__inlined_func$class-overloading/B#b@virtual48 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual49 + br $__inlined_func$class-overloading/B#b@virtual48 end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2200,32 +2226,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#get:c@virtual58 - block $default59 - block $case260 - block $case161 - block $case062 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case062 $case161 $case062 $case062 $case260 $default59 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual58 + block $__inlined_func$class-overloading/B#get:c@virtual55 + block $default56 + block $case157 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case157 + br $default56 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual58 + br $__inlined_func$class-overloading/B#get:c@virtual55 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual58 + br $__inlined_func$class-overloading/B#get:c@virtual55 end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2252,32 +2278,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual67 - block $default68 - block $case269 - block $case170 - block $case071 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case071 $case170 $case071 $case071 $case269 $default68 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual67 + block $__inlined_func$class-overloading/B#b@virtual62 + block $default63 + block $case164 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.tee $0 + i32.const 5 + i32.ne + if + local.get $0 + i32.const 8 + i32.eq + br_if $case164 + br $default63 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual67 + br $__inlined_func$class-overloading/B#b@virtual62 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual67 + br $__inlined_func$class-overloading/B#b@virtual62 end - i32.const 1488 + i32.const 1520 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2332,18 +2358,16 @@ global.set $class-overloading/which global.get $~lib/memory/__stack_pointer global.get $class-overloading/a - local.tee $0 i32.store - local.get $0 - call $class-overloading/A#a@virtual + i32.const 1648 + global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - global.get $class-overloading/which - local.tee $0 + i32.const 1648 i32.store global.get $~lib/memory/__stack_pointer i32.const 1648 i32.store offset=4 - local.get $0 + i32.const 1648 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2358,46 +2382,18 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - global.get $class-overloading/a local.tee $0 + global.get $class-overloading/a i32.store - block $__inlined_func$class-overloading/A#b@virtual77 - block $default78 - block $case279 - block $case180 - block $case081 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case081 $case180 $case081 $case081 $case279 $default78 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual77 - end - i32.const 1616 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual77 - end - i32.const 1648 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual77 - end - i32.const 1488 - global.set $class-overloading/which - end - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $class-overloading/which - local.tee $1 + i32.const 1648 + global.set $class-overloading/which + local.get $0 + i32.const 1648 i32.store local.get $0 i32.const 1648 i32.store offset=4 - local.get $1 + i32.const 1648 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2412,46 +2408,18 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - global.get $class-overloading/a local.tee $0 + global.get $class-overloading/a i32.store - block $__inlined_func$class-overloading/A#get:c@virtual86 - block $default87 - block $case288 - block $case189 - block $case090 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case090 $case189 $case090 $case090 $case288 $default87 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual86 - end - i32.const 1616 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual86 - end - i32.const 1648 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#get:c@virtual86 - end - i32.const 1488 - global.set $class-overloading/which - end - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $class-overloading/which - local.tee $1 + i32.const 1648 + global.set $class-overloading/which + local.get $0 + i32.const 1648 i32.store local.get $0 i32.const 1648 i32.store offset=4 - local.get $1 + i32.const 1648 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2466,46 +2434,18 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - global.get $class-overloading/a local.tee $0 + global.get $class-overloading/a i32.store - block $__inlined_func$class-overloading/A#b@virtual95 - block $default96 - block $case297 - block $case198 - block $case099 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case099 $case198 $case099 $case099 $case297 $default96 - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual95 - end - i32.const 1616 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual95 - end - i32.const 1648 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual95 - end - i32.const 1488 - global.set $class-overloading/which - end - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $class-overloading/which - local.tee $1 + i32.const 1648 + global.set $class-overloading/which + local.get $0 + i32.const 1648 i32.store local.get $0 i32.const 1648 i32.store offset=4 - local.get $1 + i32.const 1648 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2547,8 +2487,8 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/IA#foo@virtual - block $default105 - block $case1106 + block $default73 + block $case174 local.get $0 i32.const 8 i32.sub @@ -2560,8 +2500,8 @@ local.get $0 i32.const 12 i32.eq - br_if $case1106 - br $default105 + br_if $case174 + br $default73 end i32.const 1680 global.set $class-overloading/which @@ -2622,9 +2562,9 @@ global.get $class-overloading/ic local.tee $0 i32.store - block $__inlined_func$class-overloading/IA#foo@virtual109 - block $default110 - block $case1111 + block $__inlined_func$class-overloading/IA#foo@virtual77 + block $default78 + block $case179 local.get $0 i32.const 8 i32.sub @@ -2636,16 +2576,16 @@ local.get $0 i32.const 12 i32.eq - br_if $case1111 - br $default110 + br_if $case179 + br $default78 end i32.const 1680 global.set $class-overloading/which - br $__inlined_func$class-overloading/IA#foo@virtual109 + br $__inlined_func$class-overloading/IA#foo@virtual77 end i32.const 1712 global.set $class-overloading/which - br $__inlined_func$class-overloading/IA#foo@virtual109 + br $__inlined_func$class-overloading/IA#foo@virtual77 end unreachable end diff --git a/tests/compiler/for.debug.wat b/tests/compiler/for.debug.wat index da03fcda17..e7fcf8c3f2 100644 --- a/tests/compiler/for.debug.wat +++ b/tests/compiler/for.debug.wat @@ -2544,6 +2544,7 @@ (local $0 i32) (local $1 i32) (local $2 i32) + (local $3 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub @@ -2559,10 +2560,10 @@ call $for/Ref#constructor local.tee $1 i32.store - loop $for-loop|0 + loop $for-loop|1 local.get $1 - local.set $2 - local.get $2 + local.set $3 + local.get $3 if local.get $0 i32.const 1 @@ -2580,7 +2581,7 @@ local.tee $1 i32.store end - br $for-loop|0 + br $for-loop|1 end end local.get $0 diff --git a/tests/compiler/for.release.wat b/tests/compiler/for.release.wat index cb9acc257b..3a21356e5c 100644 --- a/tests/compiler/for.release.wat +++ b/tests/compiler/for.release.wat @@ -1283,7 +1283,7 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|08 + loop $for-loop|17 local.get $0 if local.get $1 @@ -1301,7 +1301,7 @@ local.tee $0 i32.store end - br $for-loop|08 + br $for-loop|17 end end local.get $1 @@ -1349,10 +1349,10 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|012 + loop $for-loop|011 call $for/Ref#constructor if - block $for-break011 + block $for-break010 local.get $1 i32.const 1 i32.add @@ -1362,13 +1362,13 @@ if i32.const 0 local.set $0 - br $for-break011 + br $for-break010 end global.get $~lib/memory/__stack_pointer call $for/Ref#constructor local.tee $0 i32.store - br $for-loop|012 + br $for-loop|011 end end end diff --git a/tests/compiler/std/math.release.wat b/tests/compiler/std/math.release.wat index 2976d7d875..1808c052ff 100644 --- a/tests/compiler/std/math.release.wat +++ b/tests/compiler/std/math.release.wat @@ -49905,7 +49905,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -49949,7 +49949,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -49971,7 +49971,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50037,7 +50037,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50103,7 +50103,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50169,7 +50169,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50257,7 +50257,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50367,7 +50367,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50521,7 +50521,7 @@ call $~lib/builtins/abort unreachable end - f64.const -nan:0x8000000000000 + f64.const nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -51753,7 +51753,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51779,7 +51779,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51792,7 +51792,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51831,7 +51831,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51870,7 +51870,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51909,7 +51909,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51961,7 +51961,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -52026,7 +52026,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -52052,7 +52052,7 @@ call $~lib/builtins/abort unreachable end - f32.const -nan:0x400000 + f32.const nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check diff --git a/tests/compiler/while.debug.wat b/tests/compiler/while.debug.wat index 0123064c08..73018885bf 100644 --- a/tests/compiler/while.debug.wat +++ b/tests/compiler/while.debug.wat @@ -2588,7 +2588,7 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|0 + loop $while-continue|1 local.get $1 local.set $2 local.get $2 @@ -2609,7 +2609,7 @@ local.tee $1 i32.store end - br $while-continue|0 + br $while-continue|1 end end local.get $0 @@ -2665,8 +2665,8 @@ call $while/Ref#constructor local.tee $1 i32.store - block $while-break|0 - loop $while-continue|0 + block $while-break|1 + loop $while-continue|1 call $while/getRef local.set $2 local.get $2 @@ -2680,9 +2680,9 @@ if i32.const 0 local.set $1 - br $while-break|0 + br $while-break|1 end - br $while-continue|0 + br $while-continue|1 end end end diff --git a/tests/compiler/while.release.wat b/tests/compiler/while.release.wat index 1488e913b9..55ed996a0d 100644 --- a/tests/compiler/while.release.wat +++ b/tests/compiler/while.release.wat @@ -1317,7 +1317,7 @@ call $while/Ref#constructor local.tee $3 i32.store - loop $while-continue|08 + loop $while-continue|17 local.get $3 if local.get $1 @@ -1335,7 +1335,7 @@ local.tee $3 i32.store end - br $while-continue|08 + br $while-continue|17 end end local.get $1 @@ -1380,10 +1380,10 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|012 + loop $while-continue|110 call $while/Ref#constructor if - block $while-break|011 + block $while-break|19 local.get $3 i32.const 1 i32.add @@ -1393,9 +1393,9 @@ if i32.const 0 local.set $1 - br $while-break|011 + br $while-break|19 end - br $while-continue|012 + br $while-continue|110 end end end From 81a63e334be64294831b8a41bbc887b8c6eb5d45 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 10:22:34 +0800 Subject: [PATCH 28/55] fix: unused variant --- src/flow.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index f481d464fb..c9619753e7 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -63,12 +63,6 @@ import { getSelectElse, getCallTarget, getLocalSetIndex, - getIfCondition, - getUnaryValue, - getCallOperandAt, - getCallOperandCount, - isConstZero, - isConstNonZero } from "./module"; import { @@ -88,11 +82,7 @@ import { uniqueMap } from "./util"; -import { - BuiltinNames -} from "./builtins"; import { NarrowedTypeMap, TypeMergeMode, TypeNarrowChecker } from "./narrow"; -import { _BinaryenExpressionPrint } from "./glue/binaryen"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { From 66252892cf9064ea166a02d4f692654cb47a66f5 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 16:32:52 +0800 Subject: [PATCH 29/55] fix bugs --- src/compiler.ts | 12 ++- src/flow.ts | 7 +- src/narrow.ts | 241 ++++++++++++++++++++++-------------------------- 3 files changed, 125 insertions(+), 135 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index a1422369b4..a838f26f2d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -6192,7 +6192,7 @@ export class Compiler extends DiagnosticEmitter { narrowedType = valueType.nonNullableType; } flow.setNarrowedType(local, narrowedType); - flow.setConditionNarrowedType(expressionRef, local, null); + flow.setAssignType(expressionRef, local, narrowedType); } return expressionRef; } @@ -6227,8 +6227,14 @@ export class Compiler extends DiagnosticEmitter { valueExpr ); } - flow.setNarrowedType(global, valueType); - flow.setConditionNarrowedType(expressionRef, global, null); + if (type.isReference) { + let narrowedType = valueType; + if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) { + narrowedType = valueType.nonNullableType; + } + flow.setNarrowedType(global, narrowedType); + flow.setAssignType(expressionRef, global, narrowedType); + } return expressionRef; } diff --git a/src/flow.ts b/src/flow.ts index c9619753e7..6eb916ff64 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -635,11 +635,14 @@ export class Flow { return narrowedTypes.get(element); } - /** type == null means this expr is a assign expression and will insert a toxic and disable the other check */ - setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type | null): void { + setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { if (type && !type.isReference) return; this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); } + setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { + if (type && !type.isReference) return; + this.conditionalNarrowedType.setAssignType(expr, element, type); + } inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi, this); if (condiNarrow.size != 0) { diff --git a/src/narrow.ts b/src/narrow.ts index 6fdbc6a50d..2384c97600 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -118,8 +118,7 @@ export class NarrowedTypeMap { let key = bKeys[i]; let aType = aMap.has(key) ? assert(aMap.get(key)) : null; let bType = assert(bMap.get(key)); - let mergedType = - mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); if (mergedType) { aMap.set(key, mergedType); } else { @@ -127,6 +126,18 @@ export class NarrowedTypeMap { } } } + mergeElement(narrowedElement: NarrowedTypeElement, mode: TypeMergeMode = TypeMergeMode.OR): void { + let thisTypeMap = this.typeMap; + let element = narrowedElement.element; + let aType = thisTypeMap.has(element) ? assert(this.typeMap.get(element)) : null; + let bType = narrowedElement.type; + let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + if (mergedType) { + thisTypeMap.set(element, mergedType); + } else { + thisTypeMap.delete(element); + } + } toString(): string { let key = Map_keys(this.typeMap); @@ -139,61 +150,51 @@ export class NarrowedTypeMap { } } +class NarrowedTypeElement { + constructor(public element: TypedElement, public type: Type) {} +} + export class TypeNarrowChecker { - expressionMap: Map = new Map(); + expressionMap: Map = new Map(); + assignMap: Map = new Map(); + setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { + let expressionMap = this.expressionMap; + if (expr <= 0) return; + assert(!expressionMap.has(expr)); + expressionMap.set(expr, new NarrowedTypeElement(element, type)); + } + setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { + let assignMap = this.assignMap; + if (expr <= 0) return; + assert(!assignMap.has(expr)); + assignMap.set(expr, new NarrowedTypeElement(element, type)); + } removeConditionNarrowedType(element: TypedElement): void { - let maps = Map_values(this.expressionMap); - for (let i = 0, k = maps.length; i < k; i++) { - maps[i].delete(element); + let condiKeys = Map_keys(this.expressionMap); + let condiValues = Map_values(this.expressionMap); + for (let i = 0, k = condiValues.length; i < k; i++) { + if (condiValues[i].element == element) this.expressionMap.delete(condiKeys[i]); } - } - - /** - * case 1: type is nonnull, add condition type, eg t instanceof B - * case 2: type is null, remove condition type, eg t = new A() in some condition - */ - setConditionNarrowedType( - expr: ExpressionRef, - element: TypedElement, - type: Type | null - ): void { - let expressionMap = this.expressionMap; - if (expr > 0) { - if (!expressionMap.has(expr)) { - expressionMap.set(expr, new NarrowedTypeMap()); - } - let narrowMap = assert(expressionMap.get(expr)); - if (type) { - narrowMap.set(element, type); - } else { - narrowMap.set(element, Type.void); - } + let assignKeys = Map_keys(this.assignMap); + let assignValues = Map_values(this.assignMap); + for (let i = 0, k = assignValues.length; i < k; i++) { + if (assignValues[i].element == element) this.expressionMap.delete(assignKeys[i]); } } - collectNarrowedTypeIfTrue(expr: ExpressionRef, flow: Flow): NarrowedTypeMap { - let result = new NarrowedTypeMap(); - let expressionMap = assert(this.expressionMap); - if (expressionMap.has(expr)) { - let typeMap = assert(expressionMap.get(expr)); - result.merge(typeMap); - } + collectNarrowedTypeIfTrue( + expr: ExpressionRef, + flow: Flow, + typeMap: NarrowedTypeMap | null = null, + nonnullCheck: bool = true + ): NarrowedTypeMap { + if (typeMap == null) typeMap = new NarrowedTypeMap(); + // visit children switch (getExpressionId(expr)) { case ExpressionId.LocalSet: { if (!isLocalTee(expr)) break; - let subMap = this.collectNarrowedTypeIfTrue( - getLocalSetValue(expr), - flow - ); - result.merge(subMap); - let local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - result.setNonnull(local); - break; - } - case ExpressionId.LocalGet: { - let local = flow.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - result.setNonnull(local); + this.collectNarrowedTypeIfTrue(getLocalSetValue(expr), flow, typeMap); break; } case ExpressionId.If: { @@ -203,18 +204,16 @@ export class TypeNarrowChecker { if (ifFalse && isConstZero(ifFalse)) { // Logical AND: (if (condition ifTrue 0)) // the only way this had become true is if condition and ifTrue are true - let subMap = this.collectNarrowedTypeIfTrue(condition, flow); - let subMapTrue = this.collectNarrowedTypeIfTrue(ifTrue, flow); - subMap.merge(subMapTrue); - result.merge(subMap); + this.collectNarrowedTypeIfTrue(condition, flow, typeMap); + this.collectNarrowedTypeIfTrue(ifTrue, flow, typeMap); } if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMap = this.collectNarrowedTypeIfTrue(condition, flow); - let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse, flow); - subMap.merge(subMapFalse, TypeMergeMode.AND); - result.merge(subMap); + let subMapTrue = this.collectNarrowedTypeIfTrue(condition, flow, typeMap.clone()); + let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse, flow, typeMap.clone()); + subMapTrue.merge(subMapFalse, TypeMergeMode.AND); + typeMap.merge(subMapTrue); } break; } @@ -222,11 +221,8 @@ export class TypeNarrowChecker { switch (getUnaryOp(expr)) { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { - let subMap = this.collectNarrowedTypeIfFalse( - getUnaryValue(expr), - flow - ); // !value -> value must have been false - result.merge(subMap); + this.collectNarrowedTypeIfFalse(getUnaryValue(expr), flow, typeMap); // !value -> value must have been false + break; } } @@ -239,11 +235,9 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // TRUE == right -> right must have been true } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left == TRUE -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // left == TRUE -> left must have been true } break; } @@ -252,11 +246,9 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // TRUE == right -> right must have been true } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // TRUE == right -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // TRUE == right -> right must have been true } break; } @@ -271,56 +263,68 @@ export class TypeNarrowChecker { let left = getCallOperandAt(expr, 0); let right = getCallOperandAt(expr, 1); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // TRUE == right -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // TRUE == right -> right must have been true } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left == TRUE -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // left == TRUE -> left must have been true } } else if (name == BuiltinNames.String_ne) { assert(getCallOperandCount(expr) == 2); let left = getCallOperandAt(expr, 0); let right = getCallOperandAt(expr, 1); if (isConstZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // FALSE != right -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // FALSE != right -> right must have been true } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // left != FALSE -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // left != FALSE -> left must have been true } } else if (name == BuiltinNames.String_not) { assert(getCallOperandCount(expr) == 1); - let subMap = this.collectNarrowedTypeIfFalse( - getCallOperandAt(expr, 0), - flow - ); // !value -> value must have been false - result.merge(subMap); + this.collectNarrowedTypeIfFalse(getCallOperandAt(expr, 0), flow, typeMap); // !value -> value must have been false } else if (name == BuiltinNames.tostack) { assert(getCallOperandCount(expr) == 1); - let subMap = this.collectNarrowedTypeIfTrue( - getCallOperandAt(expr, 0), - flow - ); - result.merge(subMap); + this.collectNarrowedTypeIfTrue(getCallOperandAt(expr, 0), flow, typeMap); } break; } } - return result; + + // update expr + const expressionMap = this.expressionMap; + if (expressionMap.has(expr)) { + const narrowedTypeElement = assert(expressionMap.get(expr)); + typeMap.mergeElement(narrowedTypeElement); + } + const assignMap = this.assignMap; + if (assignMap.has(expr)) { + const assignTypeElement = assert(assignMap.get(expr)); + typeMap.set(assignTypeElement.element, assignTypeElement.type); + } + + // nullable check + if (nonnullCheck) { + switch (getExpressionId(expr)) { + case ExpressionId.LocalSet: { + const local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + typeMap.setNonnull(local); + break; + } + case ExpressionId.LocalGet: { + const local = flow.parentFunction.localsByIndex[getLocalGetIndex(expr)]; + typeMap.setNonnull(local); + } + } + } + return typeMap; } - collectNarrowedTypeIfFalse(expr: ExpressionRef, flow: Flow): NarrowedTypeMap { - let result = new NarrowedTypeMap(); + collectNarrowedTypeIfFalse(expr: ExpressionRef, flow: Flow, typeMap: NarrowedTypeMap | null = null): NarrowedTypeMap { + if (typeMap == null) typeMap = new NarrowedTypeMap(); switch (getExpressionId(expr)) { case ExpressionId.Unary: { switch (getUnaryOp(expr)) { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { - let subMap = this.collectNarrowedTypeIfTrue( - getUnaryValue(expr), - flow - ); // !value -> value must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(getUnaryValue(expr), flow, typeMap); // !value -> value must have been true + break; } } @@ -332,16 +336,8 @@ export class TypeNarrowChecker { if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMap = this.collectNarrowedTypeIfFalse( - getIfCondition(expr), - flow - ); - let subMapFalse = this.collectNarrowedTypeIfFalse( - getIfFalse(expr), - flow - ); - subMap.merge(subMapFalse); - result.merge(subMap); + this.collectNarrowedTypeIfFalse(getIfCondition(expr), flow, typeMap); + this.collectNarrowedTypeIfFalse(getIfFalse(expr), flow, typeMap); } break; } @@ -353,11 +349,9 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(FALSE == right) -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // !(FALSE == right) -> right must have been true } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left == FALSE) -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // !(left == FALSE) -> left must have been true } break; } @@ -366,11 +360,9 @@ export class TypeNarrowChecker { let left = getBinaryLeft(expr); let right = getBinaryRight(expr); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(TRUE != right) -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // !(TRUE != right) -> right must have been true } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left != TRUE) -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // !(left != TRUE) -> left must have been true } break; } @@ -385,40 +377,29 @@ export class TypeNarrowChecker { let left = getCallOperandAt(expr, 0); let right = getCallOperandAt(expr, 1); if (isConstZero(left)) { - this.collectNarrowedTypeIfTrue(right, flow); // !(FALSE == right) -> right must have been true + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // !(FALSE == right) -> right must have been true } else if (isConstZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left == FALSE) -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // !(left == FALSE) -> left must have been true } } else if (name == BuiltinNames.String_ne) { assert(getCallOperandCount(expr) == 2); let left = getCallOperandAt(expr, 0); let right = getCallOperandAt(expr, 1); if (isConstNonZero(left)) { - let subMap = this.collectNarrowedTypeIfTrue(right, flow); // !(TRUE != right) -> right must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(right, flow, typeMap); // !(TRUE != right) -> right must have been true } else if (isConstNonZero(right)) { - let subMap = this.collectNarrowedTypeIfTrue(left, flow); // !(left != TRUE) -> left must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(left, flow, typeMap); // !(left != TRUE) -> left must have been true } } else if (name == BuiltinNames.String_not) { assert(getCallOperandCount(expr) == 1); - let subMap = this.collectNarrowedTypeIfTrue( - getCallOperandAt(expr, 0), - flow - ); // !(!value) -> value must have been true - result.merge(subMap); + this.collectNarrowedTypeIfTrue(getCallOperandAt(expr, 0), flow, typeMap); // !(!value) -> value must have been true } else if (name == BuiltinNames.tostack) { assert(getCallOperandCount(expr) == 1); - let subMap = this.collectNarrowedTypeIfFalse( - getCallOperandAt(expr, 0), - flow - ); - result.merge(subMap); + this.collectNarrowedTypeIfFalse(getCallOperandAt(expr, 0), flow, typeMap); } break; } } - return result; + return typeMap; } } From 5b244a37c270c29eafce8cf7c528d282b07a2fa6 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 16:40:45 +0800 Subject: [PATCH 30/55] fix: split logic for type and nullable --- src/narrow.ts | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/narrow.ts b/src/narrow.ts index 2384c97600..a4e0fed592 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -25,32 +25,43 @@ import { UnaryOp, } from "./module"; import { TypedElement } from "./program"; -import { Type } from "./types"; +import { Type, TypeFlags } from "./types"; export function typeAnd(a: Type | null, b: Type | null): Type | null { if (a == null || b == null) { return null; - } else if (a.isAssignableTo(b)) { - return b; - } else if (b.isAssignableTo(a)) { - return a; } else { - return null; + const nonnulla = a.nonNullableType; + const nonnullb = b.nonNullableType; + const nullable = a.is(TypeFlags.NULLABLE) || b.is(TypeFlags.NULLABLE); + if (nonnulla.isAssignableTo(nonnullb)) { + return nullable ? b.nullableType : nonnullb; + } else if (nonnullb.isAssignableTo(nonnulla)) { + return nullable ? a.nullableType : nonnulla; + } else { + return null; + } } } + export function typeOr(a: Type | null, b: Type | null): Type | null { if (a == null) { return b; } else if (b == null) { return a; - } else if (a.isAssignableTo(b)) { - // a extends b - return a; - } else if (b.isAssignableTo(a)) { - // b extends a - return b; } else { - return null; + const nonnulla = a.nonNullableType; + const nonnullb = b.nonNullableType; + const nullable = a.is(TypeFlags.NULLABLE) && b.is(TypeFlags.NULLABLE); + if (nonnulla.isAssignableTo(nonnullb)) { + // a extends b + return nullable ? a.nullableType : nonnulla; + } else if (nonnullb.isAssignableTo(nonnulla)) { + // b extends a + return nullable ? b.nullableType : nonnullb; + } else { + return null; + } } } From 403cae846a804c829b70bd2de388c3286cfdb899 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 17:06:11 +0800 Subject: [PATCH 31/55] docs: add comment for function --- src/compiler.ts | 2 - src/flow.ts | 39 +++++++++++------- src/narrow.ts | 104 +++++++++++++++++++++++++++--------------------- 3 files changed, 82 insertions(+), 63 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index a838f26f2d..cce209e798 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -6191,7 +6191,6 @@ export class Compiler extends DiagnosticEmitter { if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) { narrowedType = valueType.nonNullableType; } - flow.setNarrowedType(local, narrowedType); flow.setAssignType(expressionRef, local, narrowedType); } return expressionRef; @@ -6232,7 +6231,6 @@ export class Compiler extends DiagnosticEmitter { if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) { narrowedType = valueType.nonNullableType; } - flow.setNarrowedType(global, narrowedType); flow.setAssignType(expressionRef, global, narrowedType); } return expressionRef; diff --git a/src/flow.ts b/src/flow.ts index 6eb916ff64..fb1930c52c 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -82,7 +82,7 @@ import { uniqueMap } from "./util"; -import { NarrowedTypeMap, TypeMergeMode, TypeNarrowChecker } from "./narrow"; +import { NarrowedTypeMap, TypeNarrowChecker } from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { @@ -614,11 +614,13 @@ export class Flow { localFlags[index] = flags & ~flag; } - removeNarrowedType(element: TypedElement): void { - let thisNarrowedTypes = this.narrowedTypes; - if (thisNarrowedTypes) thisNarrowedTypes.delete(element); - this.conditionalNarrowedType.removeConditionNarrowedType(element); + /** set type assign */ + setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { + if (type && !type.isReference) return; + this.setNarrowedType(element, type); + this.conditionalNarrowedType.setAssignType(expr, element, type); } + /** set type narrow */ setNarrowedType(element: TypedElement, type: Type): void { if (!type.isReference) return; if (this.narrowedTypes == null) { @@ -627,6 +629,7 @@ export class Flow { let narrowedTypes = assert(this.narrowedTypes); narrowedTypes.set(element, type); } + /** get type narrow, return null if not exist */ getNarrowedType(element: TypedElement): Type | null { if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); @@ -634,15 +637,20 @@ export class Flow { let narrowedTypes = assert(this.narrowedTypes); return narrowedTypes.get(element); } + /** do not trace `element` type narrow */ + removeNarrowedType(element: TypedElement): void { + let thisNarrowedTypes = this.narrowedTypes; + if (thisNarrowedTypes) thisNarrowedTypes.delete(element); + this.conditionalNarrowedType.removeElement(element); + } + /** set conditional type narrow */ setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { if (type && !type.isReference) return; this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); } - setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { - if (type && !type.isReference) return; - this.conditionalNarrowedType.setAssignType(expr, element, type); - } + + /** take effect conditional type narrow if condition is true */ inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi, this); if (condiNarrow.size != 0) { @@ -650,17 +658,18 @@ export class Flow { this.narrowedTypes = new NarrowedTypeMap(); } let narrowedTypes = assert(this.narrowedTypes); - narrowedTypes.merge(condiNarrow); + narrowedTypes.mergeOr(condiNarrow); } } - inheritNarrowedTypeIfFalse(condi:ExpressionRef): void { + /** take effect conditional type narrow if condition is false */ + inheritNarrowedTypeIfFalse(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi, this); if (condiNarrow.size != 0) { if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); } let narrowedTypes = assert(this.narrowedTypes); - narrowedTypes.merge(condiNarrow); + narrowedTypes.mergeOr(condiNarrow); } } @@ -872,7 +881,7 @@ export class Flow { let thisNarrowedTypes = this.narrowedTypes; let otherNarrowedTypes = other.narrowedTypes; if (thisNarrowedTypes && otherNarrowedTypes) { - thisNarrowedTypes.merge(otherNarrowedTypes, TypeMergeMode.AND); + thisNarrowedTypes.mergeAnd(otherNarrowedTypes); } // field flags do not matter here since there's only INITIALIZED, which can @@ -1007,7 +1016,7 @@ export class Flow { let rightNarrowedTypes = right.narrowedTypes; if (leftNarrowedTypes && rightNarrowedTypes) { let narrowedTypes = rightNarrowedTypes.clone(); - narrowedTypes.merge(leftNarrowedTypes, TypeMergeMode.AND); + narrowedTypes.mergeAnd(leftNarrowedTypes); this.narrowedTypes = narrowedTypes; } } @@ -1076,7 +1085,7 @@ export class Flow { let thisNarrowedTypes = this.narrowedTypes; let otherNarrowedTypes = other.narrowedTypes; if (thisNarrowedTypes && otherNarrowedTypes) { - thisNarrowedTypes.merge(otherNarrowedTypes, TypeMergeMode.AND); + thisNarrowedTypes.mergeAnd(otherNarrowedTypes); } } diff --git a/src/narrow.ts b/src/narrow.ts index a4e0fed592..a2238c33c4 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -27,6 +27,7 @@ import { import { TypedElement } from "./program"; import { Type, TypeFlags } from "./types"; +/** both a and b can assign to return type */ export function typeAnd(a: Type | null, b: Type | null): Type | null { if (a == null || b == null) { return null; @@ -44,6 +45,7 @@ export function typeAnd(a: Type | null, b: Type | null): Type | null { } } +/** return type can assign a and b, aggressive type narrow */ export function typeOr(a: Type | null, b: Type | null): Type | null { if (a == null) { return b; @@ -65,11 +67,6 @@ export function typeOr(a: Type | null, b: Type | null): Type | null { } } -export enum TypeMergeMode { - AND, - OR, -} - export class NarrowedTypeMap { private typeMap: Map = new Map(); get size(): i32 { @@ -89,6 +86,7 @@ export class NarrowedTypeMap { set(element: TypedElement, type: Type): void { this.typeMap.set(element, type); } + /** set TypedElement as non-nullable */ setNonnull(typedElement: TypedElement): void { let typeMap = this.typeMap; if (typeMap.has(typedElement)) { @@ -112,24 +110,38 @@ export class NarrowedTypeMap { } return other; } - merge(other: NarrowedTypeMap, mode: TypeMergeMode = TypeMergeMode.OR): void { + mergeOr(other: NarrowedTypeMap): void { let aMap = this.typeMap; let bMap = other.typeMap; let bKeys = Map_keys(bMap); - if (mode == TypeMergeMode.AND) { - let aKeys = Map_keys(aMap); - for (let i = 0, k = aKeys.length; i < k; i++) { - let akey = aKeys[i]; - if (!bMap.has(akey)) { - aMap.delete(akey); - } + for (let i = 0, k = bKeys.length; i < k; i++) { + let key = bKeys[i]; + let aType = aMap.has(key) ? assert(aMap.get(key)) : null; + let bType = assert(bMap.get(key)); + let mergedType = typeOr(aType, bType); + if (mergedType) { + aMap.set(key, mergedType); + } else { + aMap.delete(key); + } + } + } + mergeAnd(other: NarrowedTypeMap): void { + let aMap = this.typeMap; + let bMap = other.typeMap; + let bKeys = Map_keys(bMap); + let aKeys = Map_keys(aMap); + for (let i = 0, k = aKeys.length; i < k; i++) { + let akey = aKeys[i]; + if (!bMap.has(akey)) { + aMap.delete(akey); } } for (let i = 0, k = bKeys.length; i < k; i++) { let key = bKeys[i]; let aType = aMap.has(key) ? assert(aMap.get(key)) : null; let bType = assert(bMap.get(key)); - let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + let mergedType = typeAnd(aType, bType); if (mergedType) { aMap.set(key, mergedType); } else { @@ -137,12 +149,13 @@ export class NarrowedTypeMap { } } } - mergeElement(narrowedElement: NarrowedTypeElement, mode: TypeMergeMode = TypeMergeMode.OR): void { + + mergeElementOr(narrowedElement: NarrowedTypeElement): void { let thisTypeMap = this.typeMap; let element = narrowedElement.element; let aType = thisTypeMap.has(element) ? assert(this.typeMap.get(element)) : null; let bType = narrowedElement.type; - let mergedType = mode == TypeMergeMode.OR ? typeOr(aType, bType) : typeAnd(aType, bType); + let mergedType = typeOr(aType, bType); if (mergedType) { thisTypeMap.set(element, mergedType); } else { @@ -166,40 +179,40 @@ class NarrowedTypeElement { } export class TypeNarrowChecker { - expressionMap: Map = new Map(); + /** expression in condition, meeting this expr means type can be narrowed */ + condiMap: Map = new Map(); + /** expression in condition, meeting this expr means element assigned as a type */ assignMap: Map = new Map(); + /** set conditional narrowed type */ setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { - let expressionMap = this.expressionMap; + let condiMap = this.condiMap; if (expr <= 0) return; - assert(!expressionMap.has(expr)); - expressionMap.set(expr, new NarrowedTypeElement(element, type)); + assert(!condiMap.has(expr)); + condiMap.set(expr, new NarrowedTypeElement(element, type)); } + /** set type of assigned element */ setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { let assignMap = this.assignMap; if (expr <= 0) return; assert(!assignMap.has(expr)); assignMap.set(expr, new NarrowedTypeElement(element, type)); } - removeConditionNarrowedType(element: TypedElement): void { - let condiKeys = Map_keys(this.expressionMap); - let condiValues = Map_values(this.expressionMap); + /** remove TypedElement from mapping, called when element is no longer used or re-used */ + removeElement(element: TypedElement): void { + let condiKeys = Map_keys(this.condiMap); + let condiValues = Map_values(this.condiMap); for (let i = 0, k = condiValues.length; i < k; i++) { - if (condiValues[i].element == element) this.expressionMap.delete(condiKeys[i]); + if (condiValues[i].element == element) this.condiMap.delete(condiKeys[i]); } let assignKeys = Map_keys(this.assignMap); let assignValues = Map_values(this.assignMap); for (let i = 0, k = assignValues.length; i < k; i++) { - if (assignValues[i].element == element) this.expressionMap.delete(assignKeys[i]); + if (assignValues[i].element == element) this.condiMap.delete(assignKeys[i]); } } - collectNarrowedTypeIfTrue( - expr: ExpressionRef, - flow: Flow, - typeMap: NarrowedTypeMap | null = null, - nonnullCheck: bool = true - ): NarrowedTypeMap { + collectNarrowedTypeIfTrue(expr: ExpressionRef, flow: Flow, typeMap: NarrowedTypeMap | null = null): NarrowedTypeMap { if (typeMap == null) typeMap = new NarrowedTypeMap(); // visit children switch (getExpressionId(expr)) { @@ -223,8 +236,8 @@ export class TypeNarrowChecker { // the only way this had become false is if condition and ifFalse are false let subMapTrue = this.collectNarrowedTypeIfTrue(condition, flow, typeMap.clone()); let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse, flow, typeMap.clone()); - subMapTrue.merge(subMapFalse, TypeMergeMode.AND); - typeMap.merge(subMapTrue); + subMapTrue.mergeAnd(subMapFalse); + typeMap.mergeOr(subMapTrue); } break; } @@ -299,10 +312,10 @@ export class TypeNarrowChecker { } // update expr - const expressionMap = this.expressionMap; + const expressionMap = this.condiMap; if (expressionMap.has(expr)) { const narrowedTypeElement = assert(expressionMap.get(expr)); - typeMap.mergeElement(narrowedTypeElement); + typeMap.mergeElementOr(narrowedTypeElement); } const assignMap = this.assignMap; if (assignMap.has(expr)) { @@ -311,19 +324,18 @@ export class TypeNarrowChecker { } // nullable check - if (nonnullCheck) { - switch (getExpressionId(expr)) { - case ExpressionId.LocalSet: { - const local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - typeMap.setNonnull(local); - break; - } - case ExpressionId.LocalGet: { - const local = flow.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - typeMap.setNonnull(local); - } + switch (getExpressionId(expr)) { + case ExpressionId.LocalSet: { + const local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + typeMap.setNonnull(local); + break; + } + case ExpressionId.LocalGet: { + const local = flow.parentFunction.localsByIndex[getLocalGetIndex(expr)]; + typeMap.setNonnull(local); } } + return typeMap; } From 9115a5cd171864e45c8b98871f326c0e3278a9cf Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 28 Jul 2022 17:25:37 +0800 Subject: [PATCH 32/55] Update src/compiler.ts Co-authored-by: Max Graey --- src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index cce209e798..3c6ddaed56 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9296,7 +9296,7 @@ export class Compiler extends DiagnosticEmitter { var outerFlow = this.currentFlow; var ifThenFlow = outerFlow.fork(); - ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); + ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); this.currentFlow = ifThenFlow; var ifThenExpr = this.compileExpression(ifThen, ctxType); var ifThenType = this.currentType; From a01d883ce41adc6033575c6b0a4643330aafd4ef Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 4 Aug 2022 00:02:54 +0800 Subject: [PATCH 33/55] feat: add type infer for `||` operator --- src/compiler.ts | 10 +++-- src/flow.ts | 8 ++-- tests/compiler/possibly-null.debug.wat | 48 +++++++++++++++++++++ tests/compiler/possibly-null.release.wat | 54 ++++++++++++++++++++++++ tests/compiler/possibly-null.ts | 5 +++ 5 files changed, 117 insertions(+), 8 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 3c6ddaed56..11e4ebff4f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -213,6 +213,8 @@ import { ShadowStackPass } from "./passes/shadowstack"; +import { typeOr } from "./narrow"; + /** Compiler options. */ export class Options { constructor() { /* as internref */ } @@ -3141,7 +3143,7 @@ export class Compiler extends DiagnosticEmitter { } if (initExpr) { initializers.push( - this.makeLocalAssignment(local, initExpr, initType ? initType : type, false) + this.makeLocalAssignment(local, initExpr, initType || type, false) ); } else { // no need to assign zero @@ -4669,7 +4671,7 @@ export class Compiler extends DiagnosticEmitter { ); flow.freeTempLocal(temp); } - this.currentType = leftType; + this.currentType = assert(typeOr(leftType.nonNullableType, rightType)); } break; } @@ -7788,7 +7790,7 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.LOCAL: { let local = target; let narrowedType = flow.getNarrowedType(local); - let localType = narrowedType ? narrowedType : local.type; + let localType = narrowedType || local.type; assert(localType != Type.void); if (this.pendingElements.has(local)) { this.error( @@ -7823,7 +7825,7 @@ export class Compiler extends DiagnosticEmitter { return module.unreachable(); } let narrowedType = flow.getNarrowedType(global); - let globalType = narrowedType ? narrowedType : global.type; + let globalType = narrowedType || global.type; if (this.pendingElements.has(global)) { this.error( DiagnosticCode.Variable_0_used_before_its_declaration, diff --git a/src/flow.ts b/src/flow.ts index fb1930c52c..4f3d5552ed 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -1061,9 +1061,9 @@ export class Flow { let beforeNarrowedTypes = before.narrowedTypes; let afterNarrowedTypes = after.narrowedTypes; let beforeType = beforeNarrowedTypes ? beforeNarrowedTypes.get(local) : null; - beforeType = beforeType ? beforeType : local.type; + beforeType = beforeType || local.type; let afterType = afterNarrowedTypes ? afterNarrowedTypes.get(local) : null; - afterType = afterType ? afterType : local.type; + afterType = afterType || local.type; if (type.isNullableReference) { if (beforeType != afterType) { return true; @@ -1101,13 +1101,13 @@ export class Flow { if (!isLocalTee(expr)) break; let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; - localType = localType ? localType : local.type; + localType = localType || local.type; return !localType.isNullableReference; } case ExpressionId.LocalGet: { let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; - localType = localType ? localType : local.type; + localType = localType || local.type; return !localType.isNullableReference; } } diff --git a/tests/compiler/possibly-null.debug.wat b/tests/compiler/possibly-null.debug.wat index d97c2aeb73..ab572a0db3 100644 --- a/tests/compiler/possibly-null.debug.wat +++ b/tests/compiler/possibly-null.debug.wat @@ -30,6 +30,7 @@ (export "testLogicalOrMulti" (func $export:possibly-null/testLogicalOrMulti)) (export "testAssign" (func $export:possibly-null/testAssign)) (export "testNeverNull" (func $export:possibly-null/testNeverNull)) + (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) (func $possibly-null/testTrue (param $0 i32) local.get $0 if @@ -258,6 +259,33 @@ drop end ) + (func $possibly-null/testLogicalOrTypeInfer (param $0 i32) (param $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $0 + if (result i32) + local.get $0 + else + local.get $1 + end + local.tee $2 + i32.store + local.get $2 + call $possibly-null/requireNonNull + drop + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $~stack_check global.get $~lib/memory/__stack_pointer global.get $~lib/memory/__data_end @@ -579,4 +607,24 @@ i32.add global.set $~lib/memory/__stack_pointer ) + (func $export:possibly-null/testLogicalOrTypeInfer (param $0 i32) (param $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $0 + local.get $1 + call $possibly-null/testLogicalOrTypeInfer + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + ) ) diff --git a/tests/compiler/possibly-null.release.wat b/tests/compiler/possibly-null.release.wat index 42cc8af75c..3e92d6d3c3 100644 --- a/tests/compiler/possibly-null.release.wat +++ b/tests/compiler/possibly-null.release.wat @@ -24,6 +24,7 @@ (export "testLogicalOrMulti" (func $export:possibly-null/testLogicalAndMulti)) (export "testAssign" (func $export:possibly-null/testLogicalAndMulti)) (export "testNeverNull" (func $export:possibly-null/testTrue)) + (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) (func $export:possibly-null/testTrue (param $0 i32) (local $1 i32) global.get $~lib/memory/__stack_pointer @@ -188,4 +189,57 @@ i32.add global.set $~lib/memory/__stack_pointer ) + (func $export:possibly-null/testLogicalOrTypeInfer (param $0 i32) (param $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1024 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $2 + local.get $0 + i32.store + local.get $2 + local.get $1 + i32.store offset=4 + local.get $2 + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1024 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $2 + i32.const 0 + i32.store + local.get $2 + local.get $0 + local.get $1 + local.get $0 + select + i32.store + local.get $2 + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + return + end + i32.const 17440 + i32.const 17488 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) ) diff --git a/tests/compiler/possibly-null.ts b/tests/compiler/possibly-null.ts index 4e1d79a989..e1ce355fac 100644 --- a/tests/compiler/possibly-null.ts +++ b/tests/compiler/possibly-null.ts @@ -125,3 +125,8 @@ export function testNeverNull(a: Ref | null): void { a!; // INFO AS225: Expression is never 'null'. } } + +export function testLogicalOrTypeInfer(a: Ref | null, b: Ref): void { + let c: Ref = a || b; + requireNonNull(c); +} From 4824b4452271fc4a635b2035760e087499bde680 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 4 Aug 2022 07:09:06 +0800 Subject: [PATCH 34/55] Update src/flow.ts Co-authored-by: Max Graey --- src/flow.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/flow.ts b/src/flow.ts index 4f3d5552ed..61c1eae81f 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -82,7 +82,10 @@ import { uniqueMap } from "./util"; -import { NarrowedTypeMap, TypeNarrowChecker } from "./narrow"; +import { + NarrowedTypeMap, + TypeNarrowChecker +} from "./narrow"; /** Control flow flags indicating specific conditions. */ export const enum FlowFlags { From 9b23ad26cff2ad7d91ddfa09ab1d83417de954a9 Mon Sep 17 00:00:00 2001 From: Max Graey Date: Thu, 4 Aug 2022 05:58:02 +0300 Subject: [PATCH 35/55] Update src/narrow.ts --- src/narrow.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/narrow.ts b/src/narrow.ts index a2238c33c4..20e2438d49 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -347,7 +347,6 @@ export class TypeNarrowChecker { case UnaryOp.EqzI32: case UnaryOp.EqzI64: { this.collectNarrowedTypeIfTrue(getUnaryValue(expr), flow, typeMap); // !value -> value must have been true - break; } } From 4ea58aaca3348adcec2b924af42c6eecf65f1734 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 15:49:41 +0800 Subject: [PATCH 36/55] revert: testcase update in 2abc9a4a81be4e30c1fb3386e598626406c3f3e5 --- tests/compiler/class-overloading.debug.wat | 199 ++------ tests/compiler/class-overloading.release.wat | 490 +++++++++++-------- tests/compiler/for.debug.wat | 9 +- tests/compiler/for.release.wat | 12 +- tests/compiler/std/math.release.wat | 36 +- tests/compiler/while.debug.wat | 12 +- tests/compiler/while.release.wat | 12 +- 7 files changed, 348 insertions(+), 422 deletions(-) diff --git a/tests/compiler/class-overloading.debug.wat b/tests/compiler/class-overloading.debug.wat index 4e597e53d8..44d8f98102 100644 --- a/tests/compiler/class-overloading.debug.wat +++ b/tests/compiler/class-overloading.debug.wat @@ -2320,47 +2320,47 @@ i32.const 592 global.set $class-overloading/which ) - (func $class-overloading/B#b (param $0 i32) (param $1 i32) - i32.const 496 - global.set $class-overloading/which + (func $class-overloading/IA#foo (param $0 i32) + unreachable ) - (func $class-overloading/B#get:c (param $0 i32) (result i32) - i32.const 496 + (func $class-overloading/A2#foo (param $0 i32) (result i32) + i32.const 720 + i32.const 528 + i32.const 198 + i32.const 5 + call $~lib/builtins/abort + unreachable + ) + (func $class-overloading/F#a (param $0 i32) (param $1 i32) + i32.const 624 global.set $class-overloading/which - i32.const 0 ) - (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) + (func $class-overloading/B#b (param $0 i32) (param $1 i32) i32.const 496 global.set $class-overloading/which ) - (func $class-overloading/F#a (param $0 i32) (param $1 i32) + (func $class-overloading/F#b (param $0 i32) (param $1 i32) i32.const 624 global.set $class-overloading/which ) - (func $class-overloading/F#b (param $0 i32) (param $1 i32) - i32.const 624 + (func $class-overloading/B#get:c (param $0 i32) (result i32) + i32.const 496 global.set $class-overloading/which + i32.const 0 ) (func $class-overloading/F#get:c (param $0 i32) (result i32) i32.const 624 global.set $class-overloading/which i32.const 0 ) + (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) + i32.const 496 + global.set $class-overloading/which + ) (func $class-overloading/F#set:c (param $0 i32) (param $1 i32) i32.const 624 global.set $class-overloading/which ) - (func $class-overloading/IA#foo (param $0 i32) - unreachable - ) - (func $class-overloading/A2#foo (param $0 i32) (result i32) - i32.const 720 - i32.const 528 - i32.const 198 - i32.const 5 - call $~lib/builtins/abort - unreachable - ) (func $class-overloading/CA#foo (param $0 i32) i32.const 656 global.set $class-overloading/which @@ -2588,139 +2588,6 @@ local.get $1 call $class-overloading/A#set:c ) - (func $class-overloading/B#a@virtual (param $0 i32) (param $1 i32) - (local $2 i32) - block $default - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.set $2 - local.get $2 - i32.const 5 - i32.eq - br_if $case0 - local.get $2 - i32.const 8 - i32.eq - br_if $case1 - br $default - end - local.get $0 - local.get $1 - call $class-overloading/C#a - return - end - local.get $0 - local.get $1 - call $class-overloading/F#a - return - end - local.get $0 - local.get $1 - call $class-overloading/B#a - ) - (func $class-overloading/B#b@virtual (param $0 i32) (param $1 i32) - (local $2 i32) - block $default - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.set $2 - local.get $2 - i32.const 5 - i32.eq - br_if $case0 - local.get $2 - i32.const 8 - i32.eq - br_if $case1 - br $default - end - local.get $0 - local.get $1 - call $class-overloading/C#b - return - end - local.get $0 - local.get $1 - call $class-overloading/F#b - return - end - local.get $0 - local.get $1 - call $class-overloading/B#b - ) - (func $class-overloading/B#get:c@virtual (param $0 i32) (result i32) - (local $1 i32) - block $default - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.set $1 - local.get $1 - i32.const 5 - i32.eq - br_if $case0 - local.get $1 - i32.const 8 - i32.eq - br_if $case1 - br $default - end - local.get $0 - call $class-overloading/C#get:c - return - end - local.get $0 - call $class-overloading/F#get:c - return - end - local.get $0 - call $class-overloading/B#get:c - ) - (func $class-overloading/B#set:c@virtual (param $0 i32) (param $1 i32) - (local $2 i32) - block $default - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.set $2 - local.get $2 - i32.const 5 - i32.eq - br_if $case0 - local.get $2 - i32.const 8 - i32.eq - br_if $case1 - br $default - end - local.get $0 - local.get $1 - call $class-overloading/C#set:c - return - end - local.get $0 - local.get $1 - call $class-overloading/F#set:c - return - end - local.get $0 - local.get $1 - call $class-overloading/B#set:c - ) (func $class-overloading/IA#foo@virtual (param $0 i32) (local $1 i32) block $default @@ -3282,7 +3149,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#a@virtual + call $class-overloading/A#a@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3314,7 +3181,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#b@virtual + call $class-overloading/A#b@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3345,7 +3212,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/B#get:c@virtual + call $class-overloading/A#get:c@virtual drop global.get $class-overloading/which local.set $0 @@ -3376,7 +3243,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#set:c@virtual + call $class-overloading/A#set:c@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3411,7 +3278,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#a@virtual + call $class-overloading/A#a@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3443,7 +3310,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#b@virtual + call $class-overloading/A#b@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3474,7 +3341,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/B#get:c@virtual + call $class-overloading/A#get:c@virtual drop global.get $class-overloading/which local.set $0 @@ -3505,7 +3372,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/B#set:c@virtual + call $class-overloading/A#set:c@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3540,7 +3407,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/F#a + call $class-overloading/A#a@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3572,7 +3439,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/F#b + call $class-overloading/A#b@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer @@ -3603,7 +3470,7 @@ local.get $0 i32.store local.get $0 - call $class-overloading/F#get:c + call $class-overloading/A#get:c@virtual drop global.get $class-overloading/which local.set $0 @@ -3636,7 +3503,7 @@ i32.store local.get $0 i32.const 1 - call $class-overloading/F#set:c + call $class-overloading/A#set:c@virtual global.get $class-overloading/which local.set $0 global.get $~lib/memory/__stack_pointer diff --git a/tests/compiler/class-overloading.release.wat b/tests/compiler/class-overloading.release.wat index ba054d3473..ff4df62b21 100644 --- a/tests/compiler/class-overloading.release.wat +++ b/tests/compiler/class-overloading.release.wat @@ -1402,22 +1402,22 @@ local.get $3 i32.eqz ) - (func $class-overloading/B#a@virtual (param $0 i32) + (func $class-overloading/A#a@virtual (param $0 i32) block $default - block $case1 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case1 - br $default + block $case2 + block $case1 + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case0 $case1 $case0 $case0 $case2 $default + end + i32.const 1520 + global.set $class-overloading/which + return end call $class-overloading/C#a return @@ -1426,7 +1426,7 @@ global.set $class-overloading/which return end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which ) (func $~lib/rt/__visit_members (param $0 i32) @@ -1608,42 +1608,16 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#a@virtual - block $default - block $case2 - block $case1 - block $case0 - local.get $0 - i32.const 8 - i32.sub - i32.load - i32.const 4 - i32.sub - br_table $case0 $case1 $case0 $case0 $case2 $default - end - i32.const 1520 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#a@virtual - end - call $class-overloading/C#a - br $__inlined_func$class-overloading/A#a@virtual - end - i32.const 1648 - global.set $class-overloading/which - br $__inlined_func$class-overloading/A#a@virtual - end - i32.const 1488 - global.set $class-overloading/which - end + local.get $0 + call $class-overloading/A#a@virtual global.get $~lib/memory/__stack_pointer - local.tee $0 global.get $class-overloading/which - local.tee $1 + local.tee $0 i32.store - local.get $0 + global.get $~lib/memory/__stack_pointer i32.const 1520 i32.store offset=4 - local.get $1 + local.get $0 i32.const 1520 call $~lib/string/String.__eq i32.eqz @@ -1662,17 +1636,17 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/A#b@virtual - block $default6 - block $case27 - block $case18 - block $case09 + block $default + block $case2 + block $case1 + block $case0 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case09 $case18 $case09 $case09 $case27 $default6 + br_table $case0 $case1 $case0 $case0 $case2 $default end i32.const 1520 global.set $class-overloading/which @@ -1716,17 +1690,17 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/A#get:c@virtual - block $default13 - block $case214 - block $case115 - block $case016 + block $default6 + block $case27 + block $case18 + block $case09 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case016 $case115 $case016 $case016 $case214 $default13 + br_table $case09 $case18 $case09 $case09 $case27 $default6 end i32.const 1520 global.set $class-overloading/which @@ -1769,30 +1743,30 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/A#b@virtual17 - block $default18 - block $case219 - block $case120 - block $case021 + block $__inlined_func$class-overloading/A#b@virtual10 + block $default11 + block $case212 + block $case113 + block $case014 local.get $0 i32.const 8 i32.sub i32.load i32.const 4 i32.sub - br_table $case021 $case120 $case021 $case021 $case219 $default18 + br_table $case014 $case113 $case014 $case014 $case212 $default11 end i32.const 1520 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual17 + br $__inlined_func$class-overloading/A#b@virtual10 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual17 + br $__inlined_func$class-overloading/A#b@virtual10 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/A#b@virtual17 + br $__inlined_func$class-overloading/A#b@virtual10 end i32.const 1488 global.set $class-overloading/which @@ -1956,7 +1930,7 @@ local.tee $0 i32.store local.get $0 - call $class-overloading/B#a@virtual + call $class-overloading/A#a@virtual global.get $~lib/memory/__stack_pointer global.get $class-overloading/which local.tee $0 @@ -1982,32 +1956,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#b@virtual - block $default29 - block $case130 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case130 - br $default29 + block $__inlined_func$class-overloading/A#b@virtual22 + block $default23 + block $case224 + block $case125 + block $case026 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case026 $case125 $case026 $case026 $case224 $default23 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual22 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual + br $__inlined_func$class-overloading/A#b@virtual22 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual + br $__inlined_func$class-overloading/A#b@virtual22 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2036,32 +2010,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#get:c@virtual - block $default35 - block $case136 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case136 - br $default35 + block $__inlined_func$class-overloading/A#get:c@virtual31 + block $default32 + block $case233 + block $case134 + block $case035 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case035 $case134 $case035 $case035 $case233 $default32 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#get:c@virtual31 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#get:c@virtual + br $__inlined_func$class-overloading/A#get:c@virtual31 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#get:c@virtual + br $__inlined_func$class-overloading/A#get:c@virtual31 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2088,32 +2062,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#b@virtual41 - block $default42 - block $case143 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case143 - br $default42 + block $__inlined_func$class-overloading/A#b@virtual40 + block $default41 + block $case242 + block $case143 + block $case044 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case044 $case143 $case044 $case044 $case242 $default41 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual40 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual41 + br $__inlined_func$class-overloading/A#b@virtual40 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual41 + br $__inlined_func$class-overloading/A#b@virtual40 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2146,7 +2120,7 @@ local.tee $0 i32.store local.get $0 - call $class-overloading/B#a@virtual + call $class-overloading/A#a@virtual global.get $~lib/memory/__stack_pointer global.get $class-overloading/which local.tee $0 @@ -2172,32 +2146,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#b@virtual48 - block $default49 - block $case150 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case150 - br $default49 + block $__inlined_func$class-overloading/A#b@virtual49 + block $default50 + block $case251 + block $case152 + block $case053 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case053 $case152 $case053 $case053 $case251 $default50 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual49 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual48 + br $__inlined_func$class-overloading/A#b@virtual49 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual48 + br $__inlined_func$class-overloading/A#b@virtual49 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2226,32 +2200,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#get:c@virtual55 - block $default56 - block $case157 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case157 - br $default56 + block $__inlined_func$class-overloading/A#get:c@virtual58 + block $default59 + block $case260 + block $case161 + block $case062 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case062 $case161 $case062 $case062 $case260 $default59 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#get:c@virtual58 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#get:c@virtual55 + br $__inlined_func$class-overloading/A#get:c@virtual58 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#get:c@virtual55 + br $__inlined_func$class-overloading/A#get:c@virtual58 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2278,32 +2252,32 @@ global.get $class-overloading/a local.tee $0 i32.store - block $__inlined_func$class-overloading/B#b@virtual62 - block $default63 - block $case164 - local.get $0 - i32.const 8 - i32.sub - i32.load - local.tee $0 - i32.const 5 - i32.ne - if - local.get $0 - i32.const 8 - i32.eq - br_if $case164 - br $default63 + block $__inlined_func$class-overloading/A#b@virtual67 + block $default68 + block $case269 + block $case170 + block $case071 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case071 $case170 $case071 $case071 $case269 $default68 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual67 end i32.const 1616 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual62 + br $__inlined_func$class-overloading/A#b@virtual67 end i32.const 1648 global.set $class-overloading/which - br $__inlined_func$class-overloading/B#b@virtual62 + br $__inlined_func$class-overloading/A#b@virtual67 end - i32.const 1520 + i32.const 1488 global.set $class-overloading/which end global.get $~lib/memory/__stack_pointer @@ -2358,16 +2332,18 @@ global.set $class-overloading/which global.get $~lib/memory/__stack_pointer global.get $class-overloading/a + local.tee $0 i32.store - i32.const 1648 - global.set $class-overloading/which + local.get $0 + call $class-overloading/A#a@virtual global.get $~lib/memory/__stack_pointer - i32.const 1648 + global.get $class-overloading/which + local.tee $0 i32.store global.get $~lib/memory/__stack_pointer i32.const 1648 i32.store offset=4 - i32.const 1648 + local.get $0 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2382,18 +2358,46 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - local.tee $0 global.get $class-overloading/a + local.tee $0 i32.store - i32.const 1648 - global.set $class-overloading/which - local.get $0 - i32.const 1648 + block $__inlined_func$class-overloading/A#b@virtual77 + block $default78 + block $case279 + block $case180 + block $case081 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case081 $case180 $case081 $case081 $case279 $default78 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual77 + end + i32.const 1616 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual77 + end + i32.const 1648 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual77 + end + i32.const 1488 + global.set $class-overloading/which + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + global.get $class-overloading/which + local.tee $1 i32.store local.get $0 i32.const 1648 i32.store offset=4 - i32.const 1648 + local.get $1 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2408,18 +2412,46 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - local.tee $0 global.get $class-overloading/a + local.tee $0 i32.store - i32.const 1648 - global.set $class-overloading/which - local.get $0 - i32.const 1648 + block $__inlined_func$class-overloading/A#get:c@virtual86 + block $default87 + block $case288 + block $case189 + block $case090 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case090 $case189 $case090 $case090 $case288 $default87 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#get:c@virtual86 + end + i32.const 1616 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#get:c@virtual86 + end + i32.const 1648 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#get:c@virtual86 + end + i32.const 1488 + global.set $class-overloading/which + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + global.get $class-overloading/which + local.tee $1 i32.store local.get $0 i32.const 1648 i32.store offset=4 - i32.const 1648 + local.get $1 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2434,18 +2466,46 @@ i32.const 1056 global.set $class-overloading/which global.get $~lib/memory/__stack_pointer - local.tee $0 global.get $class-overloading/a + local.tee $0 i32.store - i32.const 1648 - global.set $class-overloading/which - local.get $0 - i32.const 1648 + block $__inlined_func$class-overloading/A#b@virtual95 + block $default96 + block $case297 + block $case198 + block $case099 + local.get $0 + i32.const 8 + i32.sub + i32.load + i32.const 4 + i32.sub + br_table $case099 $case198 $case099 $case099 $case297 $default96 + end + i32.const 1520 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual95 + end + i32.const 1616 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual95 + end + i32.const 1648 + global.set $class-overloading/which + br $__inlined_func$class-overloading/A#b@virtual95 + end + i32.const 1488 + global.set $class-overloading/which + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + global.get $class-overloading/which + local.tee $1 i32.store local.get $0 i32.const 1648 i32.store offset=4 - i32.const 1648 + local.get $1 i32.const 1648 call $~lib/string/String.__eq i32.eqz @@ -2487,8 +2547,8 @@ local.tee $0 i32.store block $__inlined_func$class-overloading/IA#foo@virtual - block $default73 - block $case174 + block $default105 + block $case1106 local.get $0 i32.const 8 i32.sub @@ -2500,8 +2560,8 @@ local.get $0 i32.const 12 i32.eq - br_if $case174 - br $default73 + br_if $case1106 + br $default105 end i32.const 1680 global.set $class-overloading/which @@ -2562,9 +2622,9 @@ global.get $class-overloading/ic local.tee $0 i32.store - block $__inlined_func$class-overloading/IA#foo@virtual77 - block $default78 - block $case179 + block $__inlined_func$class-overloading/IA#foo@virtual109 + block $default110 + block $case1111 local.get $0 i32.const 8 i32.sub @@ -2576,16 +2636,16 @@ local.get $0 i32.const 12 i32.eq - br_if $case179 - br $default78 + br_if $case1111 + br $default110 end i32.const 1680 global.set $class-overloading/which - br $__inlined_func$class-overloading/IA#foo@virtual77 + br $__inlined_func$class-overloading/IA#foo@virtual109 end i32.const 1712 global.set $class-overloading/which - br $__inlined_func$class-overloading/IA#foo@virtual77 + br $__inlined_func$class-overloading/IA#foo@virtual109 end unreachable end diff --git a/tests/compiler/for.debug.wat b/tests/compiler/for.debug.wat index e7fcf8c3f2..da03fcda17 100644 --- a/tests/compiler/for.debug.wat +++ b/tests/compiler/for.debug.wat @@ -2544,7 +2544,6 @@ (local $0 i32) (local $1 i32) (local $2 i32) - (local $3 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub @@ -2560,10 +2559,10 @@ call $for/Ref#constructor local.tee $1 i32.store - loop $for-loop|1 + loop $for-loop|0 local.get $1 - local.set $3 - local.get $3 + local.set $2 + local.get $2 if local.get $0 i32.const 1 @@ -2581,7 +2580,7 @@ local.tee $1 i32.store end - br $for-loop|1 + br $for-loop|0 end end local.get $0 diff --git a/tests/compiler/for.release.wat b/tests/compiler/for.release.wat index 3a21356e5c..cb9acc257b 100644 --- a/tests/compiler/for.release.wat +++ b/tests/compiler/for.release.wat @@ -1283,7 +1283,7 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|17 + loop $for-loop|08 local.get $0 if local.get $1 @@ -1301,7 +1301,7 @@ local.tee $0 i32.store end - br $for-loop|17 + br $for-loop|08 end end local.get $1 @@ -1349,10 +1349,10 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|011 + loop $for-loop|012 call $for/Ref#constructor if - block $for-break010 + block $for-break011 local.get $1 i32.const 1 i32.add @@ -1362,13 +1362,13 @@ if i32.const 0 local.set $0 - br $for-break010 + br $for-break011 end global.get $~lib/memory/__stack_pointer call $for/Ref#constructor local.tee $0 i32.store - br $for-loop|011 + br $for-loop|012 end end end diff --git a/tests/compiler/std/math.release.wat b/tests/compiler/std/math.release.wat index 1808c052ff..2976d7d875 100644 --- a/tests/compiler/std/math.release.wat +++ b/tests/compiler/std/math.release.wat @@ -49905,7 +49905,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -49949,7 +49949,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -49971,7 +49971,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50037,7 +50037,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50103,7 +50103,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50169,7 +50169,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50257,7 +50257,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50367,7 +50367,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -50521,7 +50521,7 @@ call $~lib/builtins/abort unreachable end - f64.const nan:0x8000000000000 + f64.const -nan:0x8000000000000 f64.const nan:0x8000000000000 f64.const 0 call $std/math/check @@ -51753,7 +51753,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51779,7 +51779,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51792,7 +51792,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51831,7 +51831,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51870,7 +51870,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51909,7 +51909,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -51961,7 +51961,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -52026,7 +52026,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check @@ -52052,7 +52052,7 @@ call $~lib/builtins/abort unreachable end - f32.const nan:0x400000 + f32.const -nan:0x400000 f32.const nan:0x400000 f32.const 0 call $std/math/check diff --git a/tests/compiler/while.debug.wat b/tests/compiler/while.debug.wat index 73018885bf..0123064c08 100644 --- a/tests/compiler/while.debug.wat +++ b/tests/compiler/while.debug.wat @@ -2588,7 +2588,7 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|1 + loop $while-continue|0 local.get $1 local.set $2 local.get $2 @@ -2609,7 +2609,7 @@ local.tee $1 i32.store end - br $while-continue|1 + br $while-continue|0 end end local.get $0 @@ -2665,8 +2665,8 @@ call $while/Ref#constructor local.tee $1 i32.store - block $while-break|1 - loop $while-continue|1 + block $while-break|0 + loop $while-continue|0 call $while/getRef local.set $2 local.get $2 @@ -2680,9 +2680,9 @@ if i32.const 0 local.set $1 - br $while-break|1 + br $while-break|0 end - br $while-continue|1 + br $while-continue|0 end end end diff --git a/tests/compiler/while.release.wat b/tests/compiler/while.release.wat index 55ed996a0d..1488e913b9 100644 --- a/tests/compiler/while.release.wat +++ b/tests/compiler/while.release.wat @@ -1317,7 +1317,7 @@ call $while/Ref#constructor local.tee $3 i32.store - loop $while-continue|17 + loop $while-continue|08 local.get $3 if local.get $1 @@ -1335,7 +1335,7 @@ local.tee $3 i32.store end - br $while-continue|17 + br $while-continue|08 end end local.get $1 @@ -1380,10 +1380,10 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|110 + loop $while-continue|012 call $while/Ref#constructor if - block $while-break|19 + block $while-break|011 local.get $3 i32.const 1 i32.add @@ -1393,9 +1393,9 @@ if i32.const 0 local.set $1 - br $while-break|19 + br $while-break|011 end - br $while-continue|110 + br $while-continue|012 end end end From f0763d0b524a9af1d218ab2b32b035d01ce245fc Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 15:53:44 +0800 Subject: [PATCH 37/55] fix: type narrowing only effect for Local --- src/compiler.ts | 12 +- src/flow.ts | 5 + tests/compiler/typenarrow.debug.wat | 479 ++------ tests/compiler/typenarrow.release.wat | 1455 +++++++++---------------- tests/compiler/typenarrow.ts | 81 +- 5 files changed, 659 insertions(+), 1373 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 11e4ebff4f..d1fc7b65d3 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -88,7 +88,6 @@ import { IndexSignature, File, mangleInternalName, - TypedElement } from "./program"; import { @@ -6228,13 +6227,6 @@ export class Compiler extends DiagnosticEmitter { valueExpr ); } - if (type.isReference) { - let narrowedType = valueType; - if (!valueType.isNullableReference || flow.isNonnull(valueExpr, type)) { - narrowedType = valueType.nonNullableType; - } - flow.setAssignType(expressionRef, global, narrowedType); - } return expressionRef; } @@ -7938,8 +7930,8 @@ export class Compiler extends DiagnosticEmitter { let instanceExpression = this.makeInstanceofType(expression, expectedType); if (expression.expression.kind == NodeKind.IDENTIFIER) { let element = flow.lookup((expression.expression).text); - if (element instanceof TypedElement) { - flow.setConditionNarrowedType(instanceExpression, element, expectedType); + if (element instanceof Local) { + flow.setConditionNarrowedType(instanceExpression, element, expectedType); } } return instanceExpression; diff --git a/src/flow.ts b/src/flow.ts index 61c1eae81f..d91390cc1c 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -619,12 +619,14 @@ export class Flow { /** set type assign */ setAssignType(expr: ExpressionRef, element: TypedElement, type: Type): void { + assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (type && !type.isReference) return; this.setNarrowedType(element, type); this.conditionalNarrowedType.setAssignType(expr, element, type); } /** set type narrow */ setNarrowedType(element: TypedElement, type: Type): void { + assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (!type.isReference) return; if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); @@ -634,6 +636,7 @@ export class Flow { } /** get type narrow, return null if not exist */ getNarrowedType(element: TypedElement): Type | null { + if (element.kind != ElementKind.LOCAL) return null; if (this.narrowedTypes == null) { this.narrowedTypes = new NarrowedTypeMap(); } @@ -642,6 +645,7 @@ export class Flow { } /** do not trace `element` type narrow */ removeNarrowedType(element: TypedElement): void { + assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); let thisNarrowedTypes = this.narrowedTypes; if (thisNarrowedTypes) thisNarrowedTypes.delete(element); this.conditionalNarrowedType.removeElement(element); @@ -649,6 +653,7 @@ export class Flow { /** set conditional type narrow */ setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { + assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (type && !type.isReference) return; this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); } diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index e0dad05922..5130186d1f 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -23,8 +23,6 @@ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) - (global $typenarrow/value (mut i32) (i32.const 0)) - (global $typenarrow/condi (mut i32) (i32.const 1)) (global $~lib/rt/__rtti_base i32 (i32.const 448)) (global $~lib/memory/__data_end i32 (i32.const 508)) (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16892)) @@ -2192,6 +2190,8 @@ if local.get $0 call $typenarrow/B#b1 + local.get $0 + local.set $2 end local.get $0 local.tee $2 @@ -2209,6 +2209,8 @@ else local.get $0 call $typenarrow/B#b1 + local.get $0 + local.set $2 end local.get $0 local.tee $2 @@ -2228,6 +2230,8 @@ if local.get $0 call $typenarrow/B#b1 + local.get $0 + local.set $2 end local.get $1 if (result i32) @@ -2247,6 +2251,8 @@ if local.get $0 call $typenarrow/B#b1 + local.get $0 + local.set $2 end local.get $1 if (result i32) @@ -2269,6 +2275,8 @@ else i32.const 432 drop + local.get $0 + local.set $2 end local.get $0 local.tee $2 @@ -2291,6 +2299,8 @@ else i32.const 432 drop + local.get $0 + local.set $2 end local.get $0 local.tee $2 @@ -2331,6 +2341,87 @@ if nop end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 6 + call $~lib/rt/__instanceof + end + if (result i32) + i32.const 1 + else + i32.const 0 + end + if + local.get $0 + i32.load + drop + local.get $0 + call $typenarrow/B#b1 + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + i32.const 1 + else + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 6 + call $~lib/rt/__instanceof + end + end + if + local.get $0 + call $typenarrow/B#b1 + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 6 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + i32.eqz + if + nop + else + local.get $0 + call $typenarrow/B#b1 + end global.get $~lib/memory/__stack_pointer i32.const 4 i32.add @@ -2338,13 +2429,6 @@ ) (func $~lib/rt/__visit_globals (param $0 i32) (local $1 i32) - global.get $typenarrow/value - local.tee $1 - if - local.get $1 - local.get $0 - call $~lib/rt/itcms/__visit - end i32.const 224 local.get $0 call $~lib/rt/itcms/__visit @@ -2412,32 +2496,6 @@ unreachable ) (func $~start - call $start:typenarrow - ) - (func $~stack_check - global.get $~lib/memory/__stack_pointer - global.get $~lib/memory/__data_end - i32.lt_s - if - i32.const 16912 - i32.const 16960 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable - end - ) - (func $start:typenarrow - (local $0 i32) - (local $1 i32) - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.sub - global.set $~lib/memory/__stack_pointer - call $~stack_check - global.get $~lib/memory/__stack_pointer - i64.const 0 - i64.store memory.size i32.const 16 i32.shl @@ -2455,354 +2513,19 @@ i32.const 320 call $~lib/rt/itcms/initLazy global.set $~lib/rt/itcms/fromSpace - i32.const 0 - call $typenarrow/A#constructor - global.set $typenarrow/value - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - if - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - i32.eqz - if - nop - else - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - if (result i32) - global.get $typenarrow/condi - else - i32.const 0 - end - if - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $typenarrow/condi - if (result i32) - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - else - i32.const 0 - end - if - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $typenarrow/condi - if (result i32) - i32.const 1 - else - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - i32.eqz - end - if - nop - else - i32.const 432 - drop - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - i32.eqz - if (result i32) - i32.const 1 - else - global.get $typenarrow/condi - end - if - nop - else - i32.const 432 - drop - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - if (result i32) - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#check - else - i32.const 0 - end - if - nop - end + ) + (func $~stack_check global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - i32.eqz - if (result i32) - i32.const 1 - else - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#check - end + global.get $~lib/memory/__data_end + i32.lt_s if - nop - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 6 - call $~lib/rt/__instanceof - end - if (result i32) + i32.const 16912 + i32.const 16960 i32.const 1 - else - i32.const 0 - end - if - global.get $typenarrow/value - i32.load - drop - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - if (result i32) i32.const 1 - else - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 6 - call $~lib/rt/__instanceof - end - end - if - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 4 - call $~lib/rt/__instanceof - end - if (result i32) - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - i32.eqz - if (result i32) - i32.const 0 - else - local.get $0 - i32.const 6 - call $~lib/rt/__instanceof - end - else - i32.const 0 - end - i32.eqz - if - nop - else - global.get $typenarrow/value - local.set $1 - global.get $~lib/memory/__stack_pointer - local.get $1 - i32.store offset=4 - local.get $1 - call $typenarrow/B#b1 + call $~lib/builtins/abort + unreachable end - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.add - global.set $~lib/memory/__stack_pointer ) (func $typenarrow/A#constructor (param $0 i32) (result i32) (local $1 i32) diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index 0df86decda..d1a36d1c83 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -1,10 +1,10 @@ (module (type $none_=>_none (func)) (type $i32_i32_=>_none (func (param i32 i32))) - (type $none_=>_i32 (func (result i32))) (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $none_=>_i32 (func (result i32))) (type $i32_=>_i32 (func (param i32) (result i32))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) @@ -17,7 +17,6 @@ (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $typenarrow/value (mut i32) (i32.const 0)) (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17916)) (memory $0 1) (data (i32.const 1036) "<") @@ -41,12 +40,6 @@ (func $~lib/rt/itcms/visitRoots (local $0 i32) (local $1 i32) - global.get $typenarrow/value - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end i32.const 1248 call $byn-split-outlined-A$~lib/rt/itcms/__visit i32.const 1056 @@ -1004,341 +997,274 @@ (func $typenarrow/testlocal (local $0 i32) (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1532 - i32.lt_s - if - i32.const 17936 - i32.const 17984 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable - end - global.get $~lib/memory/__stack_pointer - local.tee $0 - i32.const 0 - i32.store - local.get $0 - call $typenarrow/A#constructor - local.tee $1 - i32.store - local.get $1 - if - local.get $1 - i32.const 20 + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1532 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $3 + i32.const 0 + i32.store + local.get $3 + i32.const 4 i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1532 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $2 + i32.const 0 + i32.store + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.ge_u if - loop $do-loop|0 - local.get $0 - i32.const 4 - i32.ne - if + block $__inlined_func$~lib/rt/itcms/interrupt + i32.const 2048 + local.set $0 + loop $do-loop|0 local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 + call $~lib/rt/itcms/step + i32.sub + local.set $0 + global.get $~lib/rt/itcms/state + i32.eqz + if + global.get $~lib/rt/itcms/total + i64.extend_i32_u + i64.const 200 + i64.mul + i64.const 100 + i64.div_u + i32.wrap_i64 + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + br $__inlined_func$~lib/rt/itcms/interrupt + end + local.get $0 + i32.const 0 + i32.gt_s br_if $do-loop|0 end - end - end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|02 + global.get $~lib/rt/itcms/total + local.tee $0 + global.get $~lib/rt/itcms/threshold + i32.sub + i32.const 1024 + i32.lt_u + i32.const 10 + i32.shl local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|02 - end + i32.add + global.set $~lib/rt/itcms/threshold end end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u + global.get $~lib/rt/tlsf/ROOT + i32.eqz if - loop $do-loop|06 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|06 - end - end + call $~lib/rt/tlsf/initialize end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 + global.get $~lib/rt/tlsf/ROOT + local.tee $4 + call $~lib/rt/tlsf/searchBlock local.tee $0 - i32.const 1472 - i32.load - i32.le_u + i32.eqz if - loop $do-loop|010 - local.get $0 - i32.const 4 - i32.ne + memory.size + local.tee $0 + i32.const 4 + local.get $4 + i32.load offset=1568 + local.get $0 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + i32.ne + i32.shl + i32.const 65563 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $0 + local.get $1 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $1 + memory.grow + i32.const 0 + i32.lt_s if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|010 + unreachable end end - end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|017 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|017 - end + local.get $4 + local.get $0 + i32.const 16 + i32.shl + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + local.get $4 + call $~lib/rt/tlsf/searchBlock + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 1392 + i32.const 496 + i32.const 16 + call $~lib/builtins/abort + unreachable end end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 + local.get $0 i32.load - i32.le_u + i32.const -4 + i32.and + i32.const 28 + i32.lt_u if - loop $do-loop|020 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|020 - end - end + i32.const 0 + i32.const 1392 + i32.const 498 + i32.const 14 + call $~lib/builtins/abort + unreachable end - end - local.get $1 - if - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 + local.get $4 + local.get $0 + call $~lib/rt/tlsf/removeBlock + local.get $0 i32.load - i32.le_u + local.tee $5 + i32.const -4 + i32.and + i32.const 28 + i32.sub + local.tee $1 + i32.const 16 + i32.ge_u if - loop $do-loop|023 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|023 - end - end - end - end - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - ) - (func $~lib/rt/__visit_members (param $0 i32) - block $invalid - block $typenarrow/C - block $~lib/function/Function<%28this:typenarrow/B%29=>void> - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid - end - return - end - return - end - local.get $0 - i32.load - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - return - end - return - end - return - end local.get $0 - i32.load offset=4 - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - return - end - return - end - unreachable - ) - (func $~start - call $start:typenarrow - ) - (func $start:typenarrow - (local $0 i32) - (local $1 i32) - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.sub - global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1532 - i32.lt_s - if - i32.const 17936 - i32.const 17984 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable - end - global.get $~lib/memory/__stack_pointer - i64.const 0 - i64.store - memory.size - i32.const 16 - i32.shl - i32.const 17916 - i32.sub - i32.const 1 - i32.shr_u - global.set $~lib/rt/itcms/threshold - i32.const 1172 - i32.const 1168 - i32.store - i32.const 1176 - i32.const 1168 - i32.store - i32.const 1168 - global.set $~lib/rt/itcms/pinSpace - i32.const 1204 - i32.const 1200 - i32.store - i32.const 1208 - i32.const 1200 - i32.store - i32.const 1200 - global.set $~lib/rt/itcms/toSpace - i32.const 1348 - i32.const 1344 - i32.store - i32.const 1352 - i32.const 1344 - i32.store - i32.const 1344 - global.set $~lib/rt/itcms/fromSpace - call $typenarrow/A#constructor - global.set $typenarrow/value - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof (result i32) + local.get $5 + i32.const 2 + i32.and + i32.const 28 + i32.or + i32.store + local.get $0 + i32.const 32 + i32.add + local.tee $5 + local.get $1 + i32.const 4 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $4 + local.get $5 + call $~lib/rt/tlsf/insertBlock + else + local.get $0 + local.get $5 + i32.const -2 + i32.and + i32.store + local.get $0 + i32.const 4 + i32.add local.get $0 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $1 + local.get $1 + i32.load + i32.const -3 + i32.and + i32.store + end + local.get $0 + i32.const 3 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + global.get $~lib/rt/itcms/fromSpace + local.tee $1 + i32.load offset=8 + local.set $4 + local.get $0 + global.get $~lib/rt/itcms/white + local.get $1 + i32.or + i32.store offset=4 + local.get $0 + local.get $4 + i32.store offset=8 + local.get $4 + local.get $0 + local.get $4 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + global.get $~lib/rt/itcms/total + local.get $0 + i32.load + i32.const -4 + i32.and + i32.const 4 + i32.add + i32.add + global.set $~lib/rt/itcms/total + local.get $0 + i32.const 20 + i32.add + local.tee $1 + i32.const 0 + i32.const 0 + memory.fill + local.get $2 + local.get $1 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + local.get $1 + i32.store + local.get $1 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1347,46 +1273,26 @@ i32.load i32.le_u if - loop $do-loop|0 - i32.const 1 + loop $do-loop|02 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|0 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|02 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $typenarrow/value - local.tee $1 - i32.store offset=4 - local.get $0 local.get $1 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof0 (result i32) - local.get $0 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1395,46 +1301,26 @@ i32.load i32.le_u if - loop $do-loop|02 - i32.const 1 + loop $do-loop|024 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof0 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|02 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|024 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $typenarrow/value - local.tee $1 - i32.store offset=4 - local.get $0 local.get $1 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof4 (result i32) - local.get $0 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1444,45 +1330,25 @@ i32.le_u if loop $do-loop|06 - i32.const 1 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof4 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|06 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|06 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $typenarrow/value - local.tee $1 - i32.store offset=4 - local.get $0 local.get $1 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof8 (result i32) - local.get $0 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1492,77 +1358,25 @@ i32.le_u if loop $do-loop|010 - i32.const 1 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof8 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|010 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|010 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - local.tee $0 - global.get $typenarrow/value - local.tee $1 - i32.store offset=4 - local.get $0 local.get $1 - i32.store - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u if - loop $do-loop|017 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|017 - end - end - end - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof18 (result i32) - local.get $0 + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1571,41 +1385,26 @@ i32.load i32.le_u if - loop $do-loop|020 - i32.const 1 + loop $do-loop|017 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof18 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|020 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|017 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof21 (result i32) - local.get $0 + local.get $1 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1614,88 +1413,26 @@ i32.load i32.le_u if - loop $do-loop|023 - i32.const 1 + loop $do-loop|020 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof21 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|023 - end - end - i32.const 0 - end - else - i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof25 (result i32) - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|027 - i32.const 1 - local.get $0 - i32.const 6 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof25 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|027 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|020 + end end end - i32.const 0 end - else - i32.const 0 - end - if - global.get $typenarrow/value - local.tee $0 - i32.load - drop - global.get $~lib/memory/__stack_pointer - local.get $0 - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof29 (result i32) - local.get $0 + local.get $1 + if + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1704,39 +1441,27 @@ i32.load i32.le_u if - loop $do-loop|031 - i32.const 1 + loop $do-loop|023 local.get $0 i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof29 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|031 + i32.ne + if + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|023 + end end end - i32.const 0 end - else - i32.const 0 - end - if (result i32) - i32.const 1 - else - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 + local.get $1 if (result i32) - block $__inlined_func$~lib/rt/__instanceof32 (result i32) - local.get $0 + block $__inlined_func$~lib/rt/__instanceof25 (result i32) + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1745,12 +1470,12 @@ i32.load i32.le_u if - loop $do-loop|034 + loop $do-loop|027 i32.const 1 local.get $0 i32.const 6 i32.eq - br_if $__inlined_func$~lib/rt/__instanceof32 + br_if $__inlined_func$~lib/rt/__instanceof25 drop local.get $0 i32.const 3 @@ -1759,7 +1484,7 @@ i32.add i32.load offset=4 local.tee $0 - br_if $do-loop|034 + br_if $do-loop|027 end end i32.const 0 @@ -1767,59 +1492,52 @@ else i32.const 0 end - end - if - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof36 (result i32) - local.get $0 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 + if + local.get $1 i32.load - i32.le_u - if - loop $do-loop|038 - i32.const 1 - local.get $0 - i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof36 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|038 + drop + end + local.get $1 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof29 (result i32) + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|031 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof29 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|031 + end end + i32.const 0 end + else i32.const 0 end - else - i32.const 0 - end - if (result i32) - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - local.tee $0 - i32.store - local.get $0 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof39 (result i32) - local.get $0 + i32.const 1 + local.get $1 + select + i32.eqz + if + block $__inlined_func$~lib/rt/__instanceof32 (result i32) + local.get $1 i32.const 20 i32.sub i32.load offset=12 @@ -1828,12 +1546,12 @@ i32.load i32.le_u if - loop $do-loop|041 + loop $do-loop|034 i32.const 1 local.get $0 i32.const 6 i32.eq - br_if $__inlined_func$~lib/rt/__instanceof39 + br_if $__inlined_func$~lib/rt/__instanceof32 drop local.get $0 i32.const 3 @@ -1842,7 +1560,40 @@ i32.add i32.load offset=4 local.tee $0 - br_if $do-loop|041 + br_if $do-loop|034 + end + end + i32.const 0 + end + drop + end + local.get $1 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof36 (result i32) + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u + if + loop $do-loop|038 + i32.const 1 + local.get $0 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof36 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|038 end end i32.const 0 @@ -1850,278 +1601,132 @@ else i32.const 0 end - else i32.const 0 - end - if - global.get $~lib/memory/__stack_pointer - global.get $typenarrow/value - i32.store offset=4 - end - global.get $~lib/memory/__stack_pointer - i32.const 8 - i32.add - global.set $~lib/memory/__stack_pointer - ) - (func $typenarrow/A#constructor (result i32) - (local $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1532 - i32.lt_s - if - i32.const 17936 - i32.const 17984 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable - end - global.get $~lib/memory/__stack_pointer - local.tee $2 - i32.const 0 - i32.store - global.get $~lib/rt/itcms/total - global.get $~lib/rt/itcms/threshold - i32.ge_u - if - block $__inlined_func$~lib/rt/itcms/interrupt - i32.const 2048 - local.set $0 - loop $do-loop|0 - local.get $0 - call $~lib/rt/itcms/step + local.get $1 + select + if + block $__inlined_func$~lib/rt/__instanceof39 (result i32) + local.get $1 + i32.const 20 i32.sub - local.set $0 - global.get $~lib/rt/itcms/state - i32.eqz + i32.load offset=12 + local.tee $0 + i32.const 1472 + i32.load + i32.le_u if - global.get $~lib/rt/itcms/total - i64.extend_i32_u - i64.const 200 - i64.mul - i64.const 100 - i64.div_u - i32.wrap_i64 - i32.const 1024 - i32.add - global.set $~lib/rt/itcms/threshold - br $__inlined_func$~lib/rt/itcms/interrupt + loop $do-loop|041 + i32.const 1 + local.get $0 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof39 + drop + local.get $0 + i32.const 3 + i32.shl + i32.const 1476 + i32.add + i32.load offset=4 + local.tee $0 + br_if $do-loop|041 + end end - local.get $0 i32.const 0 - i32.gt_s - br_if $do-loop|0 end - global.get $~lib/rt/itcms/total - local.tee $0 - global.get $~lib/rt/itcms/threshold - i32.sub - i32.const 1024 - i32.lt_u - i32.const 10 - i32.shl - local.get $0 - i32.add - global.set $~lib/rt/itcms/threshold + drop end - end - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.tee $3 - call $~lib/rt/tlsf/searchBlock - local.tee $0 - i32.eqz - if - memory.size - local.tee $0 - i32.const 4 - local.get $3 - i32.load offset=1568 - local.get $0 - i32.const 16 - i32.shl + global.get $~lib/memory/__stack_pointer i32.const 4 - i32.sub - i32.ne - i32.shl - i32.const 65563 i32.add - i32.const -65536 - i32.and - i32.const 16 - i32.shr_u - local.tee $1 - local.get $0 - local.get $1 - i32.gt_s - select - memory.grow - i32.const 0 - i32.lt_s - if - local.get $1 - memory.grow - i32.const 0 - i32.lt_s + global.set $~lib/memory/__stack_pointer + return + end + i32.const 17936 + i32.const 17984 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) + (func $~lib/rt/__visit_members (param $0 i32) + block $invalid + block $typenarrow/C + block $~lib/function/Function<%28this:typenarrow/B%29=>void> + block $typenarrow/B + block $typenarrow/A + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid + end + return + end + return + end + local.get $0 + i32.load + local.tee $0 + if + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit + end + return + end + return + end + return + end + local.get $0 + i32.load offset=4 + local.tee $0 if - unreachable + local.get $0 + call $byn-split-outlined-A$~lib/rt/itcms/__visit end + return end - local.get $3 - local.get $0 - i32.const 16 - i32.shl - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - local.get $3 - call $~lib/rt/tlsf/searchBlock - local.tee $0 - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 496 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - end - local.get $0 - i32.load - i32.const -4 - i32.and - i32.const 28 - i32.lt_u - if - i32.const 0 - i32.const 1392 - i32.const 498 - i32.const 14 - call $~lib/builtins/abort - unreachable + return end - local.get $3 - local.get $0 - call $~lib/rt/tlsf/removeBlock - local.get $0 - i32.load - local.tee $4 - i32.const -4 - i32.and - i32.const 28 - i32.sub - local.tee $1 + unreachable + ) + (func $~start + memory.size i32.const 16 - i32.ge_u - if - local.get $0 - local.get $4 - i32.const 2 - i32.and - i32.const 28 - i32.or - i32.store - local.get $0 - i32.const 32 - i32.add - local.tee $4 - local.get $1 - i32.const 4 - i32.sub - i32.const 1 - i32.or - i32.store - local.get $3 - local.get $4 - call $~lib/rt/tlsf/insertBlock - else - local.get $0 - local.get $4 - i32.const -2 - i32.and - i32.store - local.get $0 - i32.const 4 - i32.add - local.get $0 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $1 - local.get $1 - i32.load - i32.const -3 - i32.and - i32.store - end - local.get $0 - i32.const 3 - i32.store offset=12 - local.get $0 - i32.const 0 - i32.store offset=16 - global.get $~lib/rt/itcms/fromSpace - local.tee $1 - i32.load offset=8 - local.set $3 - local.get $0 - global.get $~lib/rt/itcms/white - local.get $1 - i32.or - i32.store offset=4 - local.get $0 - local.get $3 - i32.store offset=8 - local.get $3 - local.get $0 - local.get $3 - i32.load offset=4 - i32.const 3 - i32.and - i32.or - i32.store offset=4 - local.get $1 - local.get $0 - i32.store offset=8 - global.get $~lib/rt/itcms/total - local.get $0 - i32.load - i32.const -4 - i32.and - i32.const 4 - i32.add - i32.add - global.set $~lib/rt/itcms/total - local.get $0 - i32.const 20 - i32.add - local.tee $0 - i32.const 0 - i32.const 0 - memory.fill - local.get $2 - local.get $0 + i32.shl + i32.const 17916 + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 1172 + i32.const 1168 i32.store - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - local.get $0 + i32.const 1176 + i32.const 1168 + i32.store + i32.const 1168 + global.set $~lib/rt/itcms/pinSpace + i32.const 1204 + i32.const 1200 + i32.store + i32.const 1208 + i32.const 1200 + i32.store + i32.const 1200 + global.set $~lib/rt/itcms/toSpace + i32.const 1348 + i32.const 1344 + i32.store + i32.const 1352 + i32.const 1344 + i32.store + i32.const 1344 + global.set $~lib/rt/itcms/fromSpace ) (func $byn-split-outlined-A$~lib/rt/itcms/__visit (param $0 i32) (local $1 i32) diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 257cac0bb9..8be47f4567 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -10,66 +10,6 @@ class C extends B { c1: i32; } -let value = new A(); -let condi = true; - -// noraml -if (value instanceof B) { - value.b1(); - let t: B = value; -} - -// not -if (!(value instanceof B)) { -} else { - value.b1(); - let t: B = value; -} - -// and -if (value instanceof B && condi) { - value.b1(); - let t: B = value; -} -if (condi && value instanceof B) { - value.b1(); - let t: B = value; -} - -// or -if (condi || !(value instanceof B)) { -} else { - value.b1; - let t: B = value; -} - -if (!(value instanceof B) || condi) { -} else { - value.b1; - let t: B = value; -} - -// in condition check for logic operator -if (value instanceof B && value.check()) { -} -if (!(value instanceof B) || value.check()) { -} - -// compatibiltiy -if (value instanceof C && value instanceof B) { - value.c1; - value.b1(); -} - -if (value instanceof B || value instanceof C) { - value.b1(); -} - -if (!(value instanceof B && value instanceof C)) { -} else { - value.b1(); -} - export function testlocal(): void { let value = new A(); let condi = true; @@ -77,31 +17,37 @@ export function testlocal(): void { // noraml if (value instanceof B) { value.b1(); + let t: B = value; } // not if (!(value instanceof B)) { } else { value.b1(); + let t: B = value; } // and if (value instanceof B && condi) { value.b1(); + let t: B = value; } if (condi && value instanceof B) { value.b1(); + let t: B = value; } // or if (condi || !(value instanceof B)) { } else { value.b1; + let t: B = value; } if (!(value instanceof B) || condi) { } else { value.b1; + let t: B = value; } // in condition check for logic operator @@ -109,4 +55,19 @@ export function testlocal(): void { } if (!(value instanceof B) || value.check()) { } + + // compatibiltiy + if (value instanceof C && value instanceof B) { + value.c1; + value.b1(); + } + + if (value instanceof B || value instanceof C) { + value.b1(); + } + + if (!(value instanceof B && value instanceof C)) { + } else { + value.b1(); + } } From 71f26aa5b1ae9d6eab5c040274f15b5fb8b491b6 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 15:55:01 +0800 Subject: [PATCH 38/55] update other testcase --- tests/compiler/for.debug.wat | 9 +++++---- tests/compiler/for.release.wat | 12 ++++++------ tests/compiler/while.debug.wat | 12 ++++++------ tests/compiler/while.release.wat | 12 ++++++------ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/compiler/for.debug.wat b/tests/compiler/for.debug.wat index da03fcda17..e7fcf8c3f2 100644 --- a/tests/compiler/for.debug.wat +++ b/tests/compiler/for.debug.wat @@ -2544,6 +2544,7 @@ (local $0 i32) (local $1 i32) (local $2 i32) + (local $3 i32) global.get $~lib/memory/__stack_pointer i32.const 4 i32.sub @@ -2559,10 +2560,10 @@ call $for/Ref#constructor local.tee $1 i32.store - loop $for-loop|0 + loop $for-loop|1 local.get $1 - local.set $2 - local.get $2 + local.set $3 + local.get $3 if local.get $0 i32.const 1 @@ -2580,7 +2581,7 @@ local.tee $1 i32.store end - br $for-loop|0 + br $for-loop|1 end end local.get $0 diff --git a/tests/compiler/for.release.wat b/tests/compiler/for.release.wat index cb9acc257b..3a21356e5c 100644 --- a/tests/compiler/for.release.wat +++ b/tests/compiler/for.release.wat @@ -1283,7 +1283,7 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|08 + loop $for-loop|17 local.get $0 if local.get $1 @@ -1301,7 +1301,7 @@ local.tee $0 i32.store end - br $for-loop|08 + br $for-loop|17 end end local.get $1 @@ -1349,10 +1349,10 @@ call $for/Ref#constructor local.tee $0 i32.store - loop $for-loop|012 + loop $for-loop|011 call $for/Ref#constructor if - block $for-break011 + block $for-break010 local.get $1 i32.const 1 i32.add @@ -1362,13 +1362,13 @@ if i32.const 0 local.set $0 - br $for-break011 + br $for-break010 end global.get $~lib/memory/__stack_pointer call $for/Ref#constructor local.tee $0 i32.store - br $for-loop|012 + br $for-loop|011 end end end diff --git a/tests/compiler/while.debug.wat b/tests/compiler/while.debug.wat index 0123064c08..73018885bf 100644 --- a/tests/compiler/while.debug.wat +++ b/tests/compiler/while.debug.wat @@ -2588,7 +2588,7 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|0 + loop $while-continue|1 local.get $1 local.set $2 local.get $2 @@ -2609,7 +2609,7 @@ local.tee $1 i32.store end - br $while-continue|0 + br $while-continue|1 end end local.get $0 @@ -2665,8 +2665,8 @@ call $while/Ref#constructor local.tee $1 i32.store - block $while-break|0 - loop $while-continue|0 + block $while-break|1 + loop $while-continue|1 call $while/getRef local.set $2 local.get $2 @@ -2680,9 +2680,9 @@ if i32.const 0 local.set $1 - br $while-break|0 + br $while-break|1 end - br $while-continue|0 + br $while-continue|1 end end end diff --git a/tests/compiler/while.release.wat b/tests/compiler/while.release.wat index 1488e913b9..55ed996a0d 100644 --- a/tests/compiler/while.release.wat +++ b/tests/compiler/while.release.wat @@ -1317,7 +1317,7 @@ call $while/Ref#constructor local.tee $3 i32.store - loop $while-continue|08 + loop $while-continue|17 local.get $3 if local.get $1 @@ -1335,7 +1335,7 @@ local.tee $3 i32.store end - br $while-continue|08 + br $while-continue|17 end end local.get $1 @@ -1380,10 +1380,10 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|012 + loop $while-continue|110 call $while/Ref#constructor if - block $while-break|011 + block $while-break|19 local.get $3 i32.const 1 i32.add @@ -1393,9 +1393,9 @@ if i32.const 0 local.set $1 - br $while-break|011 + br $while-break|19 end - br $while-continue|012 + br $while-continue|110 end end end From db96d70d04f8e6efa0bd23366a10708619475ca0 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 15:56:53 +0800 Subject: [PATCH 39/55] revert `return assignmentExpression;` --- src/compiler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index d1fc7b65d3..b4ee21f5e6 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5950,7 +5950,7 @@ export class Compiler extends DiagnosticEmitter { var valueExpr = this.compileExpression(valueExpression, targetType); var valueType = this.currentType; - let assignmentExpression = this.makeAssignment( + return this.makeAssignment( target, this.convertExpression(valueExpr, valueType, targetType, false, valueExpression), valueType, @@ -5959,7 +5959,6 @@ export class Compiler extends DiagnosticEmitter { elementExpression, contextualType != Type.void ); - return assignmentExpression; } /** Makes an assignment expression or block, assigning a value to a target. */ From 250696290aa4c7a4e1fe13449fa9bc71b5964806 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 16:08:32 +0800 Subject: [PATCH 40/55] replace `if(a == null) a = new expression` with `||` --- src/flow.ts | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index d91390cc1c..1f0c27b114 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -628,19 +628,15 @@ export class Flow { setNarrowedType(element: TypedElement, type: Type): void { assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (!type.isReference) return; - if (this.narrowedTypes == null) { - this.narrowedTypes = new NarrowedTypeMap(); - } - let narrowedTypes = assert(this.narrowedTypes); + let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; narrowedTypes.set(element, type); } /** get type narrow, return null if not exist */ getNarrowedType(element: TypedElement): Type | null { if (element.kind != ElementKind.LOCAL) return null; - if (this.narrowedTypes == null) { - this.narrowedTypes = new NarrowedTypeMap(); - } - let narrowedTypes = assert(this.narrowedTypes); + const narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; return narrowedTypes.get(element); } /** do not trace `element` type narrow */ @@ -662,10 +658,8 @@ export class Flow { inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi, this); if (condiNarrow.size != 0) { - if (this.narrowedTypes == null) { - this.narrowedTypes = new NarrowedTypeMap(); - } - let narrowedTypes = assert(this.narrowedTypes); + let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; narrowedTypes.mergeOr(condiNarrow); } } @@ -673,10 +667,8 @@ export class Flow { inheritNarrowedTypeIfFalse(condi: ExpressionRef): void { let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi, this); if (condiNarrow.size != 0) { - if (this.narrowedTypes == null) { - this.narrowedTypes = new NarrowedTypeMap(); - } - let narrowedTypes = assert(this.narrowedTypes); + let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; narrowedTypes.mergeOr(condiNarrow); } } From e24574349893bdbe1a83881eedf6c00b55a61dc1 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 16:40:27 +0800 Subject: [PATCH 41/55] fix: lint error --- src/compiler.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index b4ee21f5e6..5e57878f22 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -6207,26 +6207,23 @@ export class Compiler extends DiagnosticEmitter { /** Whether to tee the value. */ tee: bool ): ExpressionRef { - let expressionRef: ExpressionRef; var module = this.module; var type = global.type; - var flow = this.currentFlow; assert(type != Type.void); var typeRef = type.toRef(); valueExpr = this.ensureSmallIntegerWrap(valueExpr, type); // globals must be wrapped if (tee) { // (global = value), global this.currentType = type; - expressionRef = module.block(null, [ + return module.block(null, [ module.global_set(global.internalName, valueExpr), module.global_get(global.internalName, typeRef) ], typeRef); } else { // global = value this.currentType = Type.void; - expressionRef = module.global_set(global.internalName, + return module.global_set(global.internalName, valueExpr ); } - return expressionRef; } /** Makes an assignment to a field. */ From 5d401b8cb354f3eea33d51c329d58b8512d72947 Mon Sep 17 00:00:00 2001 From: "Congcong Cai (EE-CN-42)" Date: Thu, 4 Aug 2022 17:20:15 +0800 Subject: [PATCH 42/55] add more testcases for nullable --- src/narrow.ts | 8 +-- tests/compiler/nullable.json | 13 +++-- tests/compiler/nullable.ts | 62 +++++++++++++++++++----- tests/compiler/possibly-null.debug.wat | 32 +++++++++++- tests/compiler/possibly-null.release.wat | 1 + tests/compiler/possibly-null.ts | 9 +++- 6 files changed, 104 insertions(+), 21 deletions(-) diff --git a/src/narrow.ts b/src/narrow.ts index 20e2438d49..4763287099 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -318,7 +318,7 @@ export class TypeNarrowChecker { typeMap.mergeElementOr(narrowedTypeElement); } const assignMap = this.assignMap; - if (assignMap.has(expr)) { + if (assignMap.has(expr)) { const assignTypeElement = assert(assignMap.get(expr)); typeMap.set(assignTypeElement.element, assignTypeElement.type); } @@ -326,8 +326,10 @@ export class TypeNarrowChecker { // nullable check switch (getExpressionId(expr)) { case ExpressionId.LocalSet: { - const local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - typeMap.setNonnull(local); + if (isLocalTee(expr)) { + const local = flow.parentFunction.localsByIndex[getLocalSetIndex(expr)]; + typeMap.setNonnull(local); + } break; } case ExpressionId.LocalGet: { diff --git a/tests/compiler/nullable.json b/tests/compiler/nullable.json index 4aaa25669e..d97e588a95 100644 --- a/tests/compiler/nullable.json +++ b/tests/compiler/nullable.json @@ -2,8 +2,15 @@ "asc_flags": [ ], "stderr": [ - "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", - "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", - "EOF" + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'." ] } diff --git a/tests/compiler/nullable.ts b/tests/compiler/nullable.ts index 9c2eda2744..75a4c90585 100644 --- a/tests/compiler/nullable.ts +++ b/tests/compiler/nullable.ts @@ -1,19 +1,57 @@ -class Example {} +class Ref {} -function notNullable(a: Example): void {} +declare function getBool(): bool; +function notNullable(a: Ref): void {} +// "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", notNullable(null); -function test(): void { - let value: Example | null = new Example(); - if (value != null) { - // value = null; - true && (value = null); - // "TS2322: Type 'nullable/Example | null' is not assignable to type 'nullable/Example'.", - notNullable(value); +export function testAssign(v: Ref | null): void { + if (v != null) { + v = null; + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } +} +export function testAssignLogicAnd(v: Ref | null): void { + if (v != null) { + getBool() && (v = null); + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } +} +export function testAssignLogicOr(v: Ref | null): void { + if (v != null) { + getBool() || (v = null); + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); } } -test(); - -ERROR("EOF"); +export function testAssignInCondiLogicAnd(v: Ref | null): void { + if (getBool() && !(v = null)) { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } else { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } + if (getBool() && (v = null)) { + } else { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } +} +export function testAssignInCondiLogicOr(v: Ref | null): void { + if (getBool() || (v = null)) { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } else { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } + if (getBool() || !(v = null)) { + // "TS2322: Type 'nullable/Ref | null' is not assignable to type 'nullable/Ref'.", + notNullable(v); + } +} diff --git a/tests/compiler/possibly-null.debug.wat b/tests/compiler/possibly-null.debug.wat index ab572a0db3..de77339aa7 100644 --- a/tests/compiler/possibly-null.debug.wat +++ b/tests/compiler/possibly-null.debug.wat @@ -29,6 +29,7 @@ (export "testLogicalAndMulti" (func $export:possibly-null/testLogicalAndMulti)) (export "testLogicalOrMulti" (func $export:possibly-null/testLogicalOrMulti)) (export "testAssign" (func $export:possibly-null/testAssign)) + (export "testAssignInCondi" (func $export:possibly-null/testAssignInCondi)) (export "testNeverNull" (func $export:possibly-null/testNeverNull)) (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) (func $possibly-null/testTrue (param $0 i32) @@ -252,6 +253,18 @@ i32.const 0 drop ) + (func $possibly-null/testAssignInCondi (param $0 i32) + i32.const 0 + local.tee $0 + if + i32.const 0 + drop + else + i32.const 1 + i32.eqz + drop + end + ) (func $possibly-null/testNeverNull (param $0 i32) local.get $0 if @@ -278,8 +291,7 @@ end local.tee $2 i32.store - local.get $2 - call $possibly-null/requireNonNull + i32.const 0 drop global.get $~lib/memory/__stack_pointer i32.const 4 @@ -591,6 +603,22 @@ i32.add global.set $~lib/memory/__stack_pointer ) + (func $export:possibly-null/testAssignInCondi (param $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + call $possibly-null/testAssignInCondi + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $export:possibly-null/testNeverNull (param $0 i32) global.get $~lib/memory/__stack_pointer i32.const 4 diff --git a/tests/compiler/possibly-null.release.wat b/tests/compiler/possibly-null.release.wat index 3e92d6d3c3..b2326f4c35 100644 --- a/tests/compiler/possibly-null.release.wat +++ b/tests/compiler/possibly-null.release.wat @@ -23,6 +23,7 @@ (export "testLogicalAndMulti" (func $export:possibly-null/testLogicalAndMulti)) (export "testLogicalOrMulti" (func $export:possibly-null/testLogicalAndMulti)) (export "testAssign" (func $export:possibly-null/testLogicalAndMulti)) + (export "testAssignInCondi" (func $export:possibly-null/testTrue)) (export "testNeverNull" (func $export:possibly-null/testTrue)) (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) (func $export:possibly-null/testTrue (param $0 i32) diff --git a/tests/compiler/possibly-null.ts b/tests/compiler/possibly-null.ts index e1ce355fac..c595e58d88 100644 --- a/tests/compiler/possibly-null.ts +++ b/tests/compiler/possibly-null.ts @@ -119,6 +119,13 @@ export function testAssign(a: Ref | null, b: Ref): void { a = b; if (isNullable(a)) ERROR("should be non-nullable"); } +export function testAssignInCondi(a: Ref | null): void { + if ((a = null)) { + if (isNullable(a)) ERROR("should be non-nullable"); + } else { + if (!isNullable(a)) ERROR("should be non-nullable"); + } +} export function testNeverNull(a: Ref | null): void { if (a) { @@ -128,5 +135,5 @@ export function testNeverNull(a: Ref | null): void { export function testLogicalOrTypeInfer(a: Ref | null, b: Ref): void { let c: Ref = a || b; - requireNonNull(c); + if (isNullable(c)) ERROR("should be non-nullable"); } From dc2f28af668aca784e06e629ff7c331bdb0ffaab Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 4 Aug 2022 22:01:11 +0800 Subject: [PATCH 43/55] rename `conditionalNarrowedType` with `typeNarrowChecker` --- src/flow.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 1f0c27b114..a13119df5b 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -241,8 +241,8 @@ export class Flow { thisFieldFlags: Map | null = null; /** type narrow */ narrowedTypes: NarrowedTypeMap | null = null; - /** conditional type narrow, eg if (condi) */ - conditionalNarrowedType: TypeNarrowChecker = new TypeNarrowChecker(); + /** handle conditional type narrow, eg if (condi) */ + typeNarrowChecker: TypeNarrowChecker = new TypeNarrowChecker(); /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; @@ -320,7 +320,7 @@ export class Flow { branch.localFlags = this.localFlags.slice(); let narrowedTypes = this.narrowedTypes; branch.narrowedTypes = narrowedTypes ? narrowedTypes.clone() : null; - branch.conditionalNarrowedType = this.conditionalNarrowedType; + branch.typeNarrowChecker = this.typeNarrowChecker; if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); branch.thisFieldFlags = uniqueMap(thisFieldFlags); @@ -622,7 +622,7 @@ export class Flow { assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (type && !type.isReference) return; this.setNarrowedType(element, type); - this.conditionalNarrowedType.setAssignType(expr, element, type); + this.typeNarrowChecker.setAssignType(expr, element, type); } /** set type narrow */ setNarrowedType(element: TypedElement, type: Type): void { @@ -644,19 +644,19 @@ export class Flow { assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); let thisNarrowedTypes = this.narrowedTypes; if (thisNarrowedTypes) thisNarrowedTypes.delete(element); - this.conditionalNarrowedType.removeElement(element); + this.typeNarrowChecker.removeElement(element); } /** set conditional type narrow */ setConditionNarrowedType(expr: ExpressionRef, element: TypedElement, type: Type): void { assert(element.kind == ElementKind.LOCAL, "type narrowing only support Local"); if (type && !type.isReference) return; - this.conditionalNarrowedType.setConditionNarrowedType(expr, element, type); + this.typeNarrowChecker.setConditionNarrowedType(expr, element, type); } /** take effect conditional type narrow if condition is true */ inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { - let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfTrue(condi, this); + let condiNarrow = this.typeNarrowChecker.collectNarrowedTypeIfTrue(condi, this); if (condiNarrow.size != 0) { let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); this.narrowedTypes = narrowedTypes; @@ -665,7 +665,7 @@ export class Flow { } /** take effect conditional type narrow if condition is false */ inheritNarrowedTypeIfFalse(condi: ExpressionRef): void { - let condiNarrow = this.conditionalNarrowedType.collectNarrowedTypeIfFalse(condi, this); + let condiNarrow = this.typeNarrowChecker.collectNarrowedTypeIfFalse(condi, this); if (condiNarrow.size != 0) { let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); this.narrowedTypes = narrowedTypes; From 41d4e4b57d50b2011fe21cf35f8dd1d00755a333 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Fri, 5 Aug 2022 11:02:53 +0800 Subject: [PATCH 44/55] refactory testcase --- tests/compiler/typenarrow-error.json | 35 +- tests/compiler/typenarrow-error.ts | 112 +- tests/compiler/typenarrow.debug.wat | 2400 ++----------------------- tests/compiler/typenarrow.release.wat | 2271 ++++++----------------- tests/compiler/typenarrow.ts | 71 +- 5 files changed, 865 insertions(+), 4024 deletions(-) diff --git a/tests/compiler/typenarrow-error.json b/tests/compiler/typenarrow-error.json index 1d74cbdee9..f7ab94db3a 100644 --- a/tests/compiler/typenarrow-error.json +++ b/tests/compiler/typenarrow-error.json @@ -3,12 +3,39 @@ ], "stderr": [ "TS2339: Property 'b1' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b2' does not exist on type 'typenarrow-error/A", + + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", - "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b3' does not exist on type 'typenarrow-error/A", + + "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", + "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", "TS2339: Property 'b4' does not exist on type 'typenarrow-error/A", - "TS2339: Property 'b5' does not exist on type 'typenarrow-error/A", - "TS2339: Property 'b6' does not exist on type 'typenarrow-error/A", - "EOF" + + "TS2339: Property 'c1' does not exist on type 'typenarrow-error/A" + ] } diff --git a/tests/compiler/typenarrow-error.ts b/tests/compiler/typenarrow-error.ts index a8154f398f..ff9c331fb4 100644 --- a/tests/compiler/typenarrow-error.ts +++ b/tests/compiler/typenarrow-error.ts @@ -7,51 +7,93 @@ class B extends A { b5: i32; b6: i32; } -class C extends A { +class C extends B { c1: i32; } -let value = new A(); -let condi = true; - -// or -if (condi || value instanceof B) { - // TS2339: Property 'b1' does not exist on type 'typenarrow-error/A'. - value.b1; -} - -if (value instanceof B) { - value = new A(); - // TS2339: Property 'b2' does not exist on type 'typenarrow-error/A - value.b2; +export function testAssign(v0: A): void { + // TS2339: Property 'b1' does not exist on type 'typenarrow-error/A + if (v0 instanceof B) { + v0 = new A(); + v0.b1; + } } -if (value instanceof B || condi) { - // TS2339: Property 'b3' does not exist on type 'typenarrow-error/A - value.b3; +export function testOr(v0: A, v1: A): void { + // TS2339: Property 'b2' does not exist on type 'typenarrow-error/A'. + if (v0 instanceof B || v1 instanceof B) { + v0.b2; + v1.b2; + } else { + v0.b2; + v1.b2; + } + if (!(v0 instanceof B) || !(v1 instanceof B)) { + v0.b2; + v1.b2; + } + if (v0 instanceof B || !(v1 instanceof B)) { + v0.b2; + v1.b2; + } else { + v0.b2; + } + if (!(v0 instanceof B) || v1 instanceof B) { + v0.b2; + v1.b2; + } else { + v1.b2; + } } -// incompatibility -if (value instanceof B && value instanceof C) { - // TS2339: Property 'c1' does not exist on type 'typenarrow-error/A - value.c1; +export function testAnd(v0: A, v1: A): void { + // TS2339: Property 'b3' does not exist on type 'typenarrow-error/A'. + if (v0 instanceof B && v1 instanceof B) { + } else { + v0.b3; + v1.b3; + } + if (!(v0 instanceof B) && !(v1 instanceof B)) { + v0.b3; + v1.b3; + } else { + v0.b3; + v1.b3; + } + if (v0 instanceof B && !(v1 instanceof B)) { + v1.b3; + } else { + v0.b3; + v1.b3; + } + if (!(v0 instanceof B) && v1 instanceof B) { + v0.b3; + } else { + v0.b3; + v1.b3; + } } -// TS2339: Property 'b4' does not exist on type 'typenarrow-error/A -if (value instanceof B || value.b4) { +export function testAssignOr(v0: A, v1: A): void { + // TS2339: Property 'b4' does not exist on type 'typenarrow-error/A + if (v0 instanceof B || (v0 = v1)) { + v0.b4; + } else { + v0.b4; + } } - -if (value instanceof B && (value = new A())) { - // TS2339: Property 'b5' does not exist on type 'typenarrow-error/A - value.b5; +export function testAssignAnd(v0: A, v1: A): void { + // TS2339: Property 'b4' does not exist on type 'typenarrow-error/A + if (v0 instanceof B && (v0 = v1)) { + v0.b4; + } else { + v0.b4; + } } -declare function externalBool(): bool; - -// externalBool may return `true` while `value` isn't instanceof C -if (externalBool() || value instanceof B) { - // TS2339: Property 'b6' does not exist on type 'typenarrow-error/A' - value.b6; +export function testLogicOr(v0: A): void { + // TS2339: Property 'c1' does not exist on type 'typenarrow-error/A + if (v0 instanceof B || v0 instanceof C) { + v0.c1; + } } - -ERROR("EOF"); diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index 5130186d1f..fef3aa1326 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -1,2120 +1,25 @@ (module (type $i32_i32_=>_none (func (param i32 i32))) - (type $i32_=>_i32 (func (param i32) (result i32))) - (type $i32_=>_none (func (param i32))) - (type $none_=>_none (func)) - (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) - (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) - (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $none_=>_i32 (func (result i32))) - (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) - (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) - (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) - (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) - (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) - (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) - (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 448)) - (global $~lib/memory/__data_end i32 (i32.const 508)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16892)) - (global $~lib/memory/__heap_base i32 (i32.const 16892)) - (memory $0 1) - (data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") - (data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 144) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 176) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 204) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00") - (data (i32.const 268) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") - (data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 412) "\1c\00\00\00\00\00\00\00\00\00\00\00\05\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 448) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\04\00\00\00") - (table $0 2 2 funcref) - (elem $0 (i32.const 1) $typenarrow/B#b1) - (export "testlocal" (func $typenarrow/testlocal)) - (export "memory" (memory $0)) - (start $~start) - (func $~lib/rt/itcms/Object#set:nextWithColor (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=4 - ) - (func $~lib/rt/itcms/Object#set:prev (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=8 - ) - (func $~lib/rt/itcms/initLazy (param $0 i32) (result i32) - local.get $0 - local.get $0 - call $~lib/rt/itcms/Object#set:nextWithColor - local.get $0 - local.get $0 - call $~lib/rt/itcms/Object#set:prev - local.get $0 - ) - (func $~lib/rt/itcms/Object#get:next (param $0 i32) (result i32) - local.get $0 - i32.load offset=4 - i32.const 3 - i32.const -1 - i32.xor - i32.and - ) - (func $~lib/rt/itcms/Object#get:color (param $0 i32) (result i32) - local.get $0 - i32.load offset=4 - i32.const 3 - i32.and - ) - (func $~lib/rt/itcms/visitRoots (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - local.get $0 - call $~lib/rt/__visit_globals - global.get $~lib/rt/itcms/pinSpace - local.set $1 - local.get $1 - call $~lib/rt/itcms/Object#get:next - local.set $2 - loop $while-continue|0 - local.get $2 - local.get $1 - i32.ne - local.set $3 - local.get $3 - if - i32.const 1 - drop - local.get $2 - call $~lib/rt/itcms/Object#get:color - i32.const 3 - i32.eq - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 159 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 20 - i32.add - local.get $0 - call $~lib/rt/__visit_members - local.get $2 - call $~lib/rt/itcms/Object#get:next - local.set $2 - br $while-continue|0 - end - end - ) - (func $~lib/rt/itcms/Object#set:color (param $0 i32) (param $1 i32) - local.get $0 - local.get $0 - i32.load offset=4 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.get $1 - i32.or - call $~lib/rt/itcms/Object#set:nextWithColor - ) - (func $~lib/rt/itcms/Object#set:next (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - local.get $0 - i32.load offset=4 - i32.const 3 - i32.and - i32.or - call $~lib/rt/itcms/Object#set:nextWithColor - ) - (func $~lib/rt/itcms/Object#unlink (param $0 i32) - (local $1 i32) - (local $2 i32) - local.get $0 - call $~lib/rt/itcms/Object#get:next - local.set $1 - local.get $1 - i32.const 0 - i32.eq - if - i32.const 1 - drop - local.get $0 - i32.load offset=8 - i32.const 0 - i32.eq - if (result i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 127 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - return - end - local.get $0 - i32.load offset=8 - local.set $2 - i32.const 1 - drop - local.get $2 - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 131 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.get $2 - call $~lib/rt/itcms/Object#set:prev - local.get $2 - local.get $1 - call $~lib/rt/itcms/Object#set:next - ) - (func $~lib/rt/__typeinfo (param $0 i32) (result i32) - (local $1 i32) - global.get $~lib/rt/__rtti_base - local.set $1 - local.get $0 - local.get $1 - i32.load - i32.gt_u - if - i32.const 224 - i32.const 288 - i32.const 22 - i32.const 28 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 4 - i32.add - local.get $0 - i32.const 8 - i32.mul - i32.add - i32.load - ) - (func $~lib/rt/itcms/Object#get:isPointerfree (param $0 i32) (result i32) - (local $1 i32) - local.get $0 - i32.load offset=12 - local.set $1 - local.get $1 - i32.const 1 - i32.le_u - if (result i32) - i32.const 1 - else - local.get $1 - call $~lib/rt/__typeinfo - i32.const 32 - i32.and - i32.const 0 - i32.ne - end - ) - (func $~lib/rt/itcms/Object#linkTo (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - local.get $1 - i32.load offset=8 - local.set $3 - local.get $0 - local.get $1 - local.get $2 - i32.or - call $~lib/rt/itcms/Object#set:nextWithColor - local.get $0 - local.get $3 - call $~lib/rt/itcms/Object#set:prev - local.get $3 - local.get $0 - call $~lib/rt/itcms/Object#set:next - local.get $1 - local.get $0 - call $~lib/rt/itcms/Object#set:prev - ) - (func $~lib/rt/itcms/Object#makeGray (param $0 i32) - (local $1 i32) - local.get $0 - global.get $~lib/rt/itcms/iter - i32.eq - if - local.get $0 - i32.load offset=8 - local.tee $1 - i32.eqz - if (result i32) - i32.const 0 - i32.const 96 - i32.const 147 - i32.const 30 - call $~lib/builtins/abort - unreachable - else - local.get $1 - end - global.set $~lib/rt/itcms/iter - end - local.get $0 - call $~lib/rt/itcms/Object#unlink - local.get $0 - global.get $~lib/rt/itcms/toSpace - local.get $0 - call $~lib/rt/itcms/Object#get:isPointerfree - if (result i32) - global.get $~lib/rt/itcms/white - i32.eqz - else - i32.const 2 - end - call $~lib/rt/itcms/Object#linkTo - ) - (func $~lib/rt/itcms/__visit (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $0 - i32.eqz - if - return - end - local.get $0 - i32.const 20 - i32.sub - local.set $2 - i32.const 0 - drop - local.get $2 - call $~lib/rt/itcms/Object#get:color - global.get $~lib/rt/itcms/white - i32.eq - if - local.get $2 - call $~lib/rt/itcms/Object#makeGray - global.get $~lib/rt/itcms/visitCount - i32.const 1 - i32.add - global.set $~lib/rt/itcms/visitCount - end - ) - (func $~lib/rt/itcms/visitStack (param $0 i32) - (local $1 i32) - (local $2 i32) - global.get $~lib/memory/__stack_pointer - local.set $1 - loop $while-continue|0 - local.get $1 - global.get $~lib/memory/__heap_base - i32.lt_u - local.set $2 - local.get $2 - if - local.get $1 - i32.load - local.get $0 - call $~lib/rt/itcms/__visit - local.get $1 - i32.const 4 - i32.add - local.set $1 - br $while-continue|0 - end - end - ) - (func $~lib/rt/itcms/Object#get:size (param $0 i32) (result i32) - i32.const 4 - local.get $0 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - ) - (func $~lib/rt/tlsf/Root#set:flMap (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store - ) - (func $~lib/rt/common/BLOCK#set:mmInfo (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store - ) - (func $~lib/rt/tlsf/Block#set:prev (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=4 - ) - (func $~lib/rt/tlsf/Block#set:next (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=8 - ) - (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - local.get $1 - i32.load - local.set $2 - i32.const 1 - drop - local.get $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 268 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.set $3 - i32.const 1 - drop - local.get $3 - i32.const 12 - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 270 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $4 - local.get $3 - i32.const 4 - i32.shr_u - local.set $5 - else - local.get $3 - local.tee $6 - i32.const 1073741820 - local.tee $7 - local.get $6 - local.get $7 - i32.lt_u - select - local.set $6 - i32.const 31 - local.get $6 - i32.clz - i32.sub - local.set $4 - local.get $6 - local.get $4 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $5 - local.get $4 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $4 - end - i32.const 1 - drop - local.get $4 - i32.const 23 - i32.lt_u - if (result i32) - local.get $5 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 284 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load offset=4 - local.set $8 - local.get $1 - i32.load offset=8 - local.set $9 - local.get $8 - if - local.get $8 - local.get $9 - call $~lib/rt/tlsf/Block#set:next - end - local.get $9 - if - local.get $9 - local.get $8 - call $~lib/rt/tlsf/Block#set:prev - end - local.get $1 - local.get $0 - local.set $10 - local.get $4 - local.set $6 - local.get $5 - local.set $7 - local.get $10 - local.get $6 - i32.const 4 - i32.shl - local.get $7 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - i32.eq - if - local.get $0 - local.set $11 - local.get $4 - local.set $10 - local.get $5 - local.set $6 - local.get $9 - local.set $7 - local.get $11 - local.get $10 - i32.const 4 - i32.shl - local.get $6 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $7 - i32.store offset=96 - local.get $9 - i32.eqz - if - local.get $0 - local.set $6 - local.get $4 - local.set $7 - local.get $6 - local.get $7 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.set $6 - local.get $0 - local.set $7 - local.get $4 - local.set $11 - local.get $6 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $6 - local.set $10 - local.get $7 - local.get $11 - i32.const 2 - i32.shl - i32.add - local.get $10 - i32.store offset=4 - local.get $6 - i32.eqz - if - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - call $~lib/rt/tlsf/Root#set:flMap - end - end - end - ) - (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - i32.const 1 - drop - local.get $1 - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 201 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load - local.set $2 - i32.const 1 - drop - local.get $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 203 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.set $3 - local.get $3 - i32.const 4 - i32.add - local.get $3 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $4 - local.get $4 - i32.load - local.set $5 - local.get $5 - i32.const 1 - i32.and - if - local.get $0 - local.get $4 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $2 - i32.const 4 - i32.add - local.get $5 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.tee $2 - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $1 - local.set $3 - local.get $3 - i32.const 4 - i32.add - local.get $3 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $4 - local.get $4 - i32.load - local.set $5 - end - local.get $2 - i32.const 2 - i32.and - if - local.get $1 - local.set $3 - local.get $3 - i32.const 4 - i32.sub - i32.load - local.set $3 - local.get $3 - i32.load - local.set $6 - i32.const 1 - drop - local.get $6 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 221 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $3 - call $~lib/rt/tlsf/removeBlock - local.get $3 - local.set $1 - local.get $1 - local.get $6 - i32.const 4 - i32.add - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.tee $2 - call $~lib/rt/common/BLOCK#set:mmInfo - end - local.get $4 - local.get $5 - i32.const 2 - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.set $7 - i32.const 1 - drop - local.get $7 - i32.const 12 - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 233 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - i32.const 1 - drop - local.get $1 - i32.const 4 - i32.add - local.get $7 - i32.add - local.get $4 - i32.eq - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 234 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $4 - i32.const 4 - i32.sub - local.get $1 - i32.store - local.get $7 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $8 - local.get $7 - i32.const 4 - i32.shr_u - local.set $9 - else - local.get $7 - local.tee $3 - i32.const 1073741820 - local.tee $6 - local.get $3 - local.get $6 - i32.lt_u - select - local.set $3 - i32.const 31 - local.get $3 - i32.clz - i32.sub - local.set $8 - local.get $3 - local.get $8 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $9 - local.get $8 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $8 - end - i32.const 1 - drop - local.get $8 - i32.const 23 - i32.lt_u - if (result i32) - local.get $9 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 251 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $10 - local.get $8 - local.set $3 - local.get $9 - local.set $6 - local.get $10 - local.get $3 - i32.const 4 - i32.shl - local.get $6 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $11 - local.get $1 - i32.const 0 - call $~lib/rt/tlsf/Block#set:prev - local.get $1 - local.get $11 - call $~lib/rt/tlsf/Block#set:next - local.get $11 - if - local.get $11 - local.get $1 - call $~lib/rt/tlsf/Block#set:prev - end - local.get $0 - local.set $12 - local.get $8 - local.set $10 - local.get $9 - local.set $3 - local.get $1 - local.set $6 - local.get $12 - local.get $10 - i32.const 4 - i32.shl - local.get $3 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=96 - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $8 - i32.shl - i32.or - call $~lib/rt/tlsf/Root#set:flMap - local.get $0 - local.set $13 - local.get $8 - local.set $12 - local.get $0 - local.set $3 - local.get $8 - local.set $6 - local.get $3 - local.get $6 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - i32.const 1 - local.get $9 - i32.shl - i32.or - local.set $10 - local.get $13 - local.get $12 - i32.const 2 - i32.shl - i32.add - local.get $10 - i32.store offset=4 - ) - (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - i32.const 1 - drop - local.get $1 - local.get $2 - i32.le_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 377 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 4 - i32.add - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.sub - local.set $1 - local.get $2 - i32.const 15 - i32.const -1 - i32.xor - i32.and - local.set $2 - local.get $0 - local.set $3 - local.get $3 - i32.load offset=1568 - local.set $4 - i32.const 0 - local.set $5 - local.get $4 - if - i32.const 1 - drop - local.get $1 - local.get $4 - i32.const 4 - i32.add - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 384 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 16 - i32.sub - local.get $4 - i32.eq - if - local.get $1 - i32.const 16 - i32.sub - local.set $1 - local.get $4 - i32.load - local.set $5 - else - nop - end - else - i32.const 1 - drop - local.get $1 - local.get $0 - i32.const 1572 - i32.add - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 397 - i32.const 5 - call $~lib/builtins/abort - unreachable - end - end - local.get $2 - local.get $1 - i32.sub - local.set $6 - local.get $6 - i32.const 4 - i32.const 12 - i32.add - i32.const 4 - i32.add - i32.lt_u - if - i32.const 0 - return - end - local.get $6 - i32.const 2 - i32.const 4 - i32.mul - i32.sub - local.set $7 - local.get $1 - local.set $8 - local.get $8 - local.get $7 - i32.const 1 - i32.or - local.get $5 - i32.const 2 - i32.and - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $8 - i32.const 0 - call $~lib/rt/tlsf/Block#set:prev - local.get $8 - i32.const 0 - call $~lib/rt/tlsf/Block#set:next - local.get $1 - i32.const 4 - i32.add - local.get $7 - i32.add - local.set $4 - local.get $4 - i32.const 0 - i32.const 2 - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $0 - local.set $9 - local.get $4 - local.set $3 - local.get $9 - local.get $3 - i32.store offset=1568 - local.get $0 - local.get $8 - call $~lib/rt/tlsf/insertBlock - i32.const 1 - ) - (func $~lib/rt/tlsf/initialize - (local $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - i32.const 0 - drop - global.get $~lib/memory/__heap_base - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - local.set $0 - memory.size - local.set $1 - local.get $0 - i32.const 1572 - i32.add - i32.const 65535 - i32.add - i32.const 65535 - i32.const -1 - i32.xor - i32.and - i32.const 16 - i32.shr_u - local.set $2 - local.get $2 - local.get $1 - i32.gt_s - if (result i32) - local.get $2 - local.get $1 - i32.sub - memory.grow - i32.const 0 - i32.lt_s - else - i32.const 0 - end - if - unreachable - end - local.get $0 - local.set $3 - local.get $3 - i32.const 0 - call $~lib/rt/tlsf/Root#set:flMap - local.get $3 - local.set $5 - i32.const 0 - local.set $4 - local.get $5 - local.get $4 - i32.store offset=1568 - i32.const 0 - local.set $5 - loop $for-loop|0 - local.get $5 - i32.const 23 - i32.lt_u - local.set $4 - local.get $4 - if - local.get $3 - local.set $8 - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - local.get $8 - local.get $7 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=4 - i32.const 0 - local.set $8 - loop $for-loop|1 - local.get $8 - i32.const 16 - i32.lt_u - local.set $7 - local.get $7 - if - local.get $3 - local.set $11 - local.get $5 - local.set $10 - local.get $8 - local.set $9 - i32.const 0 - local.set $6 - local.get $11 - local.get $10 - i32.const 4 - i32.shl - local.get $9 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=96 - local.get $8 - i32.const 1 - i32.add - local.set $8 - br $for-loop|1 - end - end - local.get $5 - i32.const 1 - i32.add - local.set $5 - br $for-loop|0 - end - end - local.get $0 - i32.const 1572 - i32.add - local.set $12 - i32.const 0 - drop - local.get $3 - local.get $12 - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - drop - local.get $3 - global.set $~lib/rt/tlsf/ROOT - ) - (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) - (local $1 i32) - local.get $0 - i32.const 4 - i32.sub - local.set $1 - local.get $0 - i32.const 0 - i32.ne - if (result i32) - local.get $0 - i32.const 15 - i32.and - i32.eqz - else - i32.const 0 - end - if (result i32) - local.get $1 - i32.load - i32.const 1 - i32.and - i32.eqz - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 559 - i32.const 3 - call $~lib/builtins/abort - unreachable - end - local.get $1 - ) - (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) - i32.const 0 - drop - local.get $1 - local.get $1 - i32.load - i32.const 1 - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - ) - (func $~lib/rt/tlsf/__free (param $0 i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.lt_u - if - return - end - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/checkUsedBlock - call $~lib/rt/tlsf/freeBlock - ) - (func $~lib/rt/itcms/free (param $0 i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.lt_u - if - local.get $0 - i32.const 0 - call $~lib/rt/itcms/Object#set:nextWithColor - local.get $0 - i32.const 0 - call $~lib/rt/itcms/Object#set:prev - else - global.get $~lib/rt/itcms/total - local.get $0 - call $~lib/rt/itcms/Object#get:size - i32.sub - global.set $~lib/rt/itcms/total - i32.const 0 - drop - local.get $0 - i32.const 4 - i32.add - call $~lib/rt/tlsf/__free - end - ) - (func $~lib/rt/itcms/step (result i32) - (local $0 i32) - (local $1 i32) - (local $2 i32) - block $break|0 - block $case2|0 - block $case1|0 - block $case0|0 - global.get $~lib/rt/itcms/state - local.set $1 - local.get $1 - i32.const 0 - i32.eq - br_if $case0|0 - local.get $1 - i32.const 1 - i32.eq - br_if $case1|0 - local.get $1 - i32.const 2 - i32.eq - br_if $case2|0 - br $break|0 - end - i32.const 1 - global.set $~lib/rt/itcms/state - i32.const 0 - global.set $~lib/rt/itcms/visitCount - i32.const 0 - call $~lib/rt/itcms/visitRoots - global.get $~lib/rt/itcms/toSpace - global.set $~lib/rt/itcms/iter - global.get $~lib/rt/itcms/visitCount - i32.const 1 - i32.mul - return - end - global.get $~lib/rt/itcms/white - i32.eqz - local.set $1 - global.get $~lib/rt/itcms/iter - call $~lib/rt/itcms/Object#get:next - local.set $0 - loop $while-continue|1 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - local.set $2 - local.get $2 - if - local.get $0 - global.set $~lib/rt/itcms/iter - local.get $0 - call $~lib/rt/itcms/Object#get:color - local.get $1 - i32.ne - if - local.get $0 - local.get $1 - call $~lib/rt/itcms/Object#set:color - i32.const 0 - global.set $~lib/rt/itcms/visitCount - local.get $0 - i32.const 20 - i32.add - i32.const 0 - call $~lib/rt/__visit_members - global.get $~lib/rt/itcms/visitCount - i32.const 1 - i32.mul - return - end - local.get $0 - call $~lib/rt/itcms/Object#get:next - local.set $0 - br $while-continue|1 - end - end - i32.const 0 - global.set $~lib/rt/itcms/visitCount - i32.const 0 - call $~lib/rt/itcms/visitRoots - global.get $~lib/rt/itcms/iter - call $~lib/rt/itcms/Object#get:next - local.set $0 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.eq - if - i32.const 0 - call $~lib/rt/itcms/visitStack - global.get $~lib/rt/itcms/iter - call $~lib/rt/itcms/Object#get:next - local.set $0 - loop $while-continue|2 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - local.set $2 - local.get $2 - if - local.get $0 - call $~lib/rt/itcms/Object#get:color - local.get $1 - i32.ne - if - local.get $0 - local.get $1 - call $~lib/rt/itcms/Object#set:color - local.get $0 - i32.const 20 - i32.add - i32.const 0 - call $~lib/rt/__visit_members - end - local.get $0 - call $~lib/rt/itcms/Object#get:next - local.set $0 - br $while-continue|2 - end - end - global.get $~lib/rt/itcms/fromSpace - local.set $2 - global.get $~lib/rt/itcms/toSpace - global.set $~lib/rt/itcms/fromSpace - local.get $2 - global.set $~lib/rt/itcms/toSpace - local.get $1 - global.set $~lib/rt/itcms/white - local.get $2 - call $~lib/rt/itcms/Object#get:next - global.set $~lib/rt/itcms/iter - i32.const 2 - global.set $~lib/rt/itcms/state - end - global.get $~lib/rt/itcms/visitCount - i32.const 1 - i32.mul - return - end - global.get $~lib/rt/itcms/iter - local.set $0 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - if - local.get $0 - call $~lib/rt/itcms/Object#get:next - global.set $~lib/rt/itcms/iter - i32.const 1 - drop - local.get $0 - call $~lib/rt/itcms/Object#get:color - global.get $~lib/rt/itcms/white - i32.eqz - i32.eq - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 228 - i32.const 20 - call $~lib/builtins/abort - unreachable - end - local.get $0 - call $~lib/rt/itcms/free - i32.const 10 - return - end - global.get $~lib/rt/itcms/toSpace - global.get $~lib/rt/itcms/toSpace - call $~lib/rt/itcms/Object#set:nextWithColor - global.get $~lib/rt/itcms/toSpace - global.get $~lib/rt/itcms/toSpace - call $~lib/rt/itcms/Object#set:prev - i32.const 0 - global.set $~lib/rt/itcms/state - br $break|0 - end - i32.const 0 - ) - (func $~lib/rt/itcms/interrupt - (local $0 i32) - i32.const 0 - drop - i32.const 0 - drop - i32.const 1024 - i32.const 200 - i32.mul - i32.const 100 - i32.div_u - local.set $0 - loop $do-loop|0 - local.get $0 - call $~lib/rt/itcms/step - i32.sub - local.set $0 - global.get $~lib/rt/itcms/state - i32.const 0 - i32.eq - if - i32.const 0 - drop - global.get $~lib/rt/itcms/total - i64.extend_i32_u - i32.const 200 - i64.extend_i32_u - i64.mul - i64.const 100 - i64.div_u - i32.wrap_i64 - i32.const 1024 - i32.add - global.set $~lib/rt/itcms/threshold - i32.const 0 - drop - return - end - local.get $0 - i32.const 0 - i32.gt_s - br_if $do-loop|0 - end - i32.const 0 - drop - global.get $~lib/rt/itcms/total - i32.const 1024 - global.get $~lib/rt/itcms/total - global.get $~lib/rt/itcms/threshold - i32.sub - i32.const 1024 - i32.lt_u - i32.mul - i32.add - global.set $~lib/rt/itcms/threshold - i32.const 0 - drop - ) - (func $~lib/rt/tlsf/computeSize (param $0 i32) (result i32) - local.get $0 - i32.const 12 - i32.le_u - if (result i32) - i32.const 12 - else - local.get $0 - i32.const 4 - i32.add - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.sub - end - ) - (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) - local.get $0 - i32.const 1073741820 - i32.gt_u - if - i32.const 32 - i32.const 368 - i32.const 458 - i32.const 29 - call $~lib/builtins/abort - unreachable - end - local.get $0 - call $~lib/rt/tlsf/computeSize - ) - (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - local.get $1 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $2 - local.get $1 - i32.const 4 - i32.shr_u - local.set $3 - else - local.get $1 - i32.const 536870910 - i32.lt_u - if (result i32) - local.get $1 - i32.const 1 - i32.const 27 - local.get $1 - i32.clz - i32.sub - i32.shl - i32.add - i32.const 1 - i32.sub - else - local.get $1 - end - local.set $4 - i32.const 31 - local.get $4 - i32.clz - i32.sub - local.set $2 - local.get $4 - local.get $2 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $3 - local.get $2 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $2 - end - i32.const 1 - drop - local.get $2 - i32.const 23 - i32.lt_u - if (result i32) - local.get $3 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 330 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $5 - local.get $2 - local.set $4 - local.get $5 - local.get $4 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - i32.const 0 - i32.const -1 - i32.xor - local.get $3 - i32.shl - i32.and - local.set $6 - i32.const 0 - local.set $7 - local.get $6 - i32.eqz - if - local.get $0 - i32.load - i32.const 0 - i32.const -1 - i32.xor - local.get $2 - i32.const 1 - i32.add - i32.shl - i32.and - local.set $5 - local.get $5 - i32.eqz - if - i32.const 0 - local.set $7 - else - local.get $5 - i32.ctz - local.set $2 - local.get $0 - local.set $8 - local.get $2 - local.set $4 - local.get $8 - local.get $4 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.set $6 - i32.const 1 - drop - local.get $6 - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 343 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $9 - local.get $2 - local.set $8 - local.get $6 - i32.ctz - local.set $4 - local.get $9 - local.get $8 - i32.const 4 - i32.shl - local.get $4 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $7 - end - else - local.get $0 - local.set $9 - local.get $2 - local.set $8 - local.get $6 - i32.ctz - local.set $4 - local.get $9 - local.get $8 - i32.const 4 - i32.shl - local.get $4 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $7 - end - local.get $7 - ) - (func $~lib/rt/tlsf/growMemory (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - i32.const 0 - drop - local.get $1 - i32.const 536870910 - i32.lt_u - if - local.get $1 - i32.const 1 - i32.const 27 - local.get $1 - i32.clz - i32.sub - i32.shl - i32.const 1 - i32.sub - i32.add - local.set $1 - end - memory.size - local.set $2 - local.get $1 - i32.const 4 - local.get $2 - i32.const 16 - i32.shl - i32.const 4 - i32.sub - local.get $0 - local.set $3 - local.get $3 - i32.load offset=1568 - i32.ne - i32.shl - i32.add - local.set $1 - local.get $1 - i32.const 65535 - i32.add - i32.const 65535 - i32.const -1 - i32.xor - i32.and - i32.const 16 - i32.shr_u - local.set $4 - local.get $2 - local.tee $3 - local.get $4 - local.tee $5 - local.get $3 - local.get $5 - i32.gt_s - select - local.set $6 - local.get $6 - memory.grow - i32.const 0 - i32.lt_s - if - local.get $4 - memory.grow - i32.const 0 - i32.lt_s - if - unreachable - end - end - memory.size - local.set $7 - local.get $0 - local.get $2 - i32.const 16 - i32.shl - local.get $7 - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - drop - ) - (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - i32.load - local.set $3 - i32.const 1 - drop - local.get $2 - i32.const 4 - i32.add - i32.const 15 - i32.and - i32.eqz - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 357 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.sub - local.set $4 - local.get $4 - i32.const 4 - i32.const 12 - i32.add - i32.ge_u - if - local.get $1 - local.get $2 - local.get $3 - i32.const 2 - i32.and - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $1 - i32.const 4 - i32.add - local.get $2 - i32.add - local.set $5 - local.get $5 - local.get $4 - i32.const 4 - i32.sub - i32.const 1 - i32.or - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $0 - local.get $5 - call $~lib/rt/tlsf/insertBlock - else - local.get $1 - local.get $3 - i32.const 1 - i32.const -1 - i32.xor - i32.and - call $~lib/rt/common/BLOCK#set:mmInfo - local.get $1 - local.set $5 - local.get $5 - i32.const 4 - i32.add - local.get $5 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.get $1 - local.set $5 - local.get $5 - i32.const 4 - i32.add - local.get $5 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - i32.load - i32.const 2 - i32.const -1 - i32.xor - i32.and - call $~lib/rt/common/BLOCK#set:mmInfo - end - ) - (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $1 - call $~lib/rt/tlsf/prepareSize - local.set $2 - local.get $0 - local.get $2 - call $~lib/rt/tlsf/searchBlock - local.set $3 - local.get $3 - i32.eqz - if - local.get $0 - local.get $2 - call $~lib/rt/tlsf/growMemory - local.get $0 - local.get $2 - call $~lib/rt/tlsf/searchBlock - local.set $3 - i32.const 1 - drop - local.get $3 - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 496 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - end - i32.const 1 - drop - local.get $3 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 368 - i32.const 498 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $3 - call $~lib/rt/tlsf/removeBlock - local.get $0 - local.get $3 - local.get $2 - call $~lib/rt/tlsf/prepareBlock - i32.const 0 - drop - local.get $3 - ) - (func $~lib/rt/tlsf/__alloc (param $0 i32) (result i32) - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/allocateBlock - i32.const 4 - i32.add - ) - (func $~lib/rt/itcms/Object#set:rtId (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=12 - ) - (func $~lib/rt/itcms/Object#set:rtSize (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store offset=16 - ) - (func $~lib/rt/itcms/__new (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $0 - i32.const 1073741804 - i32.ge_u - if - i32.const 32 - i32.const 96 - i32.const 260 - i32.const 31 - call $~lib/builtins/abort - unreachable - end - global.get $~lib/rt/itcms/total - global.get $~lib/rt/itcms/threshold - i32.ge_u - if - call $~lib/rt/itcms/interrupt - end - i32.const 16 - local.get $0 - i32.add - call $~lib/rt/tlsf/__alloc - i32.const 4 - i32.sub - local.set $2 - local.get $2 - local.get $1 - call $~lib/rt/itcms/Object#set:rtId - local.get $2 - local.get $0 - call $~lib/rt/itcms/Object#set:rtSize - local.get $2 - global.get $~lib/rt/itcms/fromSpace - global.get $~lib/rt/itcms/white - call $~lib/rt/itcms/Object#linkTo - global.get $~lib/rt/itcms/total - local.get $2 - call $~lib/rt/itcms/Object#get:size - i32.add - global.set $~lib/rt/itcms/total - local.get $2 - i32.const 20 - i32.add - local.set $3 - local.get $3 - i32.const 0 - local.get $0 - memory.fill - local.get $3 - ) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $none_=>_none (func)) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) + (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) + (global $~lib/rt/__rtti_base i32 (i32.const 48)) + (global $~lib/memory/__data_end i32 (i32.const 108)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16492)) + (global $~lib/memory/__heap_base i32 (i32.const 16492)) + (memory $0 1) + (data (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\05\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 48) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\04\00\00\00") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $typenarrow/B#b1) + (export "memory" (memory $0)) + (export "condiNarrow" (func $export:typenarrow/condiNarrow)) (func $~lib/rt/__instanceof (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) @@ -2158,25 +63,8 @@ (func $typenarrow/B#check (param $0 i32) (result i32) i32.const 1 ) - (func $typenarrow/testlocal - (local $0 i32) - (local $1 i32) + (func $typenarrow/condiNarrow (param $0 i32) (param $1 i32) (local $2 i32) - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - call $~stack_check - global.get $~lib/memory/__stack_pointer - i32.const 0 - i32.store - global.get $~lib/memory/__stack_pointer - i32.const 0 - call $typenarrow/A#constructor - local.tee $0 - i32.store - i32.const 1 - local.set $1 local.get $0 local.tee $2 i32.eqz @@ -2224,18 +112,36 @@ end if (result i32) local.get $1 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end else i32.const 0 end if local.get $0 call $typenarrow/B#b1 - local.get $0 - local.set $2 + local.get $1 + call $typenarrow/B#b1 end - local.get $1 + local.get $0 + local.tee $2 + i32.eqz if (result i32) - local.get $0 + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + local.get $1 local.tee $2 i32.eqz if (result i32) @@ -2245,20 +151,58 @@ i32.const 4 call $~lib/rt/__instanceof end + i32.eqz else i32.const 0 end if local.get $0 call $typenarrow/B#b1 - local.get $0 - local.set $2 end - local.get $1 + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + if (result i32) + local.get $1 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + else + i32.const 0 + end + if + local.get $1 + call $typenarrow/B#b1 + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz if (result i32) i32.const 1 else - local.get $0 + local.get $1 local.tee $2 i32.eqz if (result i32) @@ -2273,10 +217,41 @@ if nop else - i32.const 432 + i32.const 32 + drop + i32.const 32 + drop + end + local.get $0 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + if (result i32) + i32.const 1 + else + local.get $1 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end + i32.eqz + end + if + nop + else + i32.const 32 drop - local.get $0 - local.set $2 end local.get $0 local.tee $2 @@ -2293,14 +268,21 @@ i32.const 1 else local.get $1 + local.tee $2 + i32.eqz + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 4 + call $~lib/rt/__instanceof + end end if nop else - i32.const 432 + i32.const 32 drop - local.get $0 - local.set $2 end local.get $0 local.tee $2 @@ -2421,138 +403,42 @@ else local.get $0 call $typenarrow/B#b1 + local.get $0 + i32.load + drop end - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - ) - (func $~lib/rt/__visit_globals (param $0 i32) - (local $1 i32) - i32.const 224 - local.get $0 - call $~lib/rt/itcms/__visit - i32.const 32 - local.get $0 - call $~lib/rt/itcms/__visit - ) - (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $0 - i32.load - local.tee $2 - if - local.get $2 - local.get $1 - call $~lib/rt/itcms/__visit - end - ) - (func $~lib/function/Function<%28this:typenarrow/B%29=>void>#__visit (param $0 i32) (param $1 i32) - local.get $0 - i32.load offset=4 - local.get $1 - call $~lib/rt/itcms/__visit - ) - (func $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/function/Function<%28this:typenarrow/B%29=>void>#__visit - ) - (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) - block $invalid - block $typenarrow/C - block $~lib/function/Function<%28this:typenarrow/B%29=>void> - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid - end - return - end - return - end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit - return - end - return - end - return - end - local.get $0 - local.get $1 - call $~lib/function/Function<%28this:typenarrow/B%29=>void>~visit - return - end - return - end - unreachable - ) - (func $~start - memory.size - i32.const 16 - i32.shl - global.get $~lib/memory/__heap_base - i32.sub - i32.const 1 - i32.shr_u - global.set $~lib/rt/itcms/threshold - i32.const 144 - call $~lib/rt/itcms/initLazy - global.set $~lib/rt/itcms/pinSpace - i32.const 176 - call $~lib/rt/itcms/initLazy - global.set $~lib/rt/itcms/toSpace - i32.const 320 - call $~lib/rt/itcms/initLazy - global.set $~lib/rt/itcms/fromSpace ) (func $~stack_check global.get $~lib/memory/__stack_pointer global.get $~lib/memory/__data_end i32.lt_s if - i32.const 16912 - i32.const 16960 + i32.const 16512 + i32.const 16560 i32.const 1 i32.const 1 call $~lib/builtins/abort unreachable end ) - (func $typenarrow/A#constructor (param $0 i32) (result i32) - (local $1 i32) + (func $export:typenarrow/condiNarrow (param $0 i32) (param $1 i32) global.get $~lib/memory/__stack_pointer - i32.const 4 + i32.const 8 i32.sub global.set $~lib/memory/__stack_pointer call $~stack_check global.get $~lib/memory/__stack_pointer - i32.const 0 - i32.store local.get $0 - i32.eqz - if - global.get $~lib/memory/__stack_pointer - i32.const 0 - i32.const 3 - call $~lib/rt/itcms/__new - local.tee $0 - i32.store - end + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 local.get $0 - local.set $1 + local.get $1 + call $typenarrow/condiNarrow global.get $~lib/memory/__stack_pointer - i32.const 4 + i32.const 8 i32.add global.set $~lib/memory/__stack_pointer - local.get $1 ) ) diff --git a/tests/compiler/typenarrow.release.wat b/tests/compiler/typenarrow.release.wat index d1a36d1c83..5363dea36e 100644 --- a/tests/compiler/typenarrow.release.wat +++ b/tests/compiler/typenarrow.release.wat @@ -1,1599 +1,717 @@ (module - (type $none_=>_none (func)) (type $i32_i32_=>_none (func (param i32 i32))) - (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) - (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) - (type $none_=>_i32 (func (result i32))) - (type $i32_=>_i32 (func (param i32) (result i32))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) - (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) - (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) - (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17916)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17516)) (memory $0 1) - (data (i32.const 1036) "<") - (data (i32.const 1048) "\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") - (data (i32.const 1100) "<") - (data (i32.const 1112) "\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s") - (data (i32.const 1228) "<") - (data (i32.const 1240) "\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") - (data (i32.const 1292) ",") - (data (i32.const 1304) "\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") - (data (i32.const 1372) "<") - (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") - (data (i32.const 1436) "\1c") - (data (i32.const 1448) "\05\00\00\00\08\00\00\00\01") - (data (i32.const 1472) "\07\00\00\00 \00\00\00\00\00\00\00 ") - (data (i32.const 1500) " \00\00\00\00\00\00\00 \00\00\00\03") - (data (i32.const 1524) " \00\00\00\04") - (export "testlocal" (func $typenarrow/testlocal)) + (data (i32.const 1036) "\1c") + (data (i32.const 1048) "\05\00\00\00\08\00\00\00\01") + (data (i32.const 1072) "\07\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1100) " \00\00\00\00\00\00\00 \00\00\00\03") + (data (i32.const 1124) " \00\00\00\04") (export "memory" (memory $0)) - (start $~start) - (func $~lib/rt/itcms/visitRoots - (local $0 i32) - (local $1 i32) - i32.const 1248 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - i32.const 1056 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - global.get $~lib/rt/itcms/pinSpace - local.tee $1 - i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - loop $while-continue|0 - local.get $0 - local.get $1 - i32.ne - if - local.get $0 - i32.load offset=4 - i32.const 3 - i32.and - i32.const 3 - i32.ne - if - i32.const 0 - i32.const 1120 - i32.const 159 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 20 - i32.add - call $~lib/rt/__visit_members - local.get $0 - i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - br $while-continue|0 - end - end - ) - (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) + (export "condiNarrow" (func $export:typenarrow/condiNarrow)) + (func $typenarrow/condiNarrow (param $0 i32) (param $1 i32) (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - i32.load - local.tee $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 268 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const -4 - i32.and - local.tee $2 - i32.const 12 - i32.lt_u - if - i32.const 0 - i32.const 1392 - i32.const 270 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 256 - i32.lt_u - if (result i32) - local.get $2 - i32.const 4 - i32.shr_u - else - i32.const 31 - local.get $2 - i32.const 1073741820 - local.get $2 - i32.const 1073741820 - i32.lt_u - select - local.tee $2 - i32.clz - i32.sub - local.tee $4 - i32.const 7 - i32.sub - local.set $3 - local.get $2 - local.get $4 - i32.const 4 - i32.sub - i32.shr_u - i32.const 16 - i32.xor - end - local.tee $2 - i32.const 16 - i32.lt_u - local.get $3 - i32.const 23 - i32.lt_u - i32.and - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 284 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load offset=8 - local.set $5 - local.get $1 - i32.load offset=4 - local.tee $4 - if - local.get $4 - local.get $5 - i32.store offset=8 - end - local.get $5 - if - local.get $5 - local.get $4 - i32.store offset=4 - end - local.get $1 local.get $0 - local.get $3 - i32.const 4 - i32.shl - local.get $2 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - i32.eq if local.get $0 - local.get $3 - i32.const 4 - i32.shl - local.get $2 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $5 - i32.store offset=96 - local.get $5 - i32.eqz + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u if - local.get $0 - local.get $3 - i32.const 2 - i32.shl - i32.add - local.tee $1 - i32.load offset=4 - i32.const -2 - local.get $2 - i32.rotl - i32.and - local.set $2 - local.get $1 - local.get $2 - i32.store offset=4 - local.get $2 - i32.eqz - if - local.get $0 - local.get $0 - i32.load - i32.const -2 - local.get $3 - i32.rotl - i32.and - i32.store + loop $do-loop|0 + local.get $2 + i32.const 4 + i32.ne + if + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|0 + end end end end - ) - (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - local.get $1 - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 201 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load - local.tee $3 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 203 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $4 - i32.load - local.tee $2 - i32.const 1 - i32.and + local.get $0 if local.get $0 - local.get $4 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $3 - i32.const 4 - i32.add - local.get $2 - i32.const -4 - i32.and - i32.add - local.tee $3 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $4 - i32.load - local.set $2 - end - local.get $3 - i32.const 2 - i32.and - if - local.get $1 - i32.const 4 + i32.const 20 i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 i32.load - local.tee $1 - i32.load - local.tee $6 - i32.const 1 - i32.and - i32.eqz + i32.le_u if - i32.const 0 - i32.const 1392 - i32.const 221 - i32.const 16 - call $~lib/builtins/abort - unreachable + loop $do-loop|02 + local.get $2 + i32.const 4 + i32.ne + if + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|02 + end + end end - local.get $0 - local.get $1 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $6 - i32.const 4 - i32.add - local.get $3 - i32.const -4 - i32.and - i32.add - local.tee $3 - i32.store - end - local.get $4 - local.get $2 - i32.const 2 - i32.or - i32.store - local.get $3 - i32.const -4 - i32.and - local.tee $2 - i32.const 12 - i32.lt_u - if - i32.const 0 - i32.const 1392 - i32.const 233 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $4 - local.get $1 - i32.const 4 - i32.add - local.get $2 - i32.add - i32.ne - if - i32.const 0 - i32.const 1392 - i32.const 234 - i32.const 14 - call $~lib/builtins/abort - unreachable end - local.get $4 - i32.const 4 - i32.sub - local.get $1 - i32.store - local.get $2 - i32.const 256 - i32.lt_u + local.get $0 if (result i32) - local.get $2 - i32.const 4 - i32.shr_u + block $__inlined_func$~lib/rt/__instanceof4 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|06 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof4 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|06 + end + end + i32.const 0 + end else - i32.const 31 - local.get $2 - i32.const 1073741820 - local.get $2 - i32.const 1073741820 - i32.lt_u - select - local.tee $2 - i32.clz - i32.sub - local.tee $3 - i32.const 7 - i32.sub - local.set $5 - local.get $2 - local.get $3 - i32.const 4 - i32.sub - i32.shr_u - i32.const 16 - i32.xor - end - local.tee $2 - i32.const 16 - i32.lt_u - local.get $5 - i32.const 23 - i32.lt_u - i32.and - i32.eqz - if i32.const 0 - i32.const 1392 - i32.const 251 - i32.const 14 - call $~lib/builtins/abort - unreachable end - local.get $0 - local.get $5 - i32.const 4 - i32.shl - local.get $2 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $3 - local.get $1 i32.const 0 - i32.store offset=4 local.get $1 - local.get $3 - i32.store offset=8 - local.get $3 + select if - local.get $3 - local.get $1 - i32.store offset=4 - end - local.get $0 - local.get $5 - i32.const 4 - i32.shl - local.get $2 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $1 - i32.store offset=96 - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $5 - i32.shl - i32.or - i32.store - local.get $0 - local.get $5 - i32.const 2 - i32.shl - i32.add - local.tee $0 - local.get $0 - i32.load offset=4 - i32.const 1 - local.get $2 - i32.shl - i32.or - i32.store offset=4 - ) - (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - local.get $1 - local.get $2 - i32.gt_u - if - i32.const 0 - i32.const 1392 - i32.const 377 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 19 - i32.add - i32.const -16 - i32.and - i32.const 4 - i32.sub - local.set $1 - local.get $0 - i32.load offset=1568 - local.tee $4 - if - local.get $4 - i32.const 4 - i32.add - local.get $1 - i32.gt_u - if - i32.const 0 - i32.const 1392 - i32.const 384 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 16 - i32.sub - local.get $4 - i32.eq - if - local.get $4 - i32.load - local.set $3 + block $__inlined_func$~lib/rt/__instanceof7 (result i32) local.get $1 - i32.const 16 + i32.const 20 i32.sub - local.set $1 - end - else - local.get $0 - i32.const 1572 - i32.add - local.get $1 - i32.gt_u - if + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|09 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof7 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|09 + end + end i32.const 0 - i32.const 1392 - i32.const 397 - i32.const 5 - call $~lib/builtins/abort - unreachable end + drop end - local.get $2 - i32.const -16 - i32.and - local.get $1 - i32.sub - local.tee $2 - i32.const 20 - i32.lt_u - if - return - end - local.get $1 - local.get $3 - i32.const 2 - i32.and - local.get $2 - i32.const 8 - i32.sub - local.tee $2 - i32.const 1 - i32.or - i32.or - i32.store - local.get $1 - i32.const 0 - i32.store offset=4 - local.get $1 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 4 - i32.add - local.get $2 - i32.add - local.tee $2 - i32.const 2 - i32.store local.get $0 - local.get $2 - i32.store offset=1568 - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - ) - (func $~lib/rt/tlsf/initialize - (local $0 i32) - (local $1 i32) - memory.size - local.tee $1 - i32.const 0 - i32.le_s if (result i32) - i32.const 1 - local.get $1 - i32.sub - memory.grow - i32.const 0 - i32.lt_s - else - i32.const 0 - end - if - unreachable - end - i32.const 17920 - i32.const 0 - i32.store - i32.const 19488 - i32.const 0 - i32.store - loop $for-loop|0 - local.get $0 - i32.const 23 - i32.lt_u - if + block $__inlined_func$~lib/rt/__instanceof12 (result i32) local.get $0 - i32.const 2 - i32.shl - i32.const 17920 - i32.add - i32.const 0 - i32.store offset=4 - i32.const 0 - local.set $1 - loop $for-loop|1 - local.get $1 - i32.const 16 - i32.lt_u - if - local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|014 + i32.const 1 + local.get $2 i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof12 + drop + local.get $2 + i32.const 3 i32.shl - local.get $1 - i32.add - i32.const 2 - i32.shl - i32.const 17920 - i32.add - i32.const 0 - i32.store offset=96 - local.get $1 - i32.const 1 + i32.const 1076 i32.add - local.set $1 - br $for-loop|1 + i32.load offset=4 + local.tee $2 + br_if $do-loop|014 end end - local.get $0 - i32.const 1 - i32.add - local.set $0 - br $for-loop|0 + i32.const 0 end + else + i32.const 0 end - i32.const 17920 - i32.const 19492 - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - i32.const 17920 - global.set $~lib/rt/tlsf/ROOT - ) - (func $~lib/rt/itcms/step (result i32) - (local $0 i32) - (local $1 i32) - (local $2 i32) - block $break|0 - block $case2|0 - block $case1|0 - block $case0|0 - global.get $~lib/rt/itcms/state - br_table $case0|0 $case1|0 $case2|0 $break|0 - end - i32.const 1 - global.set $~lib/rt/itcms/state - i32.const 0 - global.set $~lib/rt/itcms/visitCount - call $~lib/rt/itcms/visitRoots - global.get $~lib/rt/itcms/toSpace - global.set $~lib/rt/itcms/iter - global.get $~lib/rt/itcms/visitCount - return - end - global.get $~lib/rt/itcms/white - i32.eqz - local.set $1 - global.get $~lib/rt/itcms/iter - i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - loop $while-continue|1 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - if - local.get $0 - global.set $~lib/rt/itcms/iter - local.get $1 - local.get $0 - i32.load offset=4 + i32.const 0 + local.get $1 + select + if + block $__inlined_func$~lib/rt/__instanceof15 (result i32) + local.get $1 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|017 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof15 + drop + local.get $2 i32.const 3 - i32.and - i32.ne - if - local.get $0 - local.get $0 - i32.load offset=4 - i32.const -4 - i32.and - local.get $1 - i32.or - i32.store offset=4 - i32.const 0 - global.set $~lib/rt/itcms/visitCount - local.get $0 - i32.const 20 - i32.add - call $~lib/rt/__visit_members - global.get $~lib/rt/itcms/visitCount - return - end - local.get $0 + i32.shl + i32.const 1076 + i32.add i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - br $while-continue|1 + local.tee $2 + br_if $do-loop|017 end end i32.const 0 - global.set $~lib/rt/itcms/visitCount - call $~lib/rt/itcms/visitRoots - global.get $~lib/rt/itcms/toSpace - global.get $~lib/rt/itcms/iter - i32.load offset=4 - i32.const -4 - i32.and - i32.eq + end + drop + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof19 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u if - global.get $~lib/memory/__stack_pointer - local.set $0 - loop $while-continue|0 - local.get $0 - i32.const 17916 - i32.lt_u - if - local.get $0 - i32.load - local.tee $2 - if - local.get $2 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - local.get $0 - i32.const 4 - i32.add - local.set $0 - br $while-continue|0 - end - end - global.get $~lib/rt/itcms/iter - i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - loop $while-continue|2 - local.get $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - if - local.get $1 - local.get $0 - i32.load offset=4 - i32.const 3 - i32.and - i32.ne - if - local.get $0 - local.get $0 - i32.load offset=4 - i32.const -4 - i32.and - local.get $1 - i32.or - i32.store offset=4 - local.get $0 - i32.const 20 - i32.add - call $~lib/rt/__visit_members - end - local.get $0 - i32.load offset=4 - i32.const -4 - i32.and - local.set $0 - br $while-continue|2 - end + loop $do-loop|021 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof19 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|021 end - global.get $~lib/rt/itcms/fromSpace - local.set $0 - global.get $~lib/rt/itcms/toSpace - global.set $~lib/rt/itcms/fromSpace - local.get $0 - global.set $~lib/rt/itcms/toSpace - local.get $1 - global.set $~lib/rt/itcms/white - local.get $0 - i32.load offset=4 - i32.const -4 - i32.and - global.set $~lib/rt/itcms/iter - i32.const 2 - global.set $~lib/rt/itcms/state end - global.get $~lib/rt/itcms/visitCount - return + i32.const 0 end - global.get $~lib/rt/itcms/iter - local.tee $0 - global.get $~lib/rt/itcms/toSpace - i32.ne - if - local.get $0 - i32.load offset=4 - local.tee $1 - i32.const -4 - i32.and - global.set $~lib/rt/itcms/iter - global.get $~lib/rt/itcms/white - i32.eqz + else + i32.const 0 + end + i32.const 1 + local.get $1 + select + i32.eqz + if + block $__inlined_func$~lib/rt/__instanceof22 (result i32) local.get $1 - i32.const 3 - i32.and - i32.ne - if - i32.const 0 - i32.const 1120 - i32.const 228 - i32.const 20 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 17916 - i32.lt_u + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u if - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store offset=8 - else - global.get $~lib/rt/itcms/total - local.get $0 - i32.load - i32.const -4 - i32.and - i32.const 4 - i32.add - i32.sub - global.set $~lib/rt/itcms/total - local.get $0 - i32.const 4 - i32.add - local.tee $0 - i32.const 17916 - i32.ge_u - if - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.get $0 - i32.const 4 - i32.sub - local.set $2 - local.get $0 - i32.const 15 - i32.and + loop $do-loop|024 i32.const 1 - local.get $0 - select - if (result i32) - i32.const 1 - else - local.get $2 - i32.load - i32.const 1 - i32.and - end - if - i32.const 0 - i32.const 1392 - i32.const 559 - i32.const 3 - call $~lib/builtins/abort - unreachable - end local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof22 + drop local.get $2 - i32.load + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|024 + end + end + i32.const 0 + end + drop + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof26 (result i32) + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|028 i32.const 1 - i32.or - i32.store local.get $2 - call $~lib/rt/tlsf/insertBlock + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof26 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|028 end end - i32.const 10 - return + i32.const 0 end - global.get $~lib/rt/itcms/toSpace - local.tee $0 - local.get $0 - i32.store offset=4 - local.get $0 - local.get $0 - i32.store offset=8 + else i32.const 0 - global.set $~lib/rt/itcms/state end i32.const 0 - ) - (func $~lib/rt/tlsf/searchBlock (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - local.get $0 - i32.load offset=4 - i32.const -2 - i32.and - local.tee $1 - if (result i32) - local.get $0 - local.get $1 - i32.ctz - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - else - local.get $0 - i32.load - i32.const -2 - i32.and - local.tee $1 - if (result i32) - local.get $0 + local.get $1 + select + if + block $__inlined_func$~lib/rt/__instanceof29 (result i32) local.get $1 - i32.ctz + i32.const 20 + i32.sub + i32.load offset=12 local.tee $2 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.tee $1 - i32.eqz + i32.const 1072 + i32.load + i32.le_u if - i32.const 0 - i32.const 1392 - i32.const 343 - i32.const 18 - call $~lib/builtins/abort - unreachable + loop $do-loop|031 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof29 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|031 + end end - local.get $0 - local.get $1 - i32.ctz - local.get $2 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - else i32.const 0 end + drop end - ) - (func $typenarrow/testlocal - (local $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - block $folding-inner0 - global.get $~lib/memory/__stack_pointer - i32.const 1532 - i32.lt_s - br_if $folding-inner0 - global.get $~lib/memory/__stack_pointer - local.tee $3 - i32.const 0 - i32.store - local.get $3 - i32.const 4 - i32.sub - global.set $~lib/memory/__stack_pointer - global.get $~lib/memory/__stack_pointer - i32.const 1532 - i32.lt_s - br_if $folding-inner0 - global.get $~lib/memory/__stack_pointer - local.tee $2 - i32.const 0 - i32.store - global.get $~lib/rt/itcms/total - global.get $~lib/rt/itcms/threshold - i32.ge_u - if - block $__inlined_func$~lib/rt/itcms/interrupt - i32.const 2048 - local.set $0 - loop $do-loop|0 - local.get $0 - call $~lib/rt/itcms/step - i32.sub - local.set $0 - global.get $~lib/rt/itcms/state - i32.eqz - if - global.get $~lib/rt/itcms/total - i64.extend_i32_u - i64.const 200 - i64.mul - i64.const 100 - i64.div_u - i32.wrap_i64 - i32.const 1024 - i32.add - global.set $~lib/rt/itcms/threshold - br $__inlined_func$~lib/rt/itcms/interrupt - end - local.get $0 - i32.const 0 - i32.gt_s - br_if $do-loop|0 - end - global.get $~lib/rt/itcms/total - local.tee $0 - global.get $~lib/rt/itcms/threshold - i32.sub - i32.const 1024 - i32.lt_u - i32.const 10 - i32.shl - local.get $0 - i32.add - global.set $~lib/rt/itcms/threshold - end - end - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.tee $4 - call $~lib/rt/tlsf/searchBlock - local.tee $0 - i32.eqz - if - memory.size - local.tee $0 - i32.const 4 - local.get $4 - i32.load offset=1568 + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof32 (result i32) local.get $0 - i32.const 16 - i32.shl - i32.const 4 + i32.const 20 i32.sub - i32.ne - i32.shl - i32.const 65563 - i32.add - i32.const -65536 - i32.and - i32.const 16 - i32.shr_u - local.tee $1 - local.get $0 - local.get $1 - i32.gt_s - select - memory.grow - i32.const 0 - i32.lt_s + i32.load offset=12 + local.tee $2 + i32.const 1072 + i32.load + i32.le_u if - local.get $1 - memory.grow - i32.const 0 - i32.lt_s - if - unreachable + loop $do-loop|034 + i32.const 1 + local.get $2 + i32.const 4 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof32 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|034 end end - local.get $4 - local.get $0 - i32.const 16 - i32.shl - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - local.get $4 - call $~lib/rt/tlsf/searchBlock - local.tee $0 - i32.eqz - if - i32.const 0 - i32.const 1392 - i32.const 496 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - end - local.get $0 - i32.load - i32.const -4 - i32.and - i32.const 28 - i32.lt_u - if i32.const 0 - i32.const 1392 - i32.const 498 - i32.const 14 - call $~lib/builtins/abort - unreachable end - local.get $4 - local.get $0 - call $~lib/rt/tlsf/removeBlock - local.get $0 - i32.load - local.tee $5 - i32.const -4 - i32.and - i32.const 28 - i32.sub - local.tee $1 - i32.const 16 - i32.ge_u - if - local.get $0 - local.get $5 - i32.const 2 - i32.and - i32.const 28 - i32.or - i32.store - local.get $0 - i32.const 32 - i32.add - local.tee $5 - local.get $1 - i32.const 4 - i32.sub - i32.const 1 - i32.or - i32.store - local.get $4 - local.get $5 - call $~lib/rt/tlsf/insertBlock - else - local.get $0 - local.get $5 - i32.const -2 - i32.and - i32.store - local.get $0 - i32.const 4 - i32.add - local.get $0 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $1 - local.get $1 - i32.load - i32.const -3 - i32.and - i32.store - end - local.get $0 - i32.const 3 - i32.store offset=12 - local.get $0 - i32.const 0 - i32.store offset=16 - global.get $~lib/rt/itcms/fromSpace - local.tee $1 - i32.load offset=8 - local.set $4 - local.get $0 - global.get $~lib/rt/itcms/white - local.get $1 - i32.or - i32.store offset=4 - local.get $0 - local.get $4 - i32.store offset=8 - local.get $4 - local.get $0 - local.get $4 - i32.load offset=4 - i32.const 3 - i32.and - i32.or - i32.store offset=4 - local.get $1 - local.get $0 - i32.store offset=8 - global.get $~lib/rt/itcms/total - local.get $0 - i32.load - i32.const -4 - i32.and - i32.const 4 - i32.add - i32.add - global.set $~lib/rt/itcms/total - local.get $0 - i32.const 20 - i32.add - local.tee $1 - i32.const 0 + else i32.const 0 - memory.fill - local.get $2 - local.get $1 - i32.store - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - local.get $3 - local.get $1 - i32.store - local.get $1 - if + end + i32.const 1 + local.get $1 + select + i32.eqz + if + block $__inlined_func$~lib/rt/__instanceof35 (result i32) local.get $1 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $2 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|02 - local.get $0 + loop $do-loop|037 + i32.const 1 + local.get $2 i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|02 - end + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof35 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|037 end end + i32.const 0 end - local.get $1 - if - local.get $1 + drop + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof38 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $2 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|024 - local.get $0 + loop $do-loop|040 + i32.const 1 + local.get $2 i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|024 - end + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof38 + drop + local.get $2 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $2 + br_if $do-loop|040 end end + i32.const 0 end - local.get $1 - if + else + i32.const 0 + end + i32.const 0 + local.get $1 + select + if + block $__inlined_func$~lib/rt/__instanceof41 (result i32) local.get $1 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|06 - local.get $0 + loop $do-loop|043 + i32.const 1 + local.get $1 i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|06 - end + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof41 + drop + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|043 end end + i32.const 0 end - local.get $1 + drop + end + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $1 + i32.const 1072 + i32.load + i32.le_u if - local.get $1 + loop $do-loop|046 + local.get $1 + i32.const 4 + i32.ne + if + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|046 + end + end + end + end + local.get $0 + if + local.get $0 + i32.const 20 + i32.sub + i32.load offset=12 + local.tee $1 + i32.const 1072 + i32.load + i32.le_u + if + loop $do-loop|049 + local.get $1 + i32.const 4 + i32.ne + if + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|049 + end + end + end + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof51 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|010 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|010 - end + loop $do-loop|053 + i32.const 1 + local.get $1 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof51 + drop + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|053 end end + i32.const 0 end - local.get $1 - if - local.get $1 + else + i32.const 0 + end + if + local.get $0 + i32.load + drop + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof55 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|017 - local.get $0 + loop $do-loop|057 + i32.const 1 + local.get $1 i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|017 - end + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof55 + drop + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|057 end end + i32.const 0 end - local.get $1 - if - local.get $1 + else + i32.const 0 + end + i32.const 1 + local.get $0 + select + i32.eqz + if + block $__inlined_func$~lib/rt/__instanceof58 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|020 - local.get $0 - i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|020 - end + loop $do-loop|060 + i32.const 1 + local.get $1 + i32.const 6 + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof58 + drop + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|060 end end + i32.const 0 end - local.get $1 - if - local.get $1 + drop + end + local.get $0 + if (result i32) + block $__inlined_func$~lib/rt/__instanceof62 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|023 - local.get $0 + loop $do-loop|064 + i32.const 1 + local.get $1 i32.const 4 - i32.ne - if - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|023 - end - end - end - end - local.get $1 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof25 (result i32) - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|027 - i32.const 1 - local.get $0 - i32.const 6 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof25 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|027 - end + i32.eq + br_if $__inlined_func$~lib/rt/__instanceof62 + drop + local.get $1 + i32.const 3 + i32.shl + i32.const 1076 + i32.add + i32.load offset=4 + local.tee $1 + br_if $do-loop|064 end - i32.const 0 end - else i32.const 0 end - if - local.get $1 - i32.load - drop - end - local.get $1 + else + i32.const 0 + end + if (result i32) + local.get $0 if (result i32) - block $__inlined_func$~lib/rt/__instanceof29 (result i32) - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|031 - i32.const 1 - local.get $0 - i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof29 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|031 - end - end - i32.const 0 - end - else - i32.const 0 - end - i32.const 1 - local.get $1 - select - i32.eqz - if - block $__inlined_func$~lib/rt/__instanceof32 (result i32) - local.get $1 + block $__inlined_func$~lib/rt/__instanceof65 (result i32) + local.get $0 i32.const 20 i32.sub i32.load offset=12 - local.tee $0 - i32.const 1472 + local.tee $1 + i32.const 1072 i32.load i32.le_u if - loop $do-loop|034 + loop $do-loop|067 i32.const 1 - local.get $0 + local.get $1 i32.const 6 i32.eq - br_if $__inlined_func$~lib/rt/__instanceof32 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|034 - end - end - i32.const 0 - end - drop - end - local.get $1 - if (result i32) - block $__inlined_func$~lib/rt/__instanceof36 (result i32) - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|038 - i32.const 1 - local.get $0 - i32.const 4 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof36 + br_if $__inlined_func$~lib/rt/__instanceof65 drop - local.get $0 + local.get $1 i32.const 3 i32.shl - i32.const 1476 + i32.const 1076 i32.add i32.load offset=4 - local.tee $0 - br_if $do-loop|038 + local.tee $1 + br_if $do-loop|067 end end i32.const 0 @@ -1601,280 +719,45 @@ else i32.const 0 end + else i32.const 0 - local.get $1 - select - if - block $__inlined_func$~lib/rt/__instanceof39 (result i32) - local.get $1 - i32.const 20 - i32.sub - i32.load offset=12 - local.tee $0 - i32.const 1472 - i32.load - i32.le_u - if - loop $do-loop|041 - i32.const 1 - local.get $0 - i32.const 6 - i32.eq - br_if $__inlined_func$~lib/rt/__instanceof39 - drop - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load offset=4 - local.tee $0 - br_if $do-loop|041 - end - end - i32.const 0 - end - drop - end - global.get $~lib/memory/__stack_pointer - i32.const 4 - i32.add - global.set $~lib/memory/__stack_pointer - return end - i32.const 17936 - i32.const 17984 - i32.const 1 - i32.const 1 - call $~lib/builtins/abort - unreachable - ) - (func $~lib/rt/__visit_members (param $0 i32) - block $invalid - block $typenarrow/C - block $~lib/function/Function<%28this:typenarrow/B%29=>void> - block $typenarrow/B - block $typenarrow/A - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $typenarrow/A $typenarrow/B $~lib/function/Function<%28this:typenarrow/B%29=>void> $typenarrow/C $invalid - end - return - end - return - end - local.get $0 - i32.load - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - return - end - return - end - return - end - local.get $0 - i32.load offset=4 - local.tee $0 - if - local.get $0 - call $byn-split-outlined-A$~lib/rt/itcms/__visit - end - return - end - return + if + local.get $0 + i32.load + drop end - unreachable - ) - (func $~start - memory.size - i32.const 16 - i32.shl - i32.const 17916 - i32.sub - i32.const 1 - i32.shr_u - global.set $~lib/rt/itcms/threshold - i32.const 1172 - i32.const 1168 - i32.store - i32.const 1176 - i32.const 1168 - i32.store - i32.const 1168 - global.set $~lib/rt/itcms/pinSpace - i32.const 1204 - i32.const 1200 - i32.store - i32.const 1208 - i32.const 1200 - i32.store - i32.const 1200 - global.set $~lib/rt/itcms/toSpace - i32.const 1348 - i32.const 1344 - i32.store - i32.const 1352 - i32.const 1344 - i32.store - i32.const 1344 - global.set $~lib/rt/itcms/fromSpace ) - (func $byn-split-outlined-A$~lib/rt/itcms/__visit (param $0 i32) - (local $1 i32) + (func $export:typenarrow/condiNarrow (param $0 i32) (param $1 i32) (local $2 i32) - (local $3 i32) - global.get $~lib/rt/itcms/white - local.get $0 - i32.const 20 + global.get $~lib/memory/__stack_pointer + i32.const 8 i32.sub - local.tee $1 - i32.load offset=4 - i32.const 3 - i32.and - i32.eq + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1132 + i32.lt_s if - local.get $1 - global.get $~lib/rt/itcms/iter - i32.eq - if - local.get $1 - i32.load offset=8 - local.tee $0 - i32.eqz - if - i32.const 0 - i32.const 1120 - i32.const 147 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - local.get $0 - global.set $~lib/rt/itcms/iter - end - block $__inlined_func$~lib/rt/itcms/Object#unlink - local.get $1 - i32.load offset=4 - i32.const -4 - i32.and - local.tee $0 - i32.eqz - if - i32.const 0 - local.get $1 - i32.const 17916 - i32.lt_u - local.get $1 - i32.load offset=8 - select - i32.eqz - if - i32.const 0 - i32.const 1120 - i32.const 127 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - br $__inlined_func$~lib/rt/itcms/Object#unlink - end - local.get $1 - i32.load offset=8 - local.tee $2 - i32.eqz - if - i32.const 0 - i32.const 1120 - i32.const 131 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $2 - i32.store offset=8 - local.get $2 - local.get $0 - local.get $2 - i32.load offset=4 - i32.const 3 - i32.and - i32.or - i32.store offset=4 - end - global.get $~lib/rt/itcms/toSpace - local.set $2 - local.get $1 - i32.load offset=12 - local.tee $0 + i32.const 17536 + i32.const 17584 i32.const 1 - i32.le_u - if (result i32) - i32.const 1 - else - local.get $0 - i32.const 1472 - i32.load - i32.gt_u - if - i32.const 1248 - i32.const 1312 - i32.const 22 - i32.const 28 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 3 - i32.shl - i32.const 1476 - i32.add - i32.load - i32.const 32 - i32.and - end - if (result i32) - global.get $~lib/rt/itcms/white - i32.eqz - else - i32.const 2 - end - local.set $3 - local.get $2 - i32.load offset=8 - local.set $0 - local.get $1 - local.get $2 - local.get $3 - i32.or - i32.store offset=4 - local.get $1 - local.get $0 - i32.store offset=8 - local.get $0 - local.get $1 - local.get $0 - i32.load offset=4 - i32.const 3 - i32.and - i32.or - i32.store offset=4 - local.get $2 - local.get $1 - i32.store offset=8 - global.get $~lib/rt/itcms/visitCount i32.const 1 - i32.add - global.set $~lib/rt/itcms/visitCount + call $~lib/builtins/abort + unreachable end + global.get $~lib/memory/__stack_pointer + local.tee $2 + local.get $0 + i32.store + local.get $2 + local.get $1 + i32.store offset=4 + local.get $0 + local.get $1 + call $typenarrow/condiNarrow + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer ) ) diff --git a/tests/compiler/typenarrow.ts b/tests/compiler/typenarrow.ts index 8be47f4567..145d0f356c 100644 --- a/tests/compiler/typenarrow.ts +++ b/tests/compiler/typenarrow.ts @@ -9,65 +9,68 @@ class B extends A { class C extends B { c1: i32; } +class D extends A { + d1: i32; +} -export function testlocal(): void { - let value = new A(); - let condi = true; - +export function condiNarrow(v0: A, v1: A): void { // noraml - if (value instanceof B) { - value.b1(); - let t: B = value; + if (v0 instanceof B) { + v0.b1(); + let t: B = v0; } // not - if (!(value instanceof B)) { + if (!(v0 instanceof B)) { } else { - value.b1(); - let t: B = value; + v0.b1(); + let t: B = v0; } // and - if (value instanceof B && condi) { - value.b1(); - let t: B = value; + if (v0 instanceof B && v1 instanceof B) { + v0.b1(); + v1.b1(); } - if (condi && value instanceof B) { - value.b1(); - let t: B = value; + if (v0 instanceof B && !(v1 instanceof B)) { + v0.b1(); + } + if (!(v0 instanceof B) && v1 instanceof B) { + v1.b1(); } // or - if (condi || !(value instanceof B)) { + if (!(v0 instanceof B) || !(v1 instanceof B)) { } else { - value.b1; - let t: B = value; + v0.b1; + v1.b1; } - - if (!(value instanceof B) || condi) { + if (v0 instanceof B || !(v1 instanceof B)) { } else { - value.b1; - let t: B = value; + v1.b1; + } + if (!(v0 instanceof B) || v1 instanceof B) { + } else { + v0.b1; } // in condition check for logic operator - if (value instanceof B && value.check()) { + if (v0 instanceof B && v0.check()) { } - if (!(value instanceof B) || value.check()) { + if (!(v0 instanceof B) || v0.check()) { } // compatibiltiy - if (value instanceof C && value instanceof B) { - value.c1; - value.b1(); + if (v0 instanceof C && v0 instanceof B) { + v0.c1; + v0.b1(); } - - if (value instanceof B || value instanceof C) { - value.b1(); + if (v0 instanceof B || v0 instanceof C) { + v0.b1(); } - - if (!(value instanceof B && value instanceof C)) { + if (!(v0 instanceof B && v0 instanceof C)) { } else { - value.b1(); + v0.b1(); + v0.c1; } } From 265eff183c1b6417a125d2464f74c58e54b59d51 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Fri, 5 Aug 2022 11:03:24 +0800 Subject: [PATCH 45/55] fix logic or narrow bugs --- src/flow.ts | 18 ++++++------------ src/narrow.ts | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index a13119df5b..e74d9042f0 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -656,21 +656,15 @@ export class Flow { /** take effect conditional type narrow if condition is true */ inheritNarrowedTypeIfTrue(condi: ExpressionRef): void { - let condiNarrow = this.typeNarrowChecker.collectNarrowedTypeIfTrue(condi, this); - if (condiNarrow.size != 0) { - let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); - this.narrowedTypes = narrowedTypes; - narrowedTypes.mergeOr(condiNarrow); - } + let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; + this.typeNarrowChecker.collectNarrowedTypeIfTrue(condi, this, narrowedTypes); } /** take effect conditional type narrow if condition is false */ inheritNarrowedTypeIfFalse(condi: ExpressionRef): void { - let condiNarrow = this.typeNarrowChecker.collectNarrowedTypeIfFalse(condi, this); - if (condiNarrow.size != 0) { - let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); - this.narrowedTypes = narrowedTypes; - narrowedTypes.mergeOr(condiNarrow); - } + let narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); + this.narrowedTypes = narrowedTypes; + this.typeNarrowChecker.collectNarrowedTypeIfFalse(condi, this, narrowedTypes); } /** Initializes `this` field flags. */ diff --git a/src/narrow.ts b/src/narrow.ts index 4763287099..734a2d437e 100644 --- a/src/narrow.ts +++ b/src/narrow.ts @@ -234,10 +234,10 @@ export class TypeNarrowChecker { if (ifFalse && isConstNonZero(ifTrue)) { // Logical OR: (if (condition 1 ifFalse)) // the only way this had become false is if condition and ifFalse are false - let subMapTrue = this.collectNarrowedTypeIfTrue(condition, flow, typeMap.clone()); - let subMapFalse = this.collectNarrowedTypeIfTrue(ifFalse, flow, typeMap.clone()); - subMapTrue.mergeAnd(subMapFalse); - typeMap.mergeOr(subMapTrue); + let subMapFalse = typeMap.clone(); + this.collectNarrowedTypeIfTrue(condition, flow, typeMap); + this.collectNarrowedTypeIfTrue(ifFalse, flow, subMapFalse); + typeMap.mergeAnd(subMapFalse); } break; } @@ -318,7 +318,7 @@ export class TypeNarrowChecker { typeMap.mergeElementOr(narrowedTypeElement); } const assignMap = this.assignMap; - if (assignMap.has(expr)) { + if (assignMap.has(expr)) { const assignTypeElement = assert(assignMap.get(expr)); typeMap.set(assignTypeElement.element, assignTypeElement.type); } @@ -337,7 +337,7 @@ export class TypeNarrowChecker { typeMap.setNonnull(local); } } - + return typeMap; } @@ -424,6 +424,12 @@ export class TypeNarrowChecker { break; } } + // update expr + const assignMap = this.assignMap; + if (assignMap.has(expr)) { + const assignTypeElement = assert(assignMap.get(expr)); + typeMap.set(assignTypeElement.element, assignTypeElement.type); + } return typeMap; } } From e2256c8ca4de6adbdfd59da034b0265ee019e8b3 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 6 Aug 2022 00:12:05 +0800 Subject: [PATCH 46/55] remove `||` assert --- src/compiler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 5e57878f22..695cc204e4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -211,7 +211,6 @@ import { import { ShadowStackPass } from "./passes/shadowstack"; - import { typeOr } from "./narrow"; /** Compiler options. */ @@ -4670,7 +4669,7 @@ export class Compiler extends DiagnosticEmitter { ); flow.freeTempLocal(temp); } - this.currentType = assert(typeOr(leftType.nonNullableType, rightType)); + this.currentType = typeOr(leftType.nonNullableType, rightType) || rightType; } break; } From 9af606459aece64dc7c5328b3e99dcf28aad437d Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 6 Aug 2022 00:14:22 +0800 Subject: [PATCH 47/55] fix: remove default initialized typeNarrowChecker --- src/flow.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index e74d9042f0..17372e2821 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -198,7 +198,7 @@ export class Flow { /** Creates the parent flow of the specified function. */ static createParent(parentFunction: Function): Flow { - var flow = new Flow(parentFunction); + var flow = new Flow(parentFunction, null); if (parentFunction.is(CommonFlags.CONSTRUCTOR)) { flow.initThisFieldFlags(); } @@ -207,7 +207,7 @@ export class Flow { /** Creates an inline flow within `parentFunction`. */ static createInline(parentFunction: Function, inlineFunction: Function): Flow { - var flow = new Flow(parentFunction); + var flow = new Flow(parentFunction, null); flow.inlineFunction = inlineFunction; flow.inlineReturnLabel = `${inlineFunction.internalName}|inlined.${(inlineFunction.nextInlineId++)}`; if (inlineFunction.is(CommonFlags.CONSTRUCTOR)) { @@ -218,9 +218,10 @@ export class Flow { private constructor( /** Function this flow belongs to. */ - public parentFunction: Function + public parentFunction: Function, + typeNarrowChecker: TypeNarrowChecker | null ) { - /* nop */ + this.typeNarrowChecker = typeNarrowChecker || new TypeNarrowChecker(); } /** Parent flow. */ @@ -242,7 +243,7 @@ export class Flow { /** type narrow */ narrowedTypes: NarrowedTypeMap | null = null; /** handle conditional type narrow, eg if (condi) */ - typeNarrowChecker: TypeNarrowChecker = new TypeNarrowChecker(); + typeNarrowChecker: TypeNarrowChecker; /** Function being inlined, when inlining. */ inlineFunction: Function | null = null; @@ -302,7 +303,7 @@ export class Flow { /** Forks this flow to a child flow. */ fork(resetBreakContext: bool = false): Flow { - var branch = new Flow(this.parentFunction); + var branch = new Flow(this.parentFunction, this.typeNarrowChecker); branch.parent = this; branch.outer = this.outer; if (resetBreakContext) { @@ -320,7 +321,6 @@ export class Flow { branch.localFlags = this.localFlags.slice(); let narrowedTypes = this.narrowedTypes; branch.narrowedTypes = narrowedTypes ? narrowedTypes.clone() : null; - branch.typeNarrowChecker = this.typeNarrowChecker; if (this.actualFunction.is(CommonFlags.CONSTRUCTOR)) { let thisFieldFlags = assert(this.thisFieldFlags); branch.thisFieldFlags = uniqueMap(thisFieldFlags); From ac12ee8273240237999e34318a34d1913634841b Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 6 Aug 2022 00:23:02 +0800 Subject: [PATCH 48/55] fix --- src/flow.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 17372e2821..af3fc254b6 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -198,7 +198,7 @@ export class Flow { /** Creates the parent flow of the specified function. */ static createParent(parentFunction: Function): Flow { - var flow = new Flow(parentFunction, null); + var flow = new Flow(parentFunction); if (parentFunction.is(CommonFlags.CONSTRUCTOR)) { flow.initThisFieldFlags(); } @@ -207,7 +207,7 @@ export class Flow { /** Creates an inline flow within `parentFunction`. */ static createInline(parentFunction: Function, inlineFunction: Function): Flow { - var flow = new Flow(parentFunction, null); + var flow = new Flow(parentFunction); flow.inlineFunction = inlineFunction; flow.inlineReturnLabel = `${inlineFunction.internalName}|inlined.${(inlineFunction.nextInlineId++)}`; if (inlineFunction.is(CommonFlags.CONSTRUCTOR)) { @@ -219,9 +219,9 @@ export class Flow { private constructor( /** Function this flow belongs to. */ public parentFunction: Function, - typeNarrowChecker: TypeNarrowChecker | null + typeNarrowChecker: TypeNarrowChecker = new TypeNarrowChecker() ) { - this.typeNarrowChecker = typeNarrowChecker || new TypeNarrowChecker(); + this.typeNarrowChecker = typeNarrowChecker; } /** Parent flow. */ From 0a0135ba8e1512182aad59663152a29d696dd1e9 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 6 Aug 2022 07:46:42 +0800 Subject: [PATCH 49/55] refactory `forkTrueBranch` and `forkFalseBranch` --- src/compiler.ts | 24 ++++++++---------------- src/flow.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 695cc204e4..a1c21dd58f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2555,8 +2555,7 @@ export class Compiler extends DiagnosticEmitter { this.currentFlow = flow; // Compile the body assuming the condition turned out true - var bodyFlow = flow.fork(); - bodyFlow.inheritNarrowedTypeIfTrue(condExpr); + var bodyFlow = flow.forkTrueBranch(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); var body = statement.statement; @@ -2699,9 +2698,8 @@ export class Compiler extends DiagnosticEmitter { // Compile ifTrue assuming the condition turned out true var thenStmts = new Array(); - var thenFlow = flow.fork(); + var thenFlow = flow.forkTrueBranch(condExpr); this.currentFlow = thenFlow; - thenFlow.inheritNarrowedTypeIfTrue(condExpr); if (ifTrue.kind == NodeKind.BLOCK) { this.compileStatements((ifTrue).statements, false, thenStmts); @@ -2718,9 +2716,8 @@ export class Compiler extends DiagnosticEmitter { // Compile ifFalse assuming the condition turned out false, if present if (ifFalse) { let elseStmts = new Array(); - let elseFlow = flow.fork(); + let elseFlow = flow.forkFalseBranch(condExpr); this.currentFlow = elseFlow; - elseFlow.inheritNarrowedTypeIfFalse(condExpr); if (ifFalse.kind == NodeKind.BLOCK) { this.compileStatements((ifFalse).statements, false, elseStmts); } else { @@ -3240,8 +3237,7 @@ export class Compiler extends DiagnosticEmitter { this.currentFlow = flow; // Compile the body assuming the condition turned out true - var bodyFlow = flow.fork(); - bodyFlow.inheritNarrowedTypeIfTrue(condExpr); + var bodyFlow = flow.forkTrueBranch(condExpr); this.currentFlow = bodyFlow; var bodyStmts = new Array(); var body = statement.statement; @@ -4547,9 +4543,8 @@ export class Compiler extends DiagnosticEmitter { leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedConstraints); leftType = this.currentType; - let rightFlow = flow.fork(); + let rightFlow = flow.forkTrueBranch(leftExpr); this.currentFlow = rightFlow; - rightFlow.inheritNarrowedTypeIfTrue(leftExpr); // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { @@ -4613,9 +4608,8 @@ export class Compiler extends DiagnosticEmitter { leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedConstraints); leftType = this.currentType; - let rightFlow = flow.fork(); + let rightFlow = flow.forkFalseBranch(leftExpr); this.currentFlow = rightFlow; - rightFlow.inheritNarrowedTypeIfFalse(leftExpr); // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { @@ -9284,14 +9278,12 @@ export class Compiler extends DiagnosticEmitter { } var outerFlow = this.currentFlow; - var ifThenFlow = outerFlow.fork(); - ifThenFlow.inheritNarrowedTypeIfTrue(condExpr); + var ifThenFlow = outerFlow.forkTrueBranch(condExpr); this.currentFlow = ifThenFlow; var ifThenExpr = this.compileExpression(ifThen, ctxType); var ifThenType = this.currentType; - var ifElseFlow = outerFlow.fork(); - ifElseFlow.inheritNarrowedTypeIfFalse(condExpr); + var ifElseFlow = outerFlow.forkFalseBranch(condExpr); this.currentFlow = ifElseFlow; var ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType); var ifElseType = this.currentType; diff --git a/src/flow.ts b/src/flow.ts index af3fc254b6..aa597537c9 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -332,6 +332,18 @@ export class Flow { return branch; } + /** Fork this flow to a child flow in branch */ + forkTrueBranch(condExpr: ExpressionRef, resetBreakContext: bool = false): Flow { + let branch = this.fork(resetBreakContext); + branch.inheritNarrowedTypeIfTrue(condExpr); + return branch; + } + forkFalseBranch(condExpr: ExpressionRef, resetBreakContext: bool = false): Flow { + let branch = this.fork(resetBreakContext); + branch.inheritNarrowedTypeIfFalse(condExpr); + return branch; + } + /** Gets a free temporary local of the specified type. */ getTempLocal(type: Type, except: BitSet | null = null): Local { var parentFunction = this.parentFunction; From bfc9ed841bc7778b894265c5b245e576ca9c89d5 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 6 Aug 2022 08:23:38 +0800 Subject: [PATCH 50/55] refactory type narrow in `inheritBranch` --- src/compiler.ts | 3 --- src/flow.ts | 10 ++++++---- tests/compiler/while.debug.wat | 8 ++++---- tests/compiler/while.release.wat | 8 ++++---- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index a1c21dd58f..870b14404d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2737,9 +2737,6 @@ export class Compiler extends DiagnosticEmitter { } else { flow.inheritNarrowedTypeIfFalse(condExpr); flow.inheritBranch(thenFlow); - if (thenFlow.isAny(FlowFlags.TERMINATES | FlowFlags.BREAKS)) { - flow.inheritNarrowedTypeIfFalse(condExpr); - } return module.if(condExpr, module.flatten(thenStmts) ); diff --git a/src/flow.ts b/src/flow.ts index aa597537c9..6a5e80c6a7 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -884,10 +884,12 @@ export class Flow { } // narrowed types - let thisNarrowedTypes = this.narrowedTypes; - let otherNarrowedTypes = other.narrowedTypes; - if (thisNarrowedTypes && otherNarrowedTypes) { - thisNarrowedTypes.mergeAnd(otherNarrowedTypes); + if (!other.isAny(FlowFlags.BREAKS | FlowFlags.TERMINATES)) { + let thisNarrowedTypes = this.narrowedTypes; + let otherNarrowedTypes = other.narrowedTypes; + if (thisNarrowedTypes && otherNarrowedTypes) { + thisNarrowedTypes.mergeAnd(otherNarrowedTypes); + } } // field flags do not matter here since there's only INITIALIZED, which can diff --git a/tests/compiler/while.debug.wat b/tests/compiler/while.debug.wat index 73018885bf..408307b988 100644 --- a/tests/compiler/while.debug.wat +++ b/tests/compiler/while.debug.wat @@ -2665,8 +2665,8 @@ call $while/Ref#constructor local.tee $1 i32.store - block $while-break|1 - loop $while-continue|1 + block $while-break|0 + loop $while-continue|0 call $while/getRef local.set $2 local.get $2 @@ -2680,9 +2680,9 @@ if i32.const 0 local.set $1 - br $while-break|1 + br $while-break|0 end - br $while-continue|1 + br $while-continue|0 end end end diff --git a/tests/compiler/while.release.wat b/tests/compiler/while.release.wat index 55ed996a0d..c93449cac0 100644 --- a/tests/compiler/while.release.wat +++ b/tests/compiler/while.release.wat @@ -1380,10 +1380,10 @@ call $while/Ref#constructor local.tee $1 i32.store - loop $while-continue|110 + loop $while-continue|010 call $while/Ref#constructor if - block $while-break|19 + block $while-break|09 local.get $3 i32.const 1 i32.add @@ -1393,9 +1393,9 @@ if i32.const 0 local.set $1 - br $while-break|19 + br $while-break|09 end - br $while-continue|110 + br $while-continue|010 end end end From 85387c8b0fa6be2790ca551aeb7f8e553b9f33da Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 11 Aug 2022 07:20:44 +0800 Subject: [PATCH 51/55] test: add initType testcase --- tests/compiler/possibly-null.debug.wat | 44 ++++++++++++++++++++++ tests/compiler/possibly-null.release.wat | 48 ++++++++++++++++++++++++ tests/compiler/possibly-null.ts | 7 ++++ 3 files changed, 99 insertions(+) diff --git a/tests/compiler/possibly-null.debug.wat b/tests/compiler/possibly-null.debug.wat index de77339aa7..0c2838ef05 100644 --- a/tests/compiler/possibly-null.debug.wat +++ b/tests/compiler/possibly-null.debug.wat @@ -32,6 +32,7 @@ (export "testAssignInCondi" (func $export:possibly-null/testAssignInCondi)) (export "testNeverNull" (func $export:possibly-null/testNeverNull)) (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) + (export "testInit" (func $export:possibly-null/testInit)) (func $possibly-null/testTrue (param $0 i32) local.get $0 if @@ -298,6 +299,33 @@ i32.add global.set $~lib/memory/__stack_pointer ) + (func $possibly-null/testInit (param $0 i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $0 + local.set $1 + i32.const 0 + drop + global.get $~lib/memory/__stack_pointer + local.get $0 + call $possibly-null/requireNonNull + local.tee $2 + i32.store + i32.const 0 + drop + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $~stack_check global.get $~lib/memory/__stack_pointer global.get $~lib/memory/__data_end @@ -655,4 +683,20 @@ i32.add global.set $~lib/memory/__stack_pointer ) + (func $export:possibly-null/testInit (param $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + call $possibly-null/testInit + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) ) diff --git a/tests/compiler/possibly-null.release.wat b/tests/compiler/possibly-null.release.wat index b2326f4c35..d517c0beaa 100644 --- a/tests/compiler/possibly-null.release.wat +++ b/tests/compiler/possibly-null.release.wat @@ -26,6 +26,7 @@ (export "testAssignInCondi" (func $export:possibly-null/testTrue)) (export "testNeverNull" (func $export:possibly-null/testTrue)) (export "testLogicalOrTypeInfer" (func $export:possibly-null/testLogicalOrTypeInfer)) + (export "testInit" (func $export:possibly-null/testInit)) (func $export:possibly-null/testTrue (param $0 i32) (local $1 i32) global.get $~lib/memory/__stack_pointer @@ -243,4 +244,51 @@ call $~lib/builtins/abort unreachable ) + (func $export:possibly-null/testInit (param $0 i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1024 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $1 + local.get $0 + i32.store + local.get $1 + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1024 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $1 + i32.const 0 + i32.store + local.get $1 + local.get $0 + i32.store + local.get $1 + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + return + end + i32.const 17440 + i32.const 17488 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) ) diff --git a/tests/compiler/possibly-null.ts b/tests/compiler/possibly-null.ts index c595e58d88..760f73b8c9 100644 --- a/tests/compiler/possibly-null.ts +++ b/tests/compiler/possibly-null.ts @@ -137,3 +137,10 @@ export function testLogicalOrTypeInfer(a: Ref | null, b: Ref): void { let c: Ref = a || b; if (isNullable(c)) ERROR("should be non-nullable"); } + +export function testInit(a: Ref): void { + let b: Ref | null = a; + if (isNullable(b)) ERROR("should be non-nullable"); + let c: Ref | null = requireNonNull(a); + if (isNullable(c)) ERROR("should be non-nullable"); +} From fb117fac92d1408fc95930f541e74516779fa271 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 11 Aug 2022 07:40:02 +0800 Subject: [PATCH 52/55] switch position for `inheritBranch` and `freeScopedLocals` --- src/compiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 870b14404d..0c729ec053 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -4571,8 +4571,8 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = this.currentType; - rightFlow.freeScopedLocals(); flow.inheritBranch(rightFlow); + rightFlow.freeScopedLocals(); this.currentFlow = flow; // simplify if copying left is trivial @@ -4636,8 +4636,8 @@ export class Compiler extends DiagnosticEmitter { } else { rightExpr = this.compileExpression(right, leftType, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = this.currentType; - rightFlow.freeScopedLocals(); flow.inheritBranch(rightFlow); + rightFlow.freeScopedLocals(); this.currentFlow = flow; // simplify if copying left is trivial From 3554be4cae1e783e33ab9991944f07073013f715 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 11 Aug 2022 07:45:31 +0800 Subject: [PATCH 53/55] copy `narrowedTypes` when `inhertiMutual` inherit 1 flow --- src/flow.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/flow.ts b/src/flow.ts index 6a5e80c6a7..cfedebbcf2 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -995,14 +995,16 @@ export class Flow { for (let i = 0, k = rightLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = rightLocalFlags[i]; } - this.narrowedTypes = right.narrowedTypes; + let rightNarrowedTypes = right.narrowedTypes; + this.narrowedTypes = rightNarrowedTypes ? rightNarrowedTypes.clone() : null; } } else if (rightFlags & FlowFlags.TERMINATES) { let leftLocalFlags = left.localFlags; for (let i = 0, k = leftLocalFlags.length; i < k; ++i) { thisLocalFlags[i] = leftLocalFlags[i]; } - this.narrowedTypes = left.narrowedTypes; + let leftNarrowedTypes = left.narrowedTypes; + this.narrowedTypes = leftNarrowedTypes ? leftNarrowedTypes.clone() : null; } else { let leftLocalFlags = left.localFlags; let numLeftLocalFlags = leftLocalFlags.length; From 3edce9df4e59eb4c31fb7e3732cadceda650f0e7 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Thu, 11 Aug 2022 07:56:34 +0800 Subject: [PATCH 54/55] refacrory `getVariantType` --- src/compiler.ts | 7 +++---- src/flow.ts | 21 +++++++-------------- src/resolver.ts | 4 +--- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 0c729ec053..a58ccc2660 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7767,8 +7767,7 @@ export class Compiler extends DiagnosticEmitter { switch (target.kind) { case ElementKind.LOCAL: { let local = target; - let narrowedType = flow.getNarrowedType(local); - let localType = narrowedType || local.type; + let localType = flow.getVariantType(local); assert(localType != Type.void); if (this.pendingElements.has(local)) { this.error( @@ -7802,8 +7801,8 @@ export class Compiler extends DiagnosticEmitter { if (!this.compileGlobal(global)) { // reports; not yet compiled if a static field return module.unreachable(); } - let narrowedType = flow.getNarrowedType(global); - let globalType = narrowedType || global.type; + // TODO: global type narrow + let globalType = global.type; if (this.pendingElements.has(global)) { this.error( DiagnosticCode.Variable_0_used_before_its_declaration, diff --git a/src/flow.ts b/src/flow.ts index cfedebbcf2..d26e67b2ab 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -645,11 +645,11 @@ export class Flow { narrowedTypes.set(element, type); } /** get type narrow, return null if not exist */ - getNarrowedType(element: TypedElement): Type | null { - if (element.kind != ElementKind.LOCAL) return null; + getVariantType(element: TypedElement): Type { + if (element.kind != ElementKind.LOCAL) return element.type; const narrowedTypes = this.narrowedTypes || new NarrowedTypeMap(); this.narrowedTypes = narrowedTypes; - return narrowedTypes.get(element); + return narrowedTypes.get(element) || element.type; } /** do not trace `element` type narrow */ removeNarrowedType(element: TypedElement): void { @@ -1068,12 +1068,8 @@ export class Flow { return true; } } - let beforeNarrowedTypes = before.narrowedTypes; - let afterNarrowedTypes = after.narrowedTypes; - let beforeType = beforeNarrowedTypes ? beforeNarrowedTypes.get(local) : null; - beforeType = beforeType || local.type; - let afterType = afterNarrowedTypes ? afterNarrowedTypes.get(local) : null; - afterType = afterType || local.type; + let beforeType = before.getVariantType(local); + let afterType = after.getVariantType(local); if (type.isNullableReference) { if (beforeType != afterType) { return true; @@ -1102,7 +1098,6 @@ export class Flow { /** Checks if an expression of the specified type is known to be non-null, even if the type might be nullable. */ isNonnull(expr: ExpressionRef, type: Type): bool { if (!type.isNullableReference) return true; - let thisNarrowedTypes = this.narrowedTypes; // below, only teeLocal/getLocal are relevant because these are the only expressions that // depend on a dynamic nullable state (flag = LocalFlags.NONNULL), while everything else // has already been handled by the nullable type check above. @@ -1110,14 +1105,12 @@ export class Flow { case ExpressionId.LocalSet: { if (!isLocalTee(expr)) break; let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)]; - let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; - localType = localType || local.type; + let localType = this.getVariantType(local); return !localType.isNullableReference; } case ExpressionId.LocalGet: { let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)]; - let localType = thisNarrowedTypes ? thisNarrowedTypes.get(local) : null; - localType = localType || local.type; + let localType = this.getVariantType(local); return !localType.isNullableReference; } } diff --git a/src/resolver.ts b/src/resolver.ts index 3f9d91e532..a3417c2f99 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -1274,9 +1274,7 @@ export class Resolver extends DiagnosticEmitter { case ElementKind.LOCAL: case ElementKind.FIELD: { // someVar.prop let variableLikeElement = target; - let type = variableLikeElement.type; - let narrowedType = ctxFlow.getNarrowedType(variableLikeElement); - if (narrowedType) type = narrowedType; + const type = ctxFlow.getVariantType(variableLikeElement); assert(type != Type.void); let classReference = type.getClassOrWrapper(this.program); if (!classReference) { From c11571f8e8f3760b340fd753c9587d38c52f869f Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 21 Aug 2022 10:45:42 +0800 Subject: [PATCH 55/55] fix merge conflict --- tests/compiler/typenarrow.debug.wat | 194 ++++++++++++++-------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/tests/compiler/typenarrow.debug.wat b/tests/compiler/typenarrow.debug.wat index fef3aa1326..c32ddd8449 100644 --- a/tests/compiler/typenarrow.debug.wat +++ b/tests/compiler/typenarrow.debug.wat @@ -20,74 +20,74 @@ (elem $0 (i32.const 1) $typenarrow/B#b1) (export "memory" (memory $0)) (export "condiNarrow" (func $export:typenarrow/condiNarrow)) - (func $~lib/rt/__instanceof (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $0 + (func $~lib/rt/__instanceof (param $ptr i32) (param $classId i32) (result i32) + (local $id i32) + (local $rttiBase i32) + local.get $ptr i32.const 20 i32.sub i32.load offset=12 - local.set $2 + local.set $id global.get $~lib/rt/__rtti_base - local.set $3 - local.get $2 - local.get $3 + local.set $rttiBase + local.get $id + local.get $rttiBase i32.load i32.le_u if loop $do-loop|0 - local.get $2 - local.get $1 + local.get $id + local.get $classId i32.eq if i32.const 1 return end - local.get $3 + local.get $rttiBase i32.const 4 i32.add - local.get $2 + local.get $id i32.const 8 i32.mul i32.add i32.load offset=4 - local.tee $2 + local.tee $id br_if $do-loop|0 end end i32.const 0 ) - (func $typenarrow/B#b1 (param $0 i32) + (func $typenarrow/B#b1 (param $this i32) nop ) - (func $typenarrow/B#check (param $0 i32) (result i32) + (func $typenarrow/B#check (param $this i32) (result i32) i32.const 1 ) - (func $typenarrow/condiNarrow (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $0 - local.tee $2 + (func $typenarrow/condiNarrow (param $v0 i32) (param $v1 i32) + (local $var$2 i32) + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if - local.get $0 + local.get $v0 call $typenarrow/B#b1 - local.get $0 - local.set $2 + local.get $v0 + local.set $var$2 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -95,29 +95,29 @@ if nop else - local.get $0 + local.get $v0 call $typenarrow/B#b1 - local.get $0 - local.set $2 + local.get $v0 + local.set $var$2 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -125,29 +125,29 @@ i32.const 0 end if - local.get $0 + local.get $v0 call $typenarrow/B#b1 - local.get $1 + local.get $v1 call $typenarrow/B#b1 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -156,28 +156,28 @@ i32.const 0 end if - local.get $0 + local.get $v0 call $typenarrow/B#b1 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end i32.eqz if (result i32) - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -185,16 +185,16 @@ i32.const 0 end if - local.get $1 + local.get $v1 call $typenarrow/B#b1 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -202,13 +202,13 @@ if (result i32) i32.const 1 else - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -222,26 +222,26 @@ i32.const 32 drop end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) i32.const 1 else - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -253,13 +253,13 @@ i32.const 32 drop end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -267,13 +267,13 @@ if (result i32) i32.const 1 else - local.get $1 - local.tee $2 + local.get $v1 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -284,18 +284,18 @@ i32.const 32 drop end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) - local.get $0 + local.get $v0 call $typenarrow/B#check else i32.const 0 @@ -303,13 +303,13 @@ if nop end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end @@ -317,19 +317,19 @@ if (result i32) i32.const 1 else - local.get $0 + local.get $v0 call $typenarrow/B#check end if nop end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 6 call $~lib/rt/__instanceof end @@ -339,58 +339,58 @@ i32.const 0 end if - local.get $0 + local.get $v0 i32.load drop - local.get $0 + local.get $v0 call $typenarrow/B#b1 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) i32.const 1 else - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 6 call $~lib/rt/__instanceof end end if - local.get $0 + local.get $v0 call $typenarrow/B#b1 end - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 4 call $~lib/rt/__instanceof end if (result i32) - local.get $0 - local.tee $2 + local.get $v0 + local.tee $var$2 i32.eqz if (result i32) i32.const 0 else - local.get $2 + local.get $var$2 i32.const 6 call $~lib/rt/__instanceof end @@ -401,9 +401,9 @@ if nop else - local.get $0 + local.get $v0 call $typenarrow/B#b1 - local.get $0 + local.get $v0 i32.load drop end