1use std::{
2 collections::{BTreeMap, BTreeSet},
3 convert::Infallible,
4 default,
5 iter::{empty, once},
6 mem::take,
7};
8
9use anyhow::Context;
10use cfg_traits::Term;
11use id_arena::{Arena, Id};
12use portal_jsc_common::LId;
13use ssa_traits::Value;
14use swc_atoms::Atom;
15use swc_cfg::Catch;
16use swc_common::Span;
17use swc_ecma_ast::{Id as Ident, Lit, Null, TsType, TsTypeAnn, TsTypeParamDecl};
18use swc_tac::{Item, TBlock, TCallee, TCfg, TFunc, TStmt, ValFlags};
19pub mod ch;
20pub mod idw;
21pub mod impls;
22pub mod rew;
23pub mod simplify;
24
25pub fn benj(swc_func: &mut SCfg) {
26 for block_index in swc_func.blocks.iter().map(|a| a.0).collect::<Vec<_>>() {
27 let mut postcedent = take(&mut swc_func.blocks[block_index].postcedent);
28 for target in postcedent.targets_mut() {
29 if target.block.index() <= block_index.index() {
30 for arg in target.args.iter_mut() {
31 let value = swc_func.values.alloc(SValueW { value: SValue::Benc(*arg) });
32 swc_func.blocks[block_index].stmts.push(value);
33 *arg = value;
34 }
35 }
36 }
37 swc_func.blocks[block_index].postcedent = postcedent;
38 }
39}
40#[derive(Clone, Debug)]
41pub struct SFunc {
42 pub cfg: SCfg,
43 pub entry: Id<SBlock>,
44 pub is_generator: bool,
45 pub is_async: bool,
46 pub ts_params: Vec<Option<TsType>>,
47}
48impl TryFrom<TFunc> for SFunc {
49 type Error = anyhow::Error;
50
51 fn try_from(value: TFunc) -> Result<Self, Self::Error> {
52 let mut decls = value.cfg.decls.clone();
53 let mut d = BTreeSet::new();
54 for e in value.cfg.externs().collect::<BTreeSet<_>>() {
55 decls.remove(&e);
56 d.insert(e);
57 }
58 let mut cfg = SCfg {
59 blocks: Default::default(),
60 values: Default::default(),
61 ts: Default::default(),
62 decls: d,
63 generics: None,
64 ts_retty: None,
65 };
66 let entry2 = cfg.blocks.alloc(Default::default());
67 let params = value
68 .params
69 .iter()
70 .cloned()
71 .map(|a| (a, cfg.add_blockparam(entry2)))
72 .collect::<BTreeMap<_, _>>();
73 let undef = cfg.values.alloc(
74 SValue::Item {
75 item: Item::Undef,
76 span: None,
77 }
78 .into(),
79 );
80 let mut trans = Trans {
81 map: BTreeMap::new(),
82 all: decls.clone(),
83 undef,
84 };
85 let entry = trans.trans(&value.cfg, &mut cfg, value.entry)?;
86 let target = STarget {
87 block: entry,
88 args: decls
89 .iter()
90 .cloned()
91 .map(|a| match params.get(&a) {
92 Some(v) => *v,
93 None => undef,
94 })
95 .collect(),
96 };
97 cfg.blocks[entry2].postcedent.term = STerm::Jmp(target);
98 cfg.generics = value.cfg.generics;
99 cfg.ts_retty = value.cfg.ts_retty;
100 Ok(Self {
101 cfg,
102 entry: entry2,
103 is_generator: value.is_generator,
104 is_async: value.is_async,
105 ts_params: value.ts_params,
106 })
107 }
108}
109#[derive(Default, Clone, Debug)]
110pub struct SCfg {
111 pub blocks: Arena<SBlock>,
112 pub values: Arena<SValueW>,
113 pub ts: BTreeMap<Id<SValueW>, TsType>,
114 pub decls: BTreeSet<Ident>,
115 pub generics: Option<TsTypeParamDecl>,
116 pub ts_retty: Option<TsTypeAnn>,
117}
118impl SCfg {
119 pub fn refs(&self) -> BTreeSet<Ident> {
120 return self
121 .values
122 .iter()
123 .flat_map(|(a, b)| match &b.value {
124 SValue::LoadId(target) | SValue::StoreId { target, val: _ } => {
125 [target.clone()].into_iter().collect::<BTreeSet<Ident>>()
126 }
127 SValue::Item {
128 item: Item::Func { func },
129 span,
130 } => func.cfg.externals(),
131 _ => Default::default(),
132 })
133 .collect();
134 }
135 pub fn externals(&self) -> BTreeSet<Ident> {
136 return self
137 .refs()
138 .into_iter()
139 .filter(|a| !self.decls.contains(&*a))
140 .collect();
141 }
142}
143#[derive(Default, Clone, Debug)]
144pub struct SBlock {
145 pub params: Vec<(Id<SValueW>, ())>,
146 pub stmts: Vec<Id<SValueW>>,
147 pub postcedent: SPostcedent,
148}
149#[derive(Clone, Debug)]
150pub struct SPostcedent<I = Id<SValueW>, B = Id<SBlock>> {
151 pub term: STerm<I, B>,
152 pub catch: SCatch<I, B>,
153}
154impl<I, B> Default for SPostcedent<I, B> {
155 fn default() -> Self {
156 Self {
157 term: Default::default(),
158 catch: Default::default(),
159 }
160 }
161}
162
163#[derive(Clone, Debug)]
164#[non_exhaustive]
165pub enum SValue<I = Id<SValueW>, B = Id<SBlock>, F = SFunc> {
166 Param {
167 block: B,
168 idx: usize,
169 ty: (),
170 },
171 Item {
172 item: Item<I, F>,
173 span: Option<Span>,
174 },
175 Assign {
176 target: LId<I>,
177 val: I,
178 },
179 LoadId(Ident),
180 StoreId {
181 target: Ident,
182 val: I,
183 },
184 Benc(I),
185}
186impl<I: Copy, B, F> SValue<I, B, F> {
187 pub fn vals<'a>(&'a self) -> Box<dyn Iterator<Item = I> + 'a> {
188 match self {
189 SValue::Param { block, idx, ty } => Box::new(empty()),
190 SValue::Item { item, span } => Box::new(item.refs().map(|a| *a)),
191 SValue::Assign { target, val } => {
192 let v = once(*val);
193 let w: Box<dyn Iterator<Item = &I> + '_> = match target {
194 LId::Id { id } => todo!(),
195 LId::Member { obj, mem } => Box::new([obj, &mem[0]].into_iter()),
196 _ => todo!(),
197 };
198 Box::new(v.chain(w.cloned()))
199 }
200 SValue::LoadId(_) => Box::new(empty()),
201 SValue::StoreId { target, val } => Box::new(once(*val)),
202 SValue::Benc(a) => Box::new(once(*a)),
203 }
204 }
205}
206impl<I, B, F> SValue<I, B, F> {
207 pub fn as_ref(&self) -> SValue<&I, &B, &F> {
208 match self {
209 SValue::Param { block, idx, ty } => SValue::Param {
210 block,
211 idx: *idx,
212 ty: *ty,
213 },
214 SValue::Item { item, span } => SValue::Item {
215 item: item.as_ref(),
216 span: *span,
217 },
218 SValue::Assign { target, val } => SValue::Assign {
219 target: target
220 .as_ref()
221 .map2(&mut (), &mut |_, a| Ok::<_, Infallible>(a), &mut |_, b| {
222 Ok([&b[0]])
223 })
224 .unwrap(),
225 val,
226 },
227 SValue::LoadId(v) => SValue::LoadId(v.clone()),
228 SValue::StoreId { target, val } => SValue::StoreId {
229 target: target.clone(),
230 val,
231 },
232 SValue::Benc(v) => SValue::Benc(v),
233 }
234 }
235 pub fn as_mut(&mut self) -> SValue<&mut I, &mut B, &mut F> {
236 match self {
237 SValue::Param { block, idx, ty } => SValue::Param {
238 block,
239 idx: *idx,
240 ty: *ty,
241 },
242 SValue::Item { item, span } => SValue::Item {
243 item: item.as_mut(),
244 span: *span,
245 },
246 SValue::Assign { target, val } => SValue::Assign {
247 target: target
248 .as_mut()
249 .map2(&mut (), &mut |_, a| Ok::<_, Infallible>(a), &mut |_, b| {
250 Ok([&mut b[0]])
251 })
252 .unwrap(),
253 val,
254 },
255 SValue::LoadId(v) => SValue::LoadId(v.clone()),
256 SValue::StoreId { target, val } => SValue::StoreId {
257 target: target.clone(),
258 val,
259 },
260 SValue::Benc(v) => SValue::Benc(v),
261 }
262 }
263 pub fn map<J: Ord, C, G, X, E>(
264 self,
265 cx: &mut X,
266 ident: &mut (dyn FnMut(&mut X, I) -> Result<J, E> + '_),
267 block_: &mut (dyn FnMut(&mut X, B) -> Result<C, E> + '_),
268 fun: &mut (dyn FnMut(&mut X, F) -> Result<G, E> + '_),
269 ) -> Result<SValue<J, C, G>, E> {
270 Ok(match self {
271 SValue::Param { block, idx, ty } => SValue::Param {
272 block: block_(cx, block)?,
273 idx,
274 ty,
275 },
276 SValue::Item { item, span } => SValue::Item {
277 item: item.map2(cx, ident, fun)?,
278 span,
279 },
280 SValue::Assign { target, val } => SValue::Assign {
281 target: target.map(&mut |a| ident(cx, a))?,
282 val: ident(cx, val)?,
283 },
284 SValue::LoadId(a) => SValue::LoadId(a),
285 SValue::StoreId { target, val } => SValue::StoreId {
286 target,
287 val: ident(cx, val)?,
288 },
289 SValue::Benc(i) => SValue::Benc(ident(cx, i)?),
290 })
291 }
292 pub fn vals_ref<'a>(&'a self) -> Box<dyn Iterator<Item = &'a I> + 'a> {
293 match self {
294 SValue::Param { block, idx, ty } => Box::new(empty()),
295 SValue::Item { item, span } => Box::new(item.refs()),
296 SValue::Assign { target, val } => {
297 let v = once(val);
298 let w: Box<dyn Iterator<Item = &I> + '_> = match target {
299 LId::Id { id } => todo!(),
300 LId::Member { obj, mem } => Box::new([obj, &mem[0]].into_iter()),
301 _ => todo!(),
302 };
303 Box::new(v.chain(w))
304 }
305 SValue::LoadId(_) => Box::new(empty()),
306 SValue::StoreId { target, val } => Box::new(once(val)),
307 SValue::Benc(a) => Box::new(once(a)),
308 }
309 }
310 pub fn vals_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut I> + 'a> {
311 match self {
312 SValue::Param { block, idx, ty } => Box::new(empty()),
313 SValue::Item { item, span } => item.refs_mut(),
314 SValue::Assign { target, val } => {
315 let v = once(val);
316 let w: Box<dyn Iterator<Item = &mut I> + '_> = match target {
317 LId::Id { id } => todo!(),
318 LId::Member { obj, mem } => Box::new([obj, &mut mem[0]].into_iter()),
319 _ => todo!(),
320 };
321 Box::new(v.chain(w))
322 }
323 SValue::LoadId(_) => Box::new(empty()),
324 SValue::StoreId { target, val } => Box::new(once(val)),
325 SValue::Benc(a) => Box::new(once(a)),
326 }
327 }
328}
329#[repr(transparent)]
330#[derive(Clone, Debug)]
331pub struct SValueW { pub value: SValue }
332impl From<SValue> for SValueW {
333 fn from(value: SValue) -> Self {
334 Self { value }
335 }
336}
337impl From<SValueW> for SValue {
338 fn from(value: SValueW) -> Self {
339 value.value
340 }
341}
342#[derive(Clone, Debug)]
343#[non_exhaustive]
344pub enum SCatch<I = Id<SValueW>, B = Id<SBlock>> {
345 Throw,
346 Just { target: STarget<I, B> },
347}
348impl<I, B> Default for SCatch<I, B> {
349 fn default() -> Self {
350 Self::Throw
351 }
352}
353#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
354pub struct STarget<I = Id<SValueW>, B = Id<SBlock>> {
355 pub block: B,
356 pub args: Vec<I>,
357}
358#[derive(Clone, Debug)]
359#[non_exhaustive]
360pub enum STerm<I = Id<SValueW>, B = Id<SBlock>> {
361 Throw(I),
362 Return(Option<I>),
363 Jmp(STarget<I, B>),
364 CondJmp {
365 cond: I,
366 if_true: STarget<I, B>,
367 if_false: STarget<I, B>,
368 },
369 Switch {
370 x: I,
371 blocks: Vec<(I, STarget<I, B>)>,
372 default: STarget<I, B>,
373 },
374 Default,
375}
376impl<I, B> Default for STerm<I, B> {
377 fn default() -> Self {
378 Self::Default
379 }
380}
381impl SCfg {
382 pub fn add_blockparam(&mut self, k: Id<SBlock>) -> Id<SValueW> {
383 let val = SValue::Param {
384 block: k,
385 idx: self.blocks[k].params.len(),
386 ty: (),
387 };
388 let val = self.values.alloc(val.into());
389 self.blocks[k].params.push((val, ()));
390 return val;
391 }
392}
393
394#[non_exhaustive]
395pub struct Trans {
396 pub map: BTreeMap<Id<TBlock>, Id<SBlock>>,
397 pub all: BTreeSet<Ident>,
398 pub undef: Id<SValueW>,
399}
400impl Trans {
401 pub fn from_undef(undef: Id<SValueW>) -> Self {
402 Self {
403 map: Default::default(),
404 all: Default::default(),
405 undef: undef,
406 }
407 }
408 pub fn apply_shim(
409 &self,
410 o: &mut SCfg,
411 state: &BTreeMap<Ident, (Id<SValueW>, ValFlags)>,
412 s: &Option<(Id<SBlock>, Vec<Ident>)>,
413 x: Id<SBlock>,
414 ) {
415 let Some((a, b)) = s else {
416 o.blocks[x].postcedent.catch = SCatch::Throw;
417 return;
418 };
419 let k = SCatch::Just {
420 target: STarget {
421 block: *a,
422 args: b
423 .iter()
424 .filter_map(|x| state.get(x))
425 .map(|a| &a.0)
426 .cloned()
427 .collect(),
428 },
429 };
430 o.blocks[x].postcedent.catch = k;
431 }
432 pub fn load(
433 &self,
434 state: &BTreeMap<Ident, (Id<SValueW>, ValFlags)>,
435 i: &TCfg,
436 o: &mut SCfg,
437 t: Id<SBlock>,
438 a: Ident,
439 cache: &BTreeMap<Ident, Id<SValueW>>,
440 ) -> Id<SValueW> {
441 if let Some(k) = cache.get(&a) {
442 return *k;
443 }
444 let x = match state.get(&a).cloned() {
445 Some(b) => b.0,
446 None => {
447 let v = o.values.alloc(SValue::LoadId(a).into());
448 o.blocks[t].stmts.push(v);
449 return v;
450 }
451 };
452 if let Some(ty) = i.type_annotations.get(&a).cloned() {
453 o.ts.insert(x, ty);
454 };
455 x
456 }
457 pub fn trans(&mut self, i: &TCfg, o: &mut SCfg, k: Id<TBlock>) -> anyhow::Result<Id<SBlock>> {
458 loop {
459 if let Some(a) = self.map.get(&k) {
460 return Ok(*a);
461 }
462 let mut t = o.blocks.alloc(SBlock {
463 params: vec![],
464 stmts: vec![],
465 postcedent: SPostcedent::default(),
466 });
467 self.map.insert(k, t);
468 let shim: Option<(Id<SBlock>, Vec<Ident>)> = match &i.blocks[k].catch {
469 swc_tac::TCatch::Throw => None,
470 swc_tac::TCatch::Jump { pat, k } => {
471 let a = o.blocks.alloc(SBlock {
472 params: vec![],
473 stmts: vec![],
474 postcedent: SPostcedent::default(),
475 });
476 let mut state2 = once(pat.clone())
477 .chain(self.all.iter().filter(|a| *a != pat).cloned())
478 .collect::<Vec<_>>();
479 let mut v = state2
480 .iter()
481 .cloned()
482 .map(|a2| (a2, o.add_blockparam(a)))
483 .collect::<BTreeMap<_, _>>();
484 let p = self
485 .all
486 .iter()
487 .filter_map(|x| v.get(x))
488 .cloned()
489 .collect::<Vec<_>>();
490 let t = STerm::Jmp(STarget {
491 block: self.trans(i, o, *k)?,
492 args: p,
493 });
494 o.blocks[a].postcedent.term = t;
495 Some((a, state2))
496 }
497 };
498 let mut state = self
499 .all
500 .iter()
501 .map(|a| (a.clone(), (o.add_blockparam(t), ValFlags::all())))
502 .collect::<BTreeMap<_, _>>();
503 self.apply_shim(o, &state, &shim, t);
504 let mut cache = BTreeMap::new();
505 for TStmt {
506 left: a,
507 flags,
508 right: b,
509 span: s,
510 } in i.blocks[k].stmts.iter()
511 {
512 let mut b = b.clone();
513 if let Item::Call { callee, args } = &mut b {
514 if let TCallee::Val(v) = callee {
515 if !i.blocks.iter().any(|k| {
516 k.1.stmts.iter().any(|a| match &a.left {
517 LId::Id { id } => id == v,
518 _ => false,
519 })
520 }) {
521 *callee = TCallee::Static(v.clone());
522 }
523 }
524 }
525 let b = b.map2::<_, _, anyhow::Error, ()>(
526 &mut (),
527 &mut |_, a| Ok(self.load(&state, i, o, t, a, &cache)),
528 &mut |_, b| b.try_into(),
529 )?;
530 let b = o.values.alloc(
531 SValue::Item {
532 item: b,
533 span: Some(*s),
534 }
535 .into(),
536 );
537 o.blocks[t].stmts.push(b);
538 let flags = match a.clone() {
539 LId::Id { id } => match state.get_mut(&id) {
540 Some((a, f)) => {
541 *f &= *flags;
542 let f = *f;
543 *a = b;
544 if !f.contains(ValFlags::SSA_LIKE) {
545 let u = o.blocks.alloc(SBlock {
546 params: vec![],
547 stmts: vec![],
548 postcedent: Default::default(),
549 });
550 self.apply_shim(o, &state, &shim, u);
551 o.blocks[t].postcedent.term = STerm::Jmp(STarget {
552 block: u,
553 args: vec![],
554 });
555 t = u;
556 }
557 Some(f)
558 }
559 None => {
560 cache.insert(id.clone(), b);
561 let c = o.values.alloc(
562 SValue::StoreId {
563 target: id.clone(),
564 val: b,
565 }
566 .into(),
567 );
568 o.blocks[t].stmts.push(c);
569 None
570 }
571 },
572 a => {
573 let c = a
576 .map::<_, Infallible>(
577 &mut |a| Ok(self.load(&state, i, o, t, a, &cache)),
578 )
579 .unwrap();
580 let c = o.values.alloc(SValue::Assign { target: c, val: b }.into());
581 o.blocks[t].stmts.push(c);
582 None
583 }
584 };
585 }
586 let params = self
587 .all
588 .iter()
589 .filter_map(|a| state.get(a))
590 .map(|a| &a.0)
591 .cloned()
592 .collect::<Vec<_>>();
593 let term = match &i.blocks[k].term {
594 swc_tac::TTerm::Return(ident) => match ident.as_ref() {
595 None => STerm::Return(None),
596 Some(a) => STerm::Return(Some(self.load(&state, i, o, t, a.clone(), &cache))),
597 },
598 swc_tac::TTerm::Throw(ident) => {
599 STerm::Throw(self.load(&state, i, o, t, ident.clone(), &cache))
600 }
601 swc_tac::TTerm::Jmp(id) => {
602 let id = self.trans(i, o, *id)?;
603 STerm::Jmp(STarget {
604 block: id,
605 args: params,
606 })
607 }
608 swc_tac::TTerm::CondJmp {
609 cond,
610 if_true,
611 if_false,
612 } => {
613 let if_true = self.trans(i, o, *if_true)?;
614 let if_true = STarget {
615 block: if_true,
616 args: params.clone(),
617 };
618 let if_false = self.trans(i, o, *if_false)?;
619 let if_false = STarget {
620 block: if_false,
621 args: params,
622 };
623 let cond = self.load(&state, i, o, t, cond.clone(), &cache);
624 STerm::CondJmp {
625 cond,
626 if_true,
627 if_false,
628 }
629 }
630 swc_tac::TTerm::Switch { x, blocks, default } => {
631 let x = self.load(&state, i, o, t, x.clone(), &cache);
632 let blocks = blocks
633 .iter()
634 .map(|(a, b)| {
635 let c = self.trans(i, o, *b)?;
636 let d = self.load(&state, i, o, t, a.clone(), &cache);
637 Ok((
638 d,
639 STarget {
640 block: c,
641 args: params.clone(),
642 },
643 ))
644 })
645 .collect::<anyhow::Result<Vec<_>>>()?;
646 let default = self.trans(i, o, *default)?;
647 let default = STarget {
648 block: default,
649 args: params,
650 };
651 STerm::Switch { x, blocks, default }
652 }
653 swc_tac::TTerm::Default => STerm::Default,
654 };
655 o.blocks[t].postcedent.term = term;
656 }
657 }
658}