initial poc

This commit is contained in:
Graham Kelly 2024-06-06 16:37:15 -04:00
commit 9f16fef6c2
18 changed files with 111073 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target

1060
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

7
Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[workspace]
members=[ "wars", "wars-macro","wars-rt", "wars-test"]
resolver="2"
[workspace.dependencies]
portal-pc-waffle = {git="https://github.com/portal-co/waffle-.git",branch="pr/changes2"}

19
wars-macro/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "wars-macro"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
expander = {version="2.1.0",features=["pretty"]}
proc-macro2 = "1.0.85"
quote = "1.0.36"
syn = "2.0.66"
wars = { version = "0.1.0", path = "../wars" }
wars-rt = { version = "0.1.0", path = "../wars-rt" }
wat = "1.209.1"
[lib]
proc-macro = true

94
wars-macro/src/lib.rs Normal file
View file

@ -0,0 +1,94 @@
use std::sync::Arc;
use expander::{Edition, Expander};
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse::Parse, parse_macro_input, Ident, Path, Token};
use wars::Opts;
struct O {
pub crate_path: syn::Path,
pub module: Vec<u8>,
pub name: Ident,
// pub cfg: Arc<dyn ImportCfg>,
}
// struct NoopCfg {}
// impl ImportCfg for NoopCfg {
// fn import(&self, module: &str, name: &str) -> proc_macro2::TokenStream {
// quote! {
// compile_error!("import used")
// }
// }
// }
impl Parse for O {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut o = O {
crate_path: syn::parse(quote! {::wars_rt}.into())?,
module: vec![],
name: Ident::new("Wars", Span::call_site()),
// cfg: Arc::new(NoopCfg {}),
};
while input.lookahead1().peek(Ident) {
let i: Ident = input.parse()?;
let _eq: Token![=] = input.parse()?;
match i.to_string().as_str() {
"crate_path" => {
let s: syn::LitStr = input.parse()?;
o.crate_path = s.parse()?;
}
"env" => {
let s: syn::LitStr = input.parse()?;
let sp = s.span();
let s = std::env::var(s.value()).map_err(|x| syn::Error::new(sp, x))?;
let x = std::fs::read(s).map_err(|x| syn::Error::new(sp, x))?;
o.module = x;
}
"file" => {
let s: syn::LitStr = input.parse()?;
let sp = s.span();
let x = std::fs::read(s.value()).map_err(|x| syn::Error::new(sp, x))?;
o.module = x;
}
"inline" => {
let s: syn::LitStr = input.parse()?;
let sp = s.span();
let x = wat::parse_str(&s.value()).map_err(|x| syn::Error::new(sp, x))?;
o.module = x;
}
"name" => {
o.name = input.parse()?;
}
_ => return Err(syn::Error::new(i.span(), "unexpected type")),
};
let _comma: Token![,] = input.parse()?;
}
// while input.lookahead1().peek(Token![;]){
// let _semi: Token![;] = input.parse()?;
// }
Ok(o)
}
}
#[proc_macro]
pub fn wars(a: TokenStream) -> TokenStream {
let o = parse_macro_input!(a as O);
let x = wars::go(&Opts {
crate_path: o.crate_path,
module: o.module,
name: o.name,
// cfg: o.cfg,
});
let expanded = Expander::new("wars")
.add_comment("This is generated code!".to_owned())
.fmt(Edition::_2021)
.verbose(true)
// common way of gating this, by making it part of the default feature set
.dry(cfg!(feature = "no-file-expansion"))
.write_to_out_dir(x.clone())
.unwrap_or_else(|e| {
eprintln!("Failed to write to file: {:?}", e);
x
});
expanded.into()
}

18
wars-macro/tests/a.rs Normal file
View file

@ -0,0 +1,18 @@
wars_macro::wars!(
inline = "
(module
(func (result i32) call 2 call 1)
(func (result i32)
i32.const 0
(if
(then i32.const 0
return_call 0
)
(else i32.const 1)
)
)
(func)
)
",
);

7
wars-macro/tests/b.rs Normal file
View file

@ -0,0 +1,7 @@
wars_macro::wars!(
file = "target/wasm32-unknown-unknown/debug/wars_test.wasm",
);
fn x<A: Wars>(){
}

54108
wars-macro/tests/out.rs.x Normal file

File diff suppressed because it is too large Load diff

54224
wars-macro/tests/out.rs.y Normal file

File diff suppressed because it is too large Load diff

14
wars-rt/Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "wars-rt"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.86"
castaway = "0.2.2"
derive_more = "0.99.17"
paste = "1.0.15"
tramp = "0.3.0"
tuple_list = "0.1.3"

202
wars-rt/src/func.rs Normal file
View file

@ -0,0 +1,202 @@
use std::sync::Arc;
use anyhow::Context;
use tramp::{tramp, BorrowRec, Thunk};
pub trait CtxSpec: Sized {
type ExternRef: Clone;
}
pub enum Value<C: CtxSpec> {
I32(u32),
I64(u64),
F32(f32),
F64(f64),
FunRef(
Arc<
dyn for<'a> Fn(
&'a mut C,
Vec<Value<C>>,
) -> tramp::BorrowRec<'a, anyhow::Result<Vec<Value<C>>>>
+ 'static,
>,
),
Null,
ExRef(C::ExternRef),
}
pub fn call_ref<'a, A: CoeVec<C> + 'static, B: CoeVec<C> + 'static, C: CtxSpec + 'static>(
ctx: &'a mut C,
go: Df<A,B,C>,
a: A,
) -> tramp::BorrowRec<'a, anyhow::Result<B>> {
// let go: Df<A, B, C> = cast(go);
go(ctx, a)
}
impl<C: CtxSpec> Clone for Value<C> {
fn clone(&self) -> Self {
match self {
Self::I32(arg0) => Self::I32(arg0.clone()),
Self::I64(arg0) => Self::I64(arg0.clone()),
Self::F32(arg0) => Self::F32(arg0.clone()),
Self::F64(arg0) => Self::F64(arg0.clone()),
Self::FunRef(arg0) => Self::FunRef(arg0.clone()),
Self::Null => Self::Null,
Self::ExRef(e) => Self::ExRef(e.clone()),
}
}
}
pub trait Coe<C: CtxSpec>: Sized {
fn coe(self) -> Value<C>;
fn uncoe(x: Value<C>) -> anyhow::Result<Self>;
}
pub fn cast<A: Coe<C> + 'static, B: Coe<C> + 'static, C: CtxSpec>(a: A) -> B {
let a = match castaway::cast!(a, B) {
Ok(b) => return b,
Err(a) => a,
};
B::uncoe(A::coe(a)).unwrap()
}
impl<C: CtxSpec> Coe<C> for Value<C> {
fn coe(self) -> Value<C> {
self
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
Ok(x)
}
}
impl<C: CtxSpec, D: Coe<C>> Coe<C> for Option<D> {
fn coe(self) -> Value<C> {
match self {
None => Value::Null,
Some(d) => d.coe(),
}
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
if let Value::Null = x {
return Ok(None);
}
return Ok(Some(D::uncoe(x)?));
}
}
macro_rules! coe_impl_prim {
($a:tt in $b:ident) => {
impl<C: CtxSpec> Coe<C> for $a {
fn coe(self) -> Value<C> {
Value::$b(self)
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
match x {
Value::$b(a) => Ok(a),
_ => anyhow::bail!("invalid type"),
}
}
}
};
}
coe_impl_prim!(u32 in I32);
coe_impl_prim!(u64 in I64);
coe_impl_prim!(f32 in F32);
coe_impl_prim!(f64 in F64);
pub trait CoeVec<C: CtxSpec>: Sized {
fn coe(self) -> Vec<Value<C>>;
fn uncoe(a: Vec<Value<C>>) -> anyhow::Result<Self>;
}
impl<C: CtxSpec> CoeVec<C> for () {
fn coe(self) -> Vec<Value<C>> {
vec![]
}
fn uncoe(a: Vec<Value<C>>) -> anyhow::Result<Self> {
Ok(())
}
}
impl<C: CtxSpec, A: Coe<C>, B: CoeVec<C>> CoeVec<C> for (A, B) {
fn coe(self) -> Vec<Value<C>> {
let mut a = self.1.coe();
a.push(self.0.coe());
return a;
}
fn uncoe(mut a: Vec<Value<C>>) -> anyhow::Result<Self> {
let Some(x) = a.pop() else {
anyhow::bail!("list too small")
};
let y = A::uncoe(x).context("invalid item (note coe lists are REVERSED)")?;
let z = B::uncoe(a)?;
Ok((y, z))
}
}
pub fn map_rec<'a, T: 'a, U>(
r: BorrowRec<'a, T>,
go: impl FnOnce(T) -> U + 'a,
) -> BorrowRec<'a, U> {
match r {
BorrowRec::Ret(a) => BorrowRec::Ret(go(a)),
BorrowRec::Call(t) => BorrowRec::Call(Thunk::new(move || {
let t = t.compute();
map_rec(t, go)
})),
}
}
pub type Df<A, B, C> =
Arc<dyn for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static>;
pub fn da<
A,
B,
C,
F: for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static,
>(
f: F,
) -> Df<A,B,C> {
Arc::new(f)
}
impl<C: CtxSpec + 'static, A: CoeVec<C> + 'static, B: CoeVec<C> + 'static> Coe<C> for Df<A, B, C> {
fn coe(self) -> Value<C> {
pub fn x<
C: CtxSpec,
T: for<'a> Fn(
&'a mut C,
Vec<Value<C>>,
) -> tramp::BorrowRec<'a, anyhow::Result<Vec<Value<C>>>>
+ 'static,
>(
a: T,
) -> T {
return a;
}
Value::FunRef(Arc::new(x(move |ctx, x| {
let x = match A::uncoe(x) {
Ok(x) => x,
Err(e) => return BorrowRec::Ret(Err(e)),
};
let x = self(ctx, x);
map_rec(x, |a| a.map(|b| b.coe()))
})))
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
let Value::FunRef(x) = x else {
anyhow::bail!("invalid value")
};
Ok(Arc::new(move |ctx, a| {
let v = a.coe();
let v = x(ctx, v);
map_rec(v, |a| a.and_then(B::uncoe))
}))
}
}
pub trait Call<A, B, C>:
for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static
{
fn call(&self, c: &mut C, a: A) -> anyhow::Result<B>;
}
impl<A, B, C, T: for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static>
Call<A, B, C> for T
{
fn call(&self, c: &mut C, a: A) -> anyhow::Result<B> {
tramp((self)(c, a))
}
}

203
wars-rt/src/lib.rs Normal file
View file

@ -0,0 +1,203 @@
pub mod func;
use func::CtxSpec;
pub use func::Value;
pub mod _rexport {
pub use anyhow;
pub use tramp;
pub use tuple_list;
}
macro_rules! int_ty{
($int:ty => $p:ident) => {
paste::paste!{
pub fn [<$p add>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.wrapping_add(b)))
}
pub fn [<$p mul>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.wrapping_mul(b)))
}
pub fn [<$p and>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a & b))
}
pub fn [<$p or>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a | b))
}
pub fn [<$p xor>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a ^ b))
}
pub fn [<$p shl>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a << b))
}
pub fn [<$p shru>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a >> b))
}
pub fn [<$p divu>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a / b))
}
pub fn [<$p rotl>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.rotate_left((b & 0xffffffff) as u32)))
}
pub fn [<$p clz>](a: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.leading_zeros() as $int))
}
pub fn [<$p ctz>](a: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.trailing_zeros() as $int))
}
//comparisons
pub fn [<$p eqz>](a: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a == 0{
1
}else{
0
}))
}
pub fn [<$p eq>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a == b{
1
}else{
0
}))
}
pub fn [<$p ne>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a != b{
1
}else{
0
}))
}
pub fn [<$p ltu>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a < b{
1
}else{
0
}))
}
pub fn [<$p gtu>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a > b{
1
}else{
0
}))
}
pub fn [<$p leu>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a <= b{
1
}else{
0
}))
}
pub fn [<$p geu>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
Ok(tuple_list::tuple_list!(if a >= b{
1
}else{
0
}))
}
//signed
pub fn [<$p lts>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
let a = a as $p;
let b = b as $p;
Ok(tuple_list::tuple_list!(if a < b{
1
}else{
0
}))
}
pub fn [<$p gts>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
let a = a as $p;
let b = b as $p;
Ok(tuple_list::tuple_list!(if a > b{
1
}else{
0
}))
}
pub fn [<$p les>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
let a = a as $p;
let b = b as $p;
Ok(tuple_list::tuple_list!(if a <= b{
1
}else{
0
}))
}
pub fn [<$p ges>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
let a = a as $p;
let b = b as $p;
Ok(tuple_list::tuple_list!(if a >= b{
1
}else{
0
}))
}
pub fn [<$p sub>](a: $int, b: $int) -> anyhow::Result<tuple_list::tuple_list_type!($int)> {
Ok(tuple_list::tuple_list!(a.wrapping_sub(b)))
}
//LOADS and STORES
pub fn [<$p load>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..std::mem::size_of::<$int>()];
Ok(tuple_list::tuple_list!($int::from_ne_bytes(r.try_into()?)))
}
pub fn [<$p store>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..std::mem::size_of::<$int>()];
r.copy_from_slice(&c.to_ne_bytes());
Ok(())
}
//8 BIT
pub fn [<$p load8u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a[b.try_into()?..][0];
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load8s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a[b.try_into()?..][0];
Ok(tuple_list::tuple_list!(r as i8 as $p as $int))
}
pub fn [<$p store8>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..1];
r[0] = (c & 0xff) as u8;
Ok(())
}
//16 BIT
pub fn [<$p load16u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..2];
let r = u16::from_ne_bytes(r.try_into()?);
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load16s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..2];
let r = u16::from_ne_bytes(r.try_into()?);
Ok(tuple_list::tuple_list!(r as i16 as $p as $int))
}
pub fn [<$p store16>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..2];
r.copy_from_slice(&((c & 0xff) as u16).to_ne_bytes());
Ok(())
}
//32 BIT
pub fn [<$p load32u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..4];
let r = u32::from_ne_bytes(r.try_into()?);
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load32s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..4];
let r = u32::from_ne_bytes(r.try_into()?);
Ok(tuple_list::tuple_list!(r as i32 as $p as $int))
}
pub fn [<$p store32>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..4];
r.copy_from_slice(&((c & 0xffffff) as u32).to_ne_bytes());
Ok(())
}
}
}
}
int_ty!(u32 => i32);
int_ty!(u64 => i64);
pub fn select<T>(u: u32, t: T, t2: T) -> anyhow::Result<tuple_list::tuple_list_type!(T)> {
Ok(tuple_list::tuple_list!(if u != 0 { t } else { t2 }))
}
pub fn i32wrapi64(a: u64) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
return Ok(tuple_list::tuple_list!((a & 0xffffffff) as u32));
}

11
wars-test/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "wars-test"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[lib]
crate-type = ["cdylib"]

2
wars-test/build.sh Normal file
View file

@ -0,0 +1,2 @@
cd $(dirname $0)
cargo build --target wasm32-unknown-unknown

8
wars-test/src/lib.rs Normal file
View file

@ -0,0 +1,8 @@
#[no_mangle]
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[no_mangle]
pub fn memory_op(a: &str) -> String{
return a.to_owned();
}

14
wars/Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "wars"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
portal-pc-waffle.workspace = true
proc-macro2 = "1.0.85"
quote = "1.0.36"
relooper = "0.1.0"
syn = "2.0.66"

1007
wars/src/lib.rs Normal file

File diff suppressed because it is too large Load diff

74
wars/src/unswitch.rs Normal file
View file

@ -0,0 +1,74 @@
use std::iter::empty;
use waffle::{
Block, ExportKind, Func, FuncDecl, Import, ImportKind, Memory, MemoryArg, MemoryData, Module,
Operator, Signature, SignatureData, Table, Type, Value, ValueDef,
};
use waffle::{BlockTarget, FunctionBody, Terminator};
pub fn go(f: &mut FunctionBody) {
let vz = f.arg_pool.from_iter(empty());
let tz = f.type_pool.from_iter(empty());
let ti = f.type_pool.from_iter(vec![Type::I32].into_iter());
let ia = f.add_value(ValueDef::Operator(Operator::I32Const { value: 1 }, vz, ti));
f.append_to_block(f.entry, ia);
loop {
let mut i = f.blocks.entries();
let (b, value, targets, default) = 'gather: loop {
let Some(a) = i.next() else {
drop(i);
f.recompute_edges();
return;
};
if let Terminator::Select {
value,
targets,
default,
} = a.1.clone().terminator
{
break 'gather (a.0, value, targets.clone(), default);
}
};
drop(i);
f.blocks[b].terminator = Terminator::None;
if targets.len() == 0 {
f.set_terminator(b, Terminator::Br { target: default });
continue;
}
if targets.len() == 1 {
f.set_terminator(
b,
Terminator::CondBr {
cond: value,
if_true: default,
if_false: targets[0].clone(),
},
);
continue;
}
let t2 = targets[1..].to_vec();
let n = f.add_block();
let vs = f.arg_pool.from_iter(vec![value, ia].into_iter());
let ic = f.add_value(ValueDef::Operator(Operator::I32Sub, vs, ti));
f.append_to_block(n, ic);
f.set_terminator(
n,
Terminator::Select {
value: ic,
targets: t2,
default: default,
},
);
f.set_terminator(
b,
Terminator::CondBr {
cond: value,
if_true: BlockTarget {
block: n,
args: vec![],
},
if_false: targets[0].clone(),
},
);
}
}