11import type { HIRModule , HIRExpr , HIRStmt , HIRFunction } from "../hir/types.js" ;
22import { I64 , F64 } from "../hir/types.js" ;
33
4- const SAFE_OPS = new Set ( [ "add" , "sub" ] ) ;
4+ const SAFE_OPS = new Set ( [ "add" , "sub" , "mul" , "rem" ] ) ;
55const CMP_OPS = new Set ( [ "eq" , "ne" , "lt" , "le" , "gt" , "ge" ] ) ;
66const ARITH_OPS = new Set ( [ "add" , "sub" , "mul" , "rem" ] ) ;
77
@@ -19,116 +19,120 @@ function isIntLiteralInit(e: HIRExpr | undefined): boolean {
1919 return false ;
2020}
2121
22- function isSafeIntPureExpr ( expr : HIRExpr , name : string ) : boolean {
22+ function isSafeIntPureExpr ( expr : HIRExpr , name : string , eligible : Set < string > ) : boolean {
2323 switch ( expr . kind ) {
2424 case "literal_i64" :
2525 return true ;
2626 case "literal_f64" :
2727 return Number . isInteger ( expr . value ) && Math . abs ( expr . value ) <= Number . MAX_SAFE_INTEGER ;
2828 case "global_get" :
29- return expr . name === name ;
29+ if ( expr . name === name ) return true ;
30+ if ( eligible . has ( expr . name ) ) return true ;
31+ return expr . type . kind === "i64" ;
32+ case "local_get" :
33+ return expr . type . kind === "i64" ;
3034 case "binary" :
3135 if ( ! SAFE_OPS . has ( expr . op ) ) return false ;
32- return isSafeIntPureExpr ( expr . left , name ) && isSafeIntPureExpr ( expr . right , name ) ;
36+ return isSafeIntPureExpr ( expr . left , name , eligible ) && isSafeIntPureExpr ( expr . right , name , eligible ) ;
3337 case "widen_f64" :
3438 case "narrow_i64" :
35- return isSafeIntPureExpr ( expr . value , name ) ;
39+ return isSafeIntPureExpr ( expr . value , name , eligible ) ;
3640 default :
3741 return false ;
3842 }
3943}
4044
41- function visitExpr ( expr : HIRExpr , info : Map < string , GlobalState > ) : void {
45+ function visitExpr ( expr : HIRExpr , info : Map < string , GlobalState > , candidates : Set < string > ) : void {
4246 if ( expr . kind === "global_set" ) {
4347 const cur = info . get ( expr . name ) ;
44- if ( cur && ! isSafeIntPureExpr ( expr . value , expr . name ) ) cur . hasIncompatibleWrite = true ;
45- visitExpr ( expr . value , info ) ;
48+ if ( cur && ! isSafeIntPureExpr ( expr . value , expr . name , candidates ) ) cur . hasIncompatibleWrite = true ;
49+ visitExpr ( expr . value , info , candidates ) ;
4650 return ;
4751 }
4852 switch ( expr . kind ) {
4953 case "binary" :
50- visitExpr ( expr . left , info ) ;
51- visitExpr ( expr . right , info ) ;
54+ visitExpr ( expr . left , info , candidates ) ;
55+ visitExpr ( expr . right , info , candidates ) ;
5256 return ;
5357 case "unary" :
54- visitExpr ( expr . operand , info ) ;
58+ visitExpr ( expr . operand , info , candidates ) ;
5559 return ;
5660 case "call" :
5761 case "runtime_call" :
58- for ( const a of expr . args ) visitExpr ( a , info ) ;
62+ for ( const a of expr . args ) visitExpr ( a , info , candidates ) ;
5963 return ;
6064 case "vtable_call" :
61- visitExpr ( expr . object , info ) ;
62- for ( const a of expr . args ) visitExpr ( a , info ) ;
65+ visitExpr ( expr . object , info , candidates ) ;
66+ for ( const a of expr . args ) visitExpr ( a , info , candidates ) ;
6367 return ;
6468 case "call_closure" :
65- visitExpr ( expr . callee , info ) ;
66- for ( const a of expr . args ) visitExpr ( a , info ) ;
69+ visitExpr ( expr . callee , info , candidates ) ;
70+ for ( const a of expr . args ) visitExpr ( a , info , candidates ) ;
6771 return ;
6872 case "conditional" :
69- visitExpr ( expr . condition , info ) ;
70- visitExpr ( expr . then , info ) ;
71- visitExpr ( expr . else , info ) ;
73+ visitExpr ( expr . condition , info , candidates ) ;
74+ visitExpr ( expr . then , info , candidates ) ;
75+ visitExpr ( expr . else , info , candidates ) ;
7276 return ;
7377 case "field_get" :
74- visitExpr ( expr . object , info ) ;
78+ visitExpr ( expr . object , info , candidates ) ;
7579 return ;
7680 case "field_set" :
77- visitExpr ( expr . object , info ) ;
78- visitExpr ( expr . value , info ) ;
81+ visitExpr ( expr . object , info , candidates ) ;
82+ visitExpr ( expr . value , info , candidates ) ;
7983 return ;
8084 case "index_get" :
81- visitExpr ( expr . array , info ) ;
82- visitExpr ( expr . index , info ) ;
85+ visitExpr ( expr . array , info , candidates ) ;
86+ visitExpr ( expr . index , info , candidates ) ;
8387 return ;
8488 case "index_set" :
85- visitExpr ( expr . array , info ) ;
86- visitExpr ( expr . index , info ) ;
87- visitExpr ( expr . value , info ) ;
89+ visitExpr ( expr . array , info , candidates ) ;
90+ visitExpr ( expr . index , info , candidates ) ;
91+ visitExpr ( expr . value , info , candidates ) ;
8892 return ;
8993 case "local_set" :
90- visitExpr ( expr . value , info ) ;
94+ visitExpr ( expr . value , info , candidates ) ;
9195 return ;
9296 case "narrow_i64" :
9397 case "widen_f64" :
9498 case "box" :
9599 case "unbox" :
96100 case "await" :
97101 case "wrap_interface" :
98- visitExpr ( expr . value , info ) ;
102+ visitExpr ( expr . value , info , candidates ) ;
99103 return ;
100104 case "alloc_array" :
101- for ( const v of expr . initialValues ) visitExpr ( v , info ) ;
105+ for ( const v of expr . initialValues ) visitExpr ( v , info , candidates ) ;
102106 return ;
103107 case "alloc_struct" :
104- for ( const v of expr . fields ) visitExpr ( v , info ) ;
108+ for ( const v of expr . fields ) visitExpr ( v , info , candidates ) ;
105109 return ;
106110 case "alloc_dynobj" :
107- for ( const p of expr . props as any [ ] ) visitExpr ( p . value , info ) ;
108- if ( expr . spreadSource ) visitExpr ( expr . spreadSource , info ) ;
111+ for ( const p of expr . props as any [ ] ) visitExpr ( p . value , info , candidates ) ;
112+ if ( expr . spreadSource ) visitExpr ( expr . spreadSource , info , candidates ) ;
109113 return ;
110114 case "alloc_dynarray" :
111- for ( const v of expr . elements as any [ ] ) visitExpr ( v , info ) ;
115+ for ( const v of expr . elements as any [ ] ) visitExpr ( v , info , candidates ) ;
112116 return ;
113117 case "alloc_array_spread" :
114- for ( const e of expr . elements as any [ ] ) visitExpr ( e . value , info ) ;
118+ for ( const e of expr . elements as any [ ] ) visitExpr ( e . value , info , candidates ) ;
115119 return ;
116120 case "alloc_map" :
117121 for ( const e of expr . entries as any [ ] ) {
118- visitExpr ( e . key , info ) ;
119- visitExpr ( e . value , info ) ;
122+ visitExpr ( e . key , info , candidates ) ;
123+ visitExpr ( e . value , info , candidates ) ;
120124 }
121125 return ;
122126 case "alloc_set" :
123- for ( const v of expr . elements as any [ ] ) visitExpr ( v , info ) ;
127+ for ( const v of expr . elements as any [ ] ) visitExpr ( v , info , candidates ) ;
124128 return ;
125129 case "nullish_coalesce" :
126- visitExpr ( expr . left , info ) ;
127- visitExpr ( expr . right , info ) ;
130+ visitExpr ( expr . left , info , candidates ) ;
131+ visitExpr ( expr . right , info , candidates ) ;
128132 return ;
129133 case "array_hof" :
130- visitExpr ( expr . array , info ) ;
131- visitExpr ( expr . callback , info ) ;
134+ visitExpr ( expr . array , info , candidates ) ;
135+ visitExpr ( expr . callback , info , candidates ) ;
132136 return ;
133137 case "make_closure" :
134138 return ;
@@ -137,45 +141,45 @@ function visitExpr(expr: HIRExpr, info: Map<string, GlobalState>): void {
137141 }
138142}
139143
140- function visitStmt ( stmt : HIRStmt , info : Map < string , GlobalState > ) : void {
144+ function visitStmt ( stmt : HIRStmt , info : Map < string , GlobalState > , candidates : Set < string > ) : void {
141145 switch ( stmt . kind ) {
142146 case "let" :
143- if ( stmt . init ) visitExpr ( stmt . init , info ) ;
147+ if ( stmt . init ) visitExpr ( stmt . init , info , candidates ) ;
144148 return ;
145149 case "expr" :
146- visitExpr ( stmt . expr , info ) ;
150+ visitExpr ( stmt . expr , info , candidates ) ;
147151 return ;
148152 case "return" :
149- if ( stmt . value ) visitExpr ( stmt . value , info ) ;
153+ if ( stmt . value ) visitExpr ( stmt . value , info , candidates ) ;
150154 return ;
151155 case "if" :
152- visitExpr ( stmt . condition , info ) ;
153- stmt . then . forEach ( ( s ) => visitStmt ( s , info ) ) ;
154- stmt . else ?. forEach ( ( s ) => visitStmt ( s , info ) ) ;
156+ visitExpr ( stmt . condition , info , candidates ) ;
157+ stmt . then . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
158+ stmt . else ?. forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
155159 return ;
156160 case "while" :
157- visitExpr ( stmt . condition , info ) ;
158- stmt . body . forEach ( ( s ) => visitStmt ( s , info ) ) ;
161+ visitExpr ( stmt . condition , info , candidates ) ;
162+ stmt . body . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
159163 return ;
160164 case "for" :
161- if ( stmt . init ) visitStmt ( stmt . init , info ) ;
162- if ( stmt . condition ) visitExpr ( stmt . condition , info ) ;
163- if ( stmt . update ) visitExpr ( stmt . update , info ) ;
164- stmt . body . forEach ( ( s ) => visitStmt ( s , info ) ) ;
165+ if ( stmt . init ) visitStmt ( stmt . init , info , candidates ) ;
166+ if ( stmt . condition ) visitExpr ( stmt . condition , info , candidates ) ;
167+ if ( stmt . update ) visitExpr ( stmt . update , info , candidates ) ;
168+ stmt . body . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
165169 return ;
166170 case "throw" :
167- visitExpr ( stmt . value , info ) ;
171+ visitExpr ( stmt . value , info , candidates ) ;
168172 return ;
169173 case "try" :
170- stmt . body . forEach ( ( s ) => visitStmt ( s , info ) ) ;
171- stmt . catch ?. body . forEach ( ( s ) => visitStmt ( s , info ) ) ;
172- stmt . finally ?. forEach ( ( s ) => visitStmt ( s , info ) ) ;
174+ stmt . body . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
175+ stmt . catch ?. body . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
176+ stmt . finally ?. forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
173177 return ;
174178 case "switch" :
175- visitExpr ( stmt . discriminant , info ) ;
179+ visitExpr ( stmt . discriminant , info , candidates ) ;
176180 for ( const c of stmt . cases ) {
177- if ( c . test ) visitExpr ( c . test , info ) ;
178- c . body . forEach ( ( s ) => visitStmt ( s , info ) ) ;
181+ if ( c . test ) visitExpr ( c . test , info , candidates ) ;
182+ c . body . forEach ( ( s ) => visitStmt ( s , info , candidates ) ) ;
179183 }
180184 return ;
181185 default :
@@ -392,8 +396,8 @@ function rewriteStmt(stmt: HIRStmt, eligible: Set<string>): HIRStmt {
392396 }
393397}
394398
395- function visitFn ( fn : HIRFunction , info : Map < string , GlobalState > ) : void {
396- for ( const s of fn . body ) visitStmt ( s , info ) ;
399+ function visitFn ( fn : HIRFunction , info : Map < string , GlobalState > , candidates : Set < string > ) : void {
400+ for ( const s of fn . body ) visitStmt ( s , info , candidates ) ;
397401}
398402
399403function rewriteFn ( fn : HIRFunction , eligible : Set < string > ) : void {
@@ -408,9 +412,10 @@ export function narrowGlobalsPass(mod: HIRModule): void {
408412 }
409413 if ( info . size === 0 ) return ;
410414
411- for ( const s of mod . init ) visitStmt ( s , info ) ;
412- for ( const fn of mod . functions ) visitFn ( fn , info ) ;
413- for ( const cls of mod . classes ) for ( const m of cls . methods ) visitFn ( m , info ) ;
415+ const candidates = new Set ( info . keys ( ) ) ;
416+ for ( const s of mod . init ) visitStmt ( s , info , candidates ) ;
417+ for ( const fn of mod . functions ) visitFn ( fn , info , candidates ) ;
418+ for ( const cls of mod . classes ) for ( const m of cls . methods ) visitFn ( m , info , candidates ) ;
414419
415420 const eligible = new Set < string > ( ) ;
416421 for ( const gi of info . values ( ) ) {
0 commit comments