Skip to content

Commit

Permalink
pulley: Add branch-with-compare-against-immediate (#9863)
Browse files Browse the repository at this point in the history
This commit adds a large number of new `br_if_x*` instructions which
compare with an immediate instead of comparing two registers. This is
pretty common in wasm/compiled code where, for example, loop upper
bounds are often constants. This helps compress code slightly while
fusing more instructions together.

The main cost of this is that the number of opcodes added here is quite
large. Like with previous immediate-taking opcodes both 8 and 32-bit
variants of immediates are added for all comparisons. Additionally
unlike the previous set of branch-and-compare instructions it's required
to add instructions for `>` and `>=` because the operands cannot be
swapped to invert the condition, further increasing the number of
opcodes added.

This is a mild size reduction on `spidermonkey.cwasm` from 29M to 28M
but it's mostly expected to be a performance win for interpreted loops.
  • Loading branch information
alexcrichton authored Dec 19, 2024
1 parent 1e4c470 commit 4178766
Show file tree
Hide file tree
Showing 10 changed files with 653 additions and 62 deletions.
22 changes: 22 additions & 0 deletions cranelift/codegen/src/isa/pulley_shared/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,35 @@
(IfXult32 (src1 XReg) (src2 XReg))
(IfXulteq32 (src1 XReg) (src2 XReg))

(IfXeq32I32 (src1 XReg) (src2 i32))
(IfXneq32I32 (src1 XReg) (src2 i32))
(IfXslt32I32 (src1 XReg) (src2 i32))
(IfXslteq32I32 (src1 XReg) (src2 i32))
(IfXult32I32 (src1 XReg) (src2 u32))
(IfXulteq32I32 (src1 XReg) (src2 u32))
(IfXsgt32I32 (src1 XReg) (src2 i32))
(IfXsgteq32I32 (src1 XReg) (src2 i32))
(IfXugt32I32 (src1 XReg) (src2 u32))
(IfXugteq32I32 (src1 XReg) (src2 u32))

;; Conditionals for comparing two 64-bit registers.
(IfXeq64 (src1 XReg) (src2 XReg))
(IfXneq64 (src1 XReg) (src2 XReg))
(IfXslt64 (src1 XReg) (src2 XReg))
(IfXslteq64 (src1 XReg) (src2 XReg))
(IfXult64 (src1 XReg) (src2 XReg))
(IfXulteq64 (src1 XReg) (src2 XReg))

(IfXeq64I32 (src1 XReg) (src2 i32))
(IfXneq64I32 (src1 XReg) (src2 i32))
(IfXslt64I32 (src1 XReg) (src2 i32))
(IfXslteq64I32 (src1 XReg) (src2 i32))
(IfXult64I32 (src1 XReg) (src2 u32))
(IfXulteq64I32 (src1 XReg) (src2 u32))
(IfXsgt64I32 (src1 XReg) (src2 i32))
(IfXsgteq64I32 (src1 XReg) (src2 i32))
(IfXugt64I32 (src1 XReg) (src2 u32))
(IfXugteq64I32 (src1 XReg) (src2 u32))
)
)

Expand Down
194 changes: 193 additions & 1 deletion cranelift/codegen/src/isa/pulley_shared/inst/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,34 @@ impl Cond {
collector.reg_use(src1);
collector.reg_use(src2);
}

Cond::IfXeq32I32 { src1, src2 }
| Cond::IfXneq32I32 { src1, src2 }
| Cond::IfXslt32I32 { src1, src2 }
| Cond::IfXslteq32I32 { src1, src2 }
| Cond::IfXsgt32I32 { src1, src2 }
| Cond::IfXsgteq32I32 { src1, src2 }
| Cond::IfXeq64I32 { src1, src2 }
| Cond::IfXneq64I32 { src1, src2 }
| Cond::IfXslt64I32 { src1, src2 }
| Cond::IfXslteq64I32 { src1, src2 }
| Cond::IfXsgt64I32 { src1, src2 }
| Cond::IfXsgteq64I32 { src1, src2 } => {
collector.reg_use(src1);
let _: &mut i32 = src2;
}

Cond::IfXult32I32 { src1, src2 }
| Cond::IfXulteq32I32 { src1, src2 }
| Cond::IfXugt32I32 { src1, src2 }
| Cond::IfXugteq32I32 { src1, src2 }
| Cond::IfXult64I32 { src1, src2 }
| Cond::IfXulteq64I32 { src1, src2 }
| Cond::IfXugt64I32 { src1, src2 }
| Cond::IfXugteq64I32 { src1, src2 } => {
collector.reg_use(src1);
let _: &mut u32 = src2;
}
}
}

Expand All @@ -263,7 +291,7 @@ impl Cond {
/// Note that the offset encoded to jump by is filled in as 0 and it's
/// assumed `MachBuffer` will come back and clean it up.
pub fn encode(&self, sink: &mut impl Extend<u8>) {
match self {
match *self {
Cond::If32 { reg } => encode::br_if32(sink, reg, 0),
Cond::IfNot32 { reg } => encode::br_if_not32(sink, reg, 0),
Cond::IfXeq32 { src1, src2 } => encode::br_if_xeq32(sink, src1, src2, 0),
Expand All @@ -278,6 +306,88 @@ impl Cond {
Cond::IfXslteq64 { src1, src2 } => encode::br_if_xslteq64(sink, src1, src2, 0),
Cond::IfXult64 { src1, src2 } => encode::br_if_xult64(sink, src1, src2, 0),
Cond::IfXulteq64 { src1, src2 } => encode::br_if_xulteq64(sink, src1, src2, 0),

Cond::IfXeq32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xeq32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xeq32_i32(sink, src1, src2, 0),
},
Cond::IfXneq32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xneq32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xneq32_i32(sink, src1, src2, 0),
},
Cond::IfXslt32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xslt32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xslt32_i32(sink, src1, src2, 0),
},
Cond::IfXslteq32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xslteq32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xslteq32_i32(sink, src1, src2, 0),
},
Cond::IfXsgt32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xsgt32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xsgt32_i32(sink, src1, src2, 0),
},
Cond::IfXsgteq32I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xsgteq32_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xsgteq32_i32(sink, src1, src2, 0),
},
Cond::IfXult32I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xult32_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xult32_u32(sink, src1, src2, 0),
},
Cond::IfXulteq32I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xulteq32_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xulteq32_u32(sink, src1, src2, 0),
},
Cond::IfXugt32I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xugt32_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xugt32_u32(sink, src1, src2, 0),
},
Cond::IfXugteq32I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xugteq32_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xugteq32_u32(sink, src1, src2, 0),
},

Cond::IfXeq64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xeq64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xeq64_i32(sink, src1, src2, 0),
},
Cond::IfXneq64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xneq64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xneq64_i32(sink, src1, src2, 0),
},
Cond::IfXslt64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xslt64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xslt64_i32(sink, src1, src2, 0),
},
Cond::IfXslteq64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xslteq64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xslteq64_i32(sink, src1, src2, 0),
},
Cond::IfXsgt64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xsgt64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xsgt64_i32(sink, src1, src2, 0),
},
Cond::IfXsgteq64I32 { src1, src2 } => match i8::try_from(src2) {
Ok(src2) => encode::br_if_xsgteq64_i8(sink, src1, src2, 0),
Err(_) => encode::br_if_xsgteq64_i32(sink, src1, src2, 0),
},
Cond::IfXult64I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xult64_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xult64_u32(sink, src1, src2, 0),
},
Cond::IfXulteq64I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xulteq64_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xulteq64_u32(sink, src1, src2, 0),
},
Cond::IfXugt64I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xugt64_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xugt64_u32(sink, src1, src2, 0),
},
Cond::IfXugteq64I32 { src1, src2 } => match u8::try_from(src2) {
Ok(src2) => encode::br_if_xugteq64_u8(sink, src1, src2, 0),
Err(_) => encode::br_if_xugteq64_u32(sink, src1, src2, 0),
},
}
}

Expand Down Expand Up @@ -325,6 +435,28 @@ impl Cond {
src1: src2,
src2: src1,
},

Cond::IfXeq32I32 { src1, src2 } => Cond::IfXneq32I32 { src1, src2 },
Cond::IfXneq32I32 { src1, src2 } => Cond::IfXeq32I32 { src1, src2 },
Cond::IfXslt32I32 { src1, src2 } => Cond::IfXsgteq32I32 { src1, src2 },
Cond::IfXslteq32I32 { src1, src2 } => Cond::IfXsgt32I32 { src1, src2 },
Cond::IfXult32I32 { src1, src2 } => Cond::IfXugteq32I32 { src1, src2 },
Cond::IfXulteq32I32 { src1, src2 } => Cond::IfXugt32I32 { src1, src2 },
Cond::IfXsgt32I32 { src1, src2 } => Cond::IfXslteq32I32 { src1, src2 },
Cond::IfXsgteq32I32 { src1, src2 } => Cond::IfXslt32I32 { src1, src2 },
Cond::IfXugt32I32 { src1, src2 } => Cond::IfXulteq32I32 { src1, src2 },
Cond::IfXugteq32I32 { src1, src2 } => Cond::IfXult32I32 { src1, src2 },

Cond::IfXeq64I32 { src1, src2 } => Cond::IfXneq64I32 { src1, src2 },
Cond::IfXneq64I32 { src1, src2 } => Cond::IfXeq64I32 { src1, src2 },
Cond::IfXslt64I32 { src1, src2 } => Cond::IfXsgteq64I32 { src1, src2 },
Cond::IfXslteq64I32 { src1, src2 } => Cond::IfXsgt64I32 { src1, src2 },
Cond::IfXult64I32 { src1, src2 } => Cond::IfXugteq64I32 { src1, src2 },
Cond::IfXulteq64I32 { src1, src2 } => Cond::IfXugt64I32 { src1, src2 },
Cond::IfXsgt64I32 { src1, src2 } => Cond::IfXslteq64I32 { src1, src2 },
Cond::IfXsgteq64I32 { src1, src2 } => Cond::IfXslt64I32 { src1, src2 },
Cond::IfXugt64I32 { src1, src2 } => Cond::IfXulteq64I32 { src1, src2 },
Cond::IfXugteq64I32 { src1, src2 } => Cond::IfXult64I32 { src1, src2 },
}
}
}
Expand Down Expand Up @@ -370,6 +502,66 @@ impl fmt::Display for Cond {
Cond::IfXulteq64 { src1, src2 } => {
write!(f, "if_xulteq64 {}, {}", reg_name(**src1), reg_name(**src2))
}
Cond::IfXeq32I32 { src1, src2 } => {
write!(f, "if_xeq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXneq32I32 { src1, src2 } => {
write!(f, "if_xneq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXslt32I32 { src1, src2 } => {
write!(f, "if_xslt32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXslteq32I32 { src1, src2 } => {
write!(f, "if_xslteq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXsgt32I32 { src1, src2 } => {
write!(f, "if_xsgt32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXsgteq32I32 { src1, src2 } => {
write!(f, "if_xsgteq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXult32I32 { src1, src2 } => {
write!(f, "if_xult32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXulteq32I32 { src1, src2 } => {
write!(f, "if_xulteq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXugt32I32 { src1, src2 } => {
write!(f, "if_xugt32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXugteq32I32 { src1, src2 } => {
write!(f, "if_xugteq32_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXeq64I32 { src1, src2 } => {
write!(f, "if_xeq64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXneq64I32 { src1, src2 } => {
write!(f, "if_xneq64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXslt64I32 { src1, src2 } => {
write!(f, "if_xslt64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXslteq64I32 { src1, src2 } => {
write!(f, "if_xslteq64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXsgt64I32 { src1, src2 } => {
write!(f, "if_xsgt64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXsgteq64I32 { src1, src2 } => {
write!(f, "if_xsgteq64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXult64I32 { src1, src2 } => {
write!(f, "if_xult64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXulteq64I32 { src1, src2 } => {
write!(f, "if_xulteq64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXugt64I32 { src1, src2 } => {
write!(f, "if_xugt64_i32 {}, {src2}", reg_name(**src1))
}
Cond::IfXugteq64I32 { src1, src2 } => {
write!(f, "if_xugteq64_i32 {}, {src2}", reg_name(**src1))
}
}
}
}
88 changes: 88 additions & 0 deletions cranelift/codegen/src/isa/pulley_shared/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@
(rule (lower_cond_icmp32 (IntCC.UnsignedGreaterThan) a b) (Cond.IfXult32 b a))
(rule (lower_cond_icmp32 (IntCC.UnsignedGreaterThanOrEqual) a b) (Cond.IfXulteq32 b a))

(rule 1 (lower_cond_icmp32 (IntCC.Equal) a (i32_from_iconst b))
(Cond.IfXeq32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.NotEqual) a (i32_from_iconst b))
(Cond.IfXneq32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.SignedLessThan) a (i32_from_iconst b))
(Cond.IfXslt32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.SignedLessThanOrEqual) a (i32_from_iconst b))
(Cond.IfXslteq32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.SignedGreaterThan) a (i32_from_iconst b))
(Cond.IfXsgt32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.SignedGreaterThanOrEqual) a (i32_from_iconst b))
(Cond.IfXsgteq32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.UnsignedLessThan) a (u32_from_iconst b))
(Cond.IfXult32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.UnsignedLessThanOrEqual) a (u32_from_iconst b))
(Cond.IfXulteq32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.UnsignedGreaterThan) a (u32_from_iconst b))
(Cond.IfXugt32I32 a b))
(rule 1 (lower_cond_icmp32 (IntCC.UnsignedGreaterThanOrEqual) a (u32_from_iconst b))
(Cond.IfXugteq32I32 a b))

(decl lower_cond_icmp64 (IntCC Value Value) Cond)
(rule (lower_cond_icmp64 (IntCC.Equal) a b) (Cond.IfXeq64 a b))
(rule (lower_cond_icmp64 (IntCC.NotEqual) a b) (Cond.IfXneq64 a b))
Expand All @@ -48,6 +69,27 @@
(rule (lower_cond_icmp64 (IntCC.UnsignedGreaterThan) a b) (Cond.IfXult64 b a))
(rule (lower_cond_icmp64 (IntCC.UnsignedGreaterThanOrEqual) a b) (Cond.IfXulteq64 b a))

(rule 1 (lower_cond_icmp64 (IntCC.Equal) a (i32_from_iconst b))
(Cond.IfXeq64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.NotEqual) a (i32_from_iconst b))
(Cond.IfXneq64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.SignedLessThan) a (i32_from_iconst b))
(Cond.IfXslt64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.SignedLessThanOrEqual) a (i32_from_iconst b))
(Cond.IfXslteq64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.SignedGreaterThan) a (i32_from_iconst b))
(Cond.IfXsgt64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.SignedGreaterThanOrEqual) a (i32_from_iconst b))
(Cond.IfXsgteq64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.UnsignedLessThan) a (u32_from_iconst b))
(Cond.IfXult64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.UnsignedLessThanOrEqual) a (u32_from_iconst b))
(Cond.IfXulteq64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.UnsignedGreaterThan) a (u32_from_iconst b))
(Cond.IfXugt64I32 a b))
(rule 1 (lower_cond_icmp64 (IntCC.UnsignedGreaterThanOrEqual) a (u32_from_iconst b))
(Cond.IfXugteq64I32 a b))

;; The main control-flow-lowering term: takes a control-flow instruction and
;; target(s) and emits the necessary instructions.
(decl partial lower_branch (Inst MachLabelSlice) Unit)
Expand Down Expand Up @@ -880,6 +922,52 @@
(rule (emit_cond (Cond.IfXult64 src1 src2)) (pulley_xult64 src1 src2))
(rule (emit_cond (Cond.IfXulteq64 src1 src2)) (pulley_xulteq64 src1 src2))

(rule (emit_cond (Cond.IfXeq32I32 src1 src2))
(pulley_xeq32 src1 (imm $I32 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXneq32I32 src1 src2))
(pulley_xneq32 src1 (imm $I32 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXslt32I32 src1 src2))
(pulley_xslt32 src1 (imm $I32 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXslteq32I32 src1 src2))
(pulley_xslteq32 src1 (imm $I32 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXult32I32 src1 src2))
(pulley_xult32 src1 (imm $I32 (u32_as_u64 src2))))
(rule (emit_cond (Cond.IfXulteq32I32 src1 src2))
(pulley_xulteq32 src1 (imm $I32 (u32_as_u64 src2))))

;; Note the operand swaps here
(rule (emit_cond (Cond.IfXsgt32I32 src1 src2))
(pulley_xslteq32 (imm $I32 (i64_as_u64 (i32_as_i64 src2))) src1))
(rule (emit_cond (Cond.IfXsgteq32I32 src1 src2))
(pulley_xslt32 (imm $I32 (i64_as_u64 (i32_as_i64 src2))) src1))
(rule (emit_cond (Cond.IfXugt32I32 src1 src2))
(pulley_xulteq32 (imm $I32 (u32_as_u64 src2)) src1))
(rule (emit_cond (Cond.IfXugteq32I32 src1 src2))
(pulley_xult32 (imm $I32 (u32_as_u64 src2)) src1))

(rule (emit_cond (Cond.IfXeq64I32 src1 src2))
(pulley_xeq64 src1 (imm $I64 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXneq64I32 src1 src2))
(pulley_xneq64 src1 (imm $I64 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXslt64I32 src1 src2))
(pulley_xslt64 src1 (imm $I64 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXslteq64I32 src1 src2))
(pulley_xslteq64 src1 (imm $I64 (i64_as_u64 (i32_as_i64 src2)))))
(rule (emit_cond (Cond.IfXult64I32 src1 src2))
(pulley_xult64 src1 (imm $I64 (u32_as_u64 src2))))
(rule (emit_cond (Cond.IfXulteq64I32 src1 src2))
(pulley_xulteq64 src1 (imm $I64 (u32_as_u64 src2))))

;; Note the operand swaps here
(rule (emit_cond (Cond.IfXsgt64I32 src1 src2))
(pulley_xslteq64 (imm $I64 (i64_as_u64 (i32_as_i64 src2))) src1))
(rule (emit_cond (Cond.IfXsgteq64I32 src1 src2))
(pulley_xslt64 (imm $I64 (i64_as_u64 (i32_as_i64 src2))) src1))
(rule (emit_cond (Cond.IfXugt64I32 src1 src2))
(pulley_xulteq64 (imm $I64 (u32_as_u64 src2)) src1))
(rule (emit_cond (Cond.IfXugteq64I32 src1 src2))
(pulley_xult64 (imm $I64 (u32_as_u64 src2)) src1))

;;;; Rules for `bitcast` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (has_type $F32 (bitcast _flags val @ (value_type $I32))))
Expand Down
Loading

0 comments on commit 4178766

Please sign in to comment.