@@ -5016,86 +5016,89 @@ export class Compiler extends DiagnosticEmitter {
50165016 rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType ) ;
50175017 rightFlow . freeScopedLocals ( ) ;
50185018 this . currentFlow = flow ;
5019- this . currentType = Type . bool ;
50205019 expr = module . if (
50215020 this . makeIsTrueish ( leftExpr , leftType ) ,
50225021 this . makeIsTrueish ( rightExpr , rightType ) ,
50235022 module . i32 ( 0 )
50245023 ) ;
5024+ this . currentType = Type . bool ;
50255025
5026- // references must properly retain and release, with the same outcome independent of the branch taken
5027- } else if ( leftType . isManaged ) {
5028- let leftAutoreleaseSkipped = this . skippedAutoreleases . has ( leftExpr ) ;
5029- let rightAutoreleaseSkipped = this . skippedAutoreleases . has ( rightExpr ) ;
5030- let temp = flow . getTempLocal ( leftType ) ;
5031- leftExpr = module . local_tee ( temp . index , leftExpr ) ;
5032-
5033- // instead of retaining left and releasing it again in right when right
5034- // is taken, we can also just retain left if right is not taken
5035- let retainLeftInElse = false ;
5036- if ( leftAutoreleaseSkipped != rightAutoreleaseSkipped ) { // xor
5037- if ( ! leftAutoreleaseSkipped ) {
5038- retainLeftInElse = true ;
5039- } else {
5040- rightExpr = this . makeRetain ( rightExpr ) ;
5041- rightAutoreleaseSkipped = true ;
5042- }
5043- } else if ( ! ( constraints & Constraints . WILL_RETAIN ) ) { // otherwise keep right alive a little longer
5044- rightExpr = this . delayAutorelease ( rightExpr , rightType , rightFlow , flow ) ;
5045- }
5026+ } else {
50465027
5047- let rightStmts = new Array < ExpressionRef > ( ) ;
5048- if ( leftAutoreleaseSkipped ) { // left turned out to be true'ish and is dropped
5049- rightStmts . unshift (
5050- this . makeRelease (
5051- module . local_get ( temp . index , leftType . toNativeType ( ) )
5052- )
5053- ) ;
5054- }
5055- rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType , rightStmts ) ;
5056- rightFlow . freeScopedLocals ( ) ;
5057- this . currentFlow = flow ;
5028+ // references must properly retain and release, with the same outcome independent of the branch taken
5029+ if ( leftType . isManaged ) {
5030+ let leftAutoreleaseSkipped = this . skippedAutoreleases . has ( leftExpr ) ;
5031+ let rightAutoreleaseSkipped = this . skippedAutoreleases . has ( rightExpr ) ;
5032+ let temp = flow . getTempLocal ( leftType ) ;
5033+ leftExpr = module . local_tee ( temp . index , leftExpr ) ;
5034+
5035+ // instead of retaining left and releasing it again in right when right
5036+ // is taken, we can also just retain left if right is not taken
5037+ let retainLeftInElse = false ;
5038+ if ( leftAutoreleaseSkipped != rightAutoreleaseSkipped ) { // xor
5039+ if ( ! leftAutoreleaseSkipped ) {
5040+ retainLeftInElse = true ;
5041+ } else {
5042+ rightExpr = this . makeRetain ( rightExpr ) ;
5043+ rightAutoreleaseSkipped = true ;
5044+ }
5045+ } else if ( ! ( constraints & Constraints . WILL_RETAIN ) ) { // otherwise keep right alive a little longer
5046+ rightExpr = this . delayAutorelease ( rightExpr , rightType , rightFlow , flow ) ;
5047+ }
50585048
5059- expr = module . if (
5060- this . makeIsTrueish ( leftExpr , leftType ) ,
5061- rightExpr ,
5062- retainLeftInElse
5063- ? this . makeRetain (
5049+ let rightStmts = new Array < ExpressionRef > ( ) ;
5050+ if ( leftAutoreleaseSkipped ) { // left turned out to be true'ish and is dropped
5051+ rightStmts . unshift (
5052+ this . makeRelease (
50645053 module . local_get ( temp . index , leftType . toNativeType ( ) )
50655054 )
5066- : module . local_get ( temp . index , leftType . toNativeType ( ) )
5067- ) ;
5068- if ( leftAutoreleaseSkipped || rightAutoreleaseSkipped ) this . skippedAutoreleases . add ( expr ) ;
5069- if ( temp ) flow . freeTempLocal ( temp ) ;
5070-
5071- // basic values can use more aggressive optimizations
5072- } else {
5073- rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType ) ;
5074- rightFlow . freeScopedLocals ( ) ;
5075- this . currentFlow = flow ;
5055+ ) ;
5056+ }
5057+ rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType , rightStmts ) ;
5058+ rightFlow . freeScopedLocals ( ) ;
5059+ this . currentFlow = flow ;
50765060
5077- // simplify if cloning left without side effects is possible
5078- if ( expr = module . cloneExpression ( leftExpr , true , 0 ) ) {
50795061 expr = module . if (
5080- this . makeIsTrueish ( leftExpr , this . currentType ) ,
5062+ this . makeIsTrueish ( leftExpr , leftType ) ,
50815063 rightExpr ,
5082- expr
5064+ retainLeftInElse
5065+ ? this . makeRetain (
5066+ module . local_get ( temp . index , leftType . toNativeType ( ) )
5067+ )
5068+ : module . local_get ( temp . index , leftType . toNativeType ( ) )
50835069 ) ;
5070+ if ( leftAutoreleaseSkipped || rightAutoreleaseSkipped ) this . skippedAutoreleases . add ( expr ) ;
5071+ if ( temp ) flow . freeTempLocal ( temp ) ;
50845072
5085- // if not possible, tee left to a temp
5073+ // basic values can use more aggressive optimizations
50865074 } else {
5087- let tempLocal = flow . getTempLocal ( leftType ) ;
5088- if ( ! flow . canOverflow ( leftExpr , leftType ) ) flow . setLocalFlag ( tempLocal . index , LocalFlags . WRAPPED ) ;
5089- if ( flow . isNonnull ( leftExpr , leftType ) ) flow . setLocalFlag ( tempLocal . index , LocalFlags . NONNULL ) ;
5090- expr = module . if (
5091- this . makeIsTrueish ( module . local_tee ( tempLocal . index , leftExpr ) , leftType ) ,
5092- rightExpr ,
5093- module . local_get ( tempLocal . index , leftType . toNativeType ( ) )
5094- ) ;
5095- flow . freeTempLocal ( tempLocal ) ;
5075+ rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType ) ;
5076+ rightFlow . freeScopedLocals ( ) ;
5077+ this . currentFlow = flow ;
5078+
5079+ // simplify if cloning left without side effects is possible
5080+ if ( expr = module . cloneExpression ( leftExpr , true , 0 ) ) {
5081+ expr = module . if (
5082+ this . makeIsTrueish ( leftExpr , this . currentType ) ,
5083+ rightExpr ,
5084+ expr
5085+ ) ;
5086+
5087+ // if not possible, tee left to a temp
5088+ } else {
5089+ let tempLocal = flow . getTempLocal ( leftType ) ;
5090+ if ( ! flow . canOverflow ( leftExpr , leftType ) ) flow . setLocalFlag ( tempLocal . index , LocalFlags . WRAPPED ) ;
5091+ if ( flow . isNonnull ( leftExpr , leftType ) ) flow . setLocalFlag ( tempLocal . index , LocalFlags . NONNULL ) ;
5092+ expr = module . if (
5093+ this . makeIsTrueish ( module . local_tee ( tempLocal . index , leftExpr ) , leftType ) ,
5094+ rightExpr ,
5095+ module . local_get ( tempLocal . index , leftType . toNativeType ( ) )
5096+ ) ;
5097+ flow . freeTempLocal ( tempLocal ) ;
5098+ }
50965099 }
5100+ this . currentType = leftType ;
50975101 }
5098- this . currentType = leftType ;
50995102 break ;
51005103 }
51015104 case Token . BAR_BAR : { // left || right -> ((t = left) ? t : right)
@@ -5115,88 +5118,91 @@ export class Compiler extends DiagnosticEmitter {
51155118 rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , leftType ) ;
51165119 rightFlow . freeScopedLocals ( ) ;
51175120 this . currentFlow = flow ;
5118- this . currentType = Type . bool ;
51195121 expr = module . if (
51205122 this . makeIsTrueish ( leftExpr , leftType ) ,
51215123 module . i32 ( 1 ) ,
51225124 this . makeIsTrueish ( rightExpr , rightType )
51235125 ) ;
5126+ this . currentType = Type . bool ;
51245127
5125- // references must properly retain and release, with the same outcome independent of the branch taken
5126- } else if ( leftType . isManaged ) {
5127- let leftAutoreleaseSkipped = this . skippedAutoreleases . has ( leftExpr ) ;
5128- let rightAutoreleaseSkipped = this . skippedAutoreleases . has ( rightExpr ) ;
5129- let temp = flow . getTempLocal ( leftType ) ;
5130- leftExpr = module . local_tee ( temp . index , leftExpr ) ;
5131-
5132- // instead of retaining left and releasing it again in right when right
5133- // is taken, we can also just retain left if right is not taken
5134- let retainLeftInThen = false ;
5135- if ( leftAutoreleaseSkipped != rightAutoreleaseSkipped ) { // xor
5136- if ( ! leftAutoreleaseSkipped ) {
5137- retainLeftInThen = true ;
5138- } else {
5139- rightExpr = this . makeRetain ( rightExpr ) ;
5140- rightAutoreleaseSkipped = true ;
5141- }
5142- } else if ( ! ( constraints & Constraints . WILL_RETAIN ) ) { // otherwise keep right alive a little longer
5143- rightExpr = this . delayAutorelease ( rightExpr , rightType , rightFlow , flow ) ;
5144- }
5128+ } else {
51455129
5146- let rightStmts = new Array < ExpressionRef > ( ) ;
5147- if ( leftAutoreleaseSkipped ) { // left turned out to be false'ish and is dropped
5148- // TODO: usually, false'ish means left is null, but this might not hold
5149- // once implicit conversion with strings is performed and left is "", so:
5150- rightStmts . unshift (
5151- this . makeRelease (
5152- module . local_get ( temp . index , leftType . toNativeType ( ) )
5153- )
5154- ) ;
5155- }
5156- rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType , rightStmts ) ;
5157- rightFlow . freeScopedLocals ( ) ;
5158- this . currentFlow = flow ;
5130+ // references must properly retain and release, with the same outcome independent of the branch taken
5131+ if ( leftType . isManaged ) {
5132+ let leftAutoreleaseSkipped = this . skippedAutoreleases . has ( leftExpr ) ;
5133+ let rightAutoreleaseSkipped = this . skippedAutoreleases . has ( rightExpr ) ;
5134+ let temp = flow . getTempLocal ( leftType ) ;
5135+ leftExpr = module . local_tee ( temp . index , leftExpr ) ;
5136+
5137+ // instead of retaining left and releasing it again in right when right
5138+ // is taken, we can also just retain left if right is not taken
5139+ let retainLeftInThen = false ;
5140+ if ( leftAutoreleaseSkipped != rightAutoreleaseSkipped ) { // xor
5141+ if ( ! leftAutoreleaseSkipped ) {
5142+ retainLeftInThen = true ;
5143+ } else {
5144+ rightExpr = this . makeRetain ( rightExpr ) ;
5145+ rightAutoreleaseSkipped = true ;
5146+ }
5147+ } else if ( ! ( constraints & Constraints . WILL_RETAIN ) ) { // otherwise keep right alive a little longer
5148+ rightExpr = this . delayAutorelease ( rightExpr , rightType , rightFlow , flow ) ;
5149+ }
51595150
5160- expr = module . if (
5161- this . makeIsTrueish ( leftExpr , leftType ) ,
5162- retainLeftInThen
5163- ? this . makeRetain (
5151+ let rightStmts = new Array < ExpressionRef > ( ) ;
5152+ if ( leftAutoreleaseSkipped ) { // left turned out to be false'ish and is dropped
5153+ // TODO: usually, false'ish means left is null, but this might not hold
5154+ // once implicit conversion with strings is performed and left is "", so:
5155+ rightStmts . unshift (
5156+ this . makeRelease (
51645157 module . local_get ( temp . index , leftType . toNativeType ( ) )
51655158 )
5166- : module . local_get ( temp . index , leftType . toNativeType ( ) ) ,
5167- rightExpr
5168- ) ;
5169- if ( leftAutoreleaseSkipped || rightAutoreleaseSkipped ) this . skippedAutoreleases . add ( expr ) ;
5170- if ( temp ) flow . freeTempLocal ( temp ) ;
5171-
5172- // basic values can use more aggressive optimizations
5173- } else {
5174- rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType ) ;
5175- rightFlow . freeScopedLocals ( ) ;
5176- this . currentFlow = flow ;
5159+ ) ;
5160+ }
5161+ rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType , rightStmts ) ;
5162+ rightFlow . freeScopedLocals ( ) ;
5163+ this . currentFlow = flow ;
51775164
5178- // simplify if cloning left without side effects is possible
5179- if ( expr = module . cloneExpression ( leftExpr , true , 0 ) ) {
51805165 expr = module . if (
51815166 this . makeIsTrueish ( leftExpr , leftType ) ,
5182- expr ,
5167+ retainLeftInThen
5168+ ? this . makeRetain (
5169+ module . local_get ( temp . index , leftType . toNativeType ( ) )
5170+ )
5171+ : module . local_get ( temp . index , leftType . toNativeType ( ) ) ,
51835172 rightExpr
51845173 ) ;
5174+ if ( leftAutoreleaseSkipped || rightAutoreleaseSkipped ) this . skippedAutoreleases . add ( expr ) ;
5175+ if ( temp ) flow . freeTempLocal ( temp ) ;
51855176
5186- // if not possible, tee left to a temp. local
5177+ // basic values can use more aggressive optimizations
51875178 } else {
5188- let temp = flow . getTempLocal ( leftType ) ;
5189- if ( ! flow . canOverflow ( leftExpr , leftType ) ) flow . setLocalFlag ( temp . index , LocalFlags . WRAPPED ) ;
5190- if ( flow . isNonnull ( leftExpr , leftType ) ) flow . setLocalFlag ( temp . index , LocalFlags . NONNULL ) ;
5191- expr = module . if (
5192- this . makeIsTrueish ( module . local_tee ( temp . index , leftExpr ) , leftType ) ,
5193- module . local_get ( temp . index , leftType . toNativeType ( ) ) ,
5194- rightExpr
5195- ) ;
5196- flow . freeTempLocal ( temp ) ;
5179+ rightExpr = this . performAutoreleasesWithValue ( rightFlow , rightExpr , rightType ) ;
5180+ rightFlow . freeScopedLocals ( ) ;
5181+ this . currentFlow = flow ;
5182+
5183+ // simplify if cloning left without side effects is possible
5184+ if ( expr = module . cloneExpression ( leftExpr , true , 0 ) ) {
5185+ expr = module . if (
5186+ this . makeIsTrueish ( leftExpr , leftType ) ,
5187+ expr ,
5188+ rightExpr
5189+ ) ;
5190+
5191+ // if not possible, tee left to a temp. local
5192+ } else {
5193+ let temp = flow . getTempLocal ( leftType ) ;
5194+ if ( ! flow . canOverflow ( leftExpr , leftType ) ) flow . setLocalFlag ( temp . index , LocalFlags . WRAPPED ) ;
5195+ if ( flow . isNonnull ( leftExpr , leftType ) ) flow . setLocalFlag ( temp . index , LocalFlags . NONNULL ) ;
5196+ expr = module . if (
5197+ this . makeIsTrueish ( module . local_tee ( temp . index , leftExpr ) , leftType ) ,
5198+ module . local_get ( temp . index , leftType . toNativeType ( ) ) ,
5199+ rightExpr
5200+ ) ;
5201+ flow . freeTempLocal ( temp ) ;
5202+ }
51975203 }
5204+ this . currentType = leftType ;
51985205 }
5199- this . currentType = leftType ;
52005206 break ;
52015207 }
52025208 default : {
0 commit comments