Skip to content

Commit c96670f

Browse files
authored
Implement more Bitcast conversions. (#872)
* Implement more `Bitcast` conversions. Implement `Bitcast` conversions between more combinations of f32, f64, pointer, and length types. * Implement more bitcast cases. * Add `variants-unioning-types` to the list of tests to skip in the csharp backend. * Factor out some additional redundancy in the rust generator.
1 parent a0c7383 commit c96670f

File tree

6 files changed

+276
-105
lines changed

6 files changed

+276
-105
lines changed

crates/c/src/lib.rs

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,41 +2183,8 @@ impl Bindgen for FunctionBindgen<'_, '_> {
21832183

21842184
Instruction::Bitcasts { casts } => {
21852185
for (cast, op) in casts.iter().zip(operands) {
2186-
let op = op;
2187-
match cast {
2188-
Bitcast::I32ToF32 | Bitcast::I64ToF32 => {
2189-
results
2190-
.push(format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op));
2191-
}
2192-
Bitcast::F32ToI32 | Bitcast::F32ToI64 => {
2193-
results
2194-
.push(format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op));
2195-
}
2196-
Bitcast::I64ToF64 => {
2197-
results.push(format!(
2198-
"((union {{ int64_t a; double b; }}){{ {} }}).b",
2199-
op
2200-
));
2201-
}
2202-
Bitcast::F64ToI64 => {
2203-
results.push(format!(
2204-
"((union {{ double a; int64_t b; }}){{ {} }}).b",
2205-
op
2206-
));
2207-
}
2208-
Bitcast::I32ToI64 | Bitcast::PToP64 => {
2209-
results.push(format!("(int64_t) {}", op));
2210-
}
2211-
Bitcast::I64ToI32 | Bitcast::P64ToP => {
2212-
results.push(format!("(int32_t) {}", op));
2213-
}
2214-
Bitcast::I64ToP64 | Bitcast::P64ToI64 => {
2215-
results.push(format!("{}", op));
2216-
}
2217-
Bitcast::I32ToP | Bitcast::PToI32 | Bitcast::None => {
2218-
results.push(op.to_string())
2219-
}
2220-
}
2186+
let op = perform_cast(op, cast);
2187+
results.push(op);
22212188
}
22222189
}
22232190

@@ -2951,6 +2918,44 @@ impl Bindgen for FunctionBindgen<'_, '_> {
29512918
}
29522919
}
29532920

2921+
fn perform_cast(op: &str, cast: &Bitcast) -> String {
2922+
match cast {
2923+
Bitcast::I32ToF32 | Bitcast::I64ToF32 => {
2924+
format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op)
2925+
}
2926+
Bitcast::F32ToI32 | Bitcast::F32ToI64 => {
2927+
format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op)
2928+
}
2929+
Bitcast::I64ToF64 => {
2930+
format!("((union {{ int64_t a; double b; }}){{ {} }}).b", op)
2931+
}
2932+
Bitcast::F64ToI64 => {
2933+
format!("((union {{ double a; int64_t b; }}){{ {} }}).b", op)
2934+
}
2935+
Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => {
2936+
format!("(int64_t) {}", op)
2937+
}
2938+
Bitcast::I64ToI32 | Bitcast::I64ToL | Bitcast::P64ToP => {
2939+
format!("(int32_t) {}", op)
2940+
}
2941+
Bitcast::I64ToP64 | Bitcast::P64ToI64 => {
2942+
format!("{}", op)
2943+
}
2944+
Bitcast::I32ToP
2945+
| Bitcast::PToI32
2946+
| Bitcast::I32ToL
2947+
| Bitcast::LToI32
2948+
| Bitcast::LToP
2949+
| Bitcast::PToL
2950+
| Bitcast::None => op.to_string(),
2951+
2952+
Bitcast::Sequence(sequence) => {
2953+
let [first, second] = &**sequence;
2954+
perform_cast(&perform_cast(op, first), second)
2955+
}
2956+
}
2957+
}
2958+
29542959
#[derive(Default, Clone, Copy)]
29552960
enum SourceType {
29562961
#[default]

crates/core/src/abi.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -536,23 +536,36 @@ pub enum Bitcast {
536536
I64ToI32,
537537
I64ToF32,
538538

539-
// PointerOrI64<->Pointer conversions. These preserve provenance.
539+
// PointerOrI64 conversions. These preserve provenance when the source
540+
// or destination is a pointer value.
540541
//
541542
// These are used when pointer values are being stored in
542-
// (PToP64) and loaded out of (P64ToP) PointerOrI64 values, so they
543-
// always have to preserve provenance.
543+
// (ToP64) and loaded out of (P64To) PointerOrI64 values, so they
544+
// always have to preserve provenance when the value being loaded or
545+
// stored is a pointer.
546+
P64ToI64,
547+
I64ToP64,
544548
P64ToP,
545549
PToP64,
546550

547-
// Pointer<->integer conversions. These do not preserve provenance.
551+
// Pointer<->number conversions. These do not preserve provenance.
548552
//
549-
// These are used when integer values are being stored in
550-
// (I64ToP64 and I32ToP) and loaded out of (P64ToI64 and PToI32) pointer
551-
// or PointerOrI64 values, so they never have any provenance to preserve.
552-
P64ToI64,
553-
I64ToP64,
553+
// These are used when integer or floating-point values are being stored in
554+
// (I32ToP/etc.) and loaded out of (PToI32/etc.) pointer values, so they
555+
// never have any provenance to preserve.
554556
I32ToP,
555557
PToI32,
558+
PToL,
559+
LToP,
560+
561+
// Number<->Number conversions.
562+
I32ToL,
563+
LToI32,
564+
I64ToL,
565+
LToI64,
566+
567+
// Multiple conversions in sequence.
568+
Sequence(Box<[Bitcast; 2]>),
556569

557570
None,
558571
}
@@ -1895,6 +1908,7 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast {
18951908
| (F32, F32)
18961909
| (F64, F64)
18971910
| (Pointer, Pointer)
1911+
| (PointerOrI64, PointerOrI64)
18981912
| (Length, Length) => Bitcast::None,
18991913

19001914
(I32, I64) => Bitcast::I32ToI64,
@@ -1909,19 +1923,33 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast {
19091923
(I64, F32) => Bitcast::I64ToF32,
19101924

19111925
(I64, PointerOrI64) => Bitcast::I64ToP64,
1912-
(PointerOrI64, I64) => Bitcast::P64ToI64,
19131926
(Pointer, PointerOrI64) => Bitcast::PToP64,
1927+
(_, PointerOrI64) => {
1928+
Bitcast::Sequence(Box::new([cast(from, I64), cast(I64, PointerOrI64)]))
1929+
}
1930+
1931+
(PointerOrI64, I64) => Bitcast::P64ToI64,
19141932
(PointerOrI64, Pointer) => Bitcast::P64ToP,
1933+
(PointerOrI64, _) => Bitcast::Sequence(Box::new([cast(PointerOrI64, I64), cast(I64, to)])),
19151934

19161935
(I32, Pointer) => Bitcast::I32ToP,
19171936
(Pointer, I32) => Bitcast::PToI32,
1937+
(I32, Length) => Bitcast::I32ToL,
1938+
(Length, I32) => Bitcast::LToI32,
1939+
(I64, Length) => Bitcast::I64ToL,
1940+
(Length, I64) => Bitcast::LToI64,
1941+
(Pointer, Length) => Bitcast::PToL,
1942+
(Length, Pointer) => Bitcast::LToP,
1943+
1944+
(F32, Pointer | Length) => Bitcast::Sequence(Box::new([cast(F32, I32), cast(I32, to)])),
1945+
(Pointer | Length, F32) => Bitcast::Sequence(Box::new([cast(from, I32), cast(I32, F32)])),
19181946

1919-
(Pointer | PointerOrI64 | Length, _)
1920-
| (_, Pointer | PointerOrI64 | Length)
1921-
| (F32, F64)
1947+
(F32, F64)
19221948
| (F64, F32)
19231949
| (F64, I32)
1924-
| (I32, F64) => {
1950+
| (I32, F64)
1951+
| (Pointer | Length, I64 | F64)
1952+
| (I64 | F64, Pointer | Length) => {
19251953
unreachable!("Don't know how to bitcast from {:?} to {:?}", from, to);
19261954
}
19271955
}

crates/csharp/tests/codegen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ macro_rules! codegen_test {
5656
"unused-import",
5757
"use-across-interfaces",
5858
"variants",
59+
"variants-unioning-types",
5960
"worlds-with-types",
6061
"zero-size-tuple",
6162
"go_params",

crates/rust/src/lib.rs

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,48 +1327,64 @@ fn int_repr(repr: Int) -> &'static str {
13271327

13281328
fn bitcast(casts: &[Bitcast], operands: &[String], results: &mut Vec<String>) {
13291329
for (cast, operand) in casts.iter().zip(operands) {
1330-
results.push(match cast {
1331-
Bitcast::None => operand.clone(),
1332-
Bitcast::I32ToI64 => format!("i64::from({})", operand),
1333-
Bitcast::F32ToI32 => format!("({}).to_bits() as i32", operand),
1334-
Bitcast::F64ToI64 => format!("({}).to_bits() as i64", operand),
1335-
Bitcast::I64ToI32 => format!("{} as i32", operand),
1336-
Bitcast::I32ToF32 => format!("f32::from_bits({} as u32)", operand),
1337-
Bitcast::I64ToF64 => format!("f64::from_bits({} as u64)", operand),
1338-
Bitcast::F32ToI64 => format!("i64::from(({}).to_bits())", operand),
1339-
Bitcast::I64ToF32 => format!("f32::from_bits({} as u32)", operand),
1340-
1341-
// Convert an `i64` into a `MaybeUninit<u64>`.
1342-
Bitcast::I64ToP64 => format!("::core::mem::MaybeUninit::new({} as u64)", operand),
1343-
// Convert a `MaybeUninit<u64>` holding an `i64` value back into
1344-
// the `i64` value.
1345-
Bitcast::P64ToI64 => format!("{}.assume_init() as i64", operand),
1346-
1347-
// Convert a pointer value into a `MaybeUninit<u64>`.
1348-
Bitcast::PToP64 => {
1349-
format!(
1350-
"{{
1351-
let mut t = ::core::mem::MaybeUnunit::<u64>::uninit();
1330+
results.push(perform_cast(operand, cast));
1331+
}
1332+
}
1333+
1334+
fn perform_cast(operand: &str, cast: &Bitcast) -> String {
1335+
match cast {
1336+
Bitcast::None => operand.to_owned(),
1337+
Bitcast::I32ToI64 => format!("i64::from({})", operand),
1338+
Bitcast::F32ToI32 => format!("({}).to_bits() as i32", operand),
1339+
Bitcast::F64ToI64 => format!("({}).to_bits() as i64", operand),
1340+
Bitcast::I64ToI32 => format!("{} as i32", operand),
1341+
Bitcast::I32ToF32 => format!("f32::from_bits({} as u32)", operand),
1342+
Bitcast::I64ToF64 => format!("f64::from_bits({} as u64)", operand),
1343+
Bitcast::F32ToI64 => format!("i64::from(({}).to_bits())", operand),
1344+
Bitcast::I64ToF32 => format!("f32::from_bits({} as u32)", operand),
1345+
1346+
// Convert an `i64` into a `MaybeUninit<u64>`.
1347+
Bitcast::I64ToP64 => format!("::core::mem::MaybeUninit::new({} as u64)", operand),
1348+
// Convert a `MaybeUninit<u64>` holding an `i64` value back into
1349+
// the `i64` value.
1350+
Bitcast::P64ToI64 => format!("{}.assume_init() as i64", operand),
1351+
1352+
// Convert a pointer value into a `MaybeUninit<u64>`.
1353+
Bitcast::PToP64 => {
1354+
format!(
1355+
"{{
1356+
let mut t = ::core::mem::MaybeUninit::<u64>::uninit();
13521357
t.as_mut_ptr().cast::<*mut u8>().write({});
13531358
t
13541359
}}",
1355-
operand
1356-
)
1357-
}
1358-
// Convert a `MaybeUninit<u64>` holding a pointer value back into
1359-
// the pointer value.
1360-
Bitcast::P64ToP => {
1361-
format!("{}.as_mut_ptr().cast::<*mut u8>().read()", operand)
1362-
}
1363-
// Convert an `i32` into a pointer.
1364-
Bitcast::I32ToP => {
1365-
format!("{} as *mut u8", operand)
1366-
}
1367-
// Convert a pointer holding an `i32` value back into the `i32`.
1368-
Bitcast::PToI32 => {
1369-
format!("{} as i32", operand)
1370-
}
1371-
});
1360+
operand
1361+
)
1362+
}
1363+
// Convert a `MaybeUninit<u64>` holding a pointer value back into
1364+
// the pointer value.
1365+
Bitcast::P64ToP => {
1366+
format!("{}.as_mut_ptr().cast::<*mut u8>().read()", operand)
1367+
}
1368+
// Convert an `i32` or a `usize` into a pointer.
1369+
Bitcast::I32ToP | Bitcast::LToP => {
1370+
format!("{} as *mut u8", operand)
1371+
}
1372+
// Convert a pointer or length holding an `i32` value back into the `i32`.
1373+
Bitcast::PToI32 | Bitcast::LToI32 => {
1374+
format!("{} as i32", operand)
1375+
}
1376+
// Convert an `i32`, `i64`, or pointer holding a `usize` value back into the `usize`.
1377+
Bitcast::I32ToL | Bitcast::I64ToL | Bitcast::PToL => {
1378+
format!("{} as usize", operand)
1379+
}
1380+
// Convert a `usize` into an `i64`.
1381+
Bitcast::LToI64 => {
1382+
format!("{} as i64", operand)
1383+
}
1384+
Bitcast::Sequence(sequence) => {
1385+
let [first, second] = &**sequence;
1386+
perform_cast(&perform_cast(operand, first), second)
1387+
}
13721388
}
13731389
}
13741390

crates/teavm-java/src/lib.rs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,23 +1329,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
13291329
| Instruction::Float32FromF32
13301330
| Instruction::Float64FromF64 => results.push(operands[0].clone()),
13311331

1332-
Instruction::Bitcasts { casts } => {
1333-
results.extend(casts.iter().zip(operands).map(|(cast, op)| match cast {
1334-
Bitcast::I32ToF32 => format!("Float.intBitsToFloat({op})"),
1335-
Bitcast::I64ToF32 => format!("Float.intBitsToFloat((int) ({op}))"),
1336-
Bitcast::F32ToI32 => format!("Float.floatToIntBits({op})"),
1337-
Bitcast::F32ToI64 => format!("(long) Float.floatToIntBits({op})"),
1338-
Bitcast::I64ToF64 => format!("Double.longBitsToDouble({op})"),
1339-
Bitcast::F64ToI64 => format!("Double.doubleToLongBits({op})"),
1340-
Bitcast::I32ToI64 => format!("(long) ({op})"),
1341-
Bitcast::I64ToI32 => format!("(int) ({op})"),
1342-
Bitcast::I64ToP64 => format!("{op}"),
1343-
Bitcast::P64ToI64 => format!("{op}"),
1344-
Bitcast::PToP64 => format!("(long) ({op})"),
1345-
Bitcast::P64ToP => format!("(int) ({op})"),
1346-
Bitcast::I32ToP | Bitcast::PToI32 | Bitcast::None => op.to_owned(),
1347-
}))
1348-
}
1332+
Instruction::Bitcasts { casts } => results.extend(
1333+
casts
1334+
.iter()
1335+
.zip(operands)
1336+
.map(|(cast, op)| perform_cast(op, cast)),
1337+
),
13491338

13501339
Instruction::I32FromBool => {
13511340
results.push(format!("({} ? 1 : 0)", operands[0]));
@@ -2099,6 +2088,43 @@ impl Bindgen for FunctionBindgen<'_, '_> {
20992088
}
21002089
}
21012090

2091+
fn perform_cast(op: &str, cast: &Bitcast) -> String {
2092+
match cast {
2093+
Bitcast::I32ToF32 => {
2094+
format!("Float.intBitsToFloat({op})")
2095+
}
2096+
Bitcast::I64ToF32 => format!("Float.intBitsToFloat((int) ({op}))"),
2097+
Bitcast::F32ToI32 => {
2098+
format!("Float.floatToIntBits({op})")
2099+
}
2100+
Bitcast::F32ToI64 => format!("(long) Float.floatToIntBits({op})"),
2101+
Bitcast::I64ToF64 => {
2102+
format!("Double.longBitsToDouble({op})")
2103+
}
2104+
Bitcast::F64ToI64 => {
2105+
format!("Double.doubleToLongBits({op})")
2106+
}
2107+
Bitcast::I32ToI64 => format!("(long) ({op})"),
2108+
Bitcast::I64ToI32 => format!("(int) ({op})"),
2109+
Bitcast::I64ToP64 => format!("{op}"),
2110+
Bitcast::P64ToI64 => format!("{op}"),
2111+
Bitcast::LToI64 | Bitcast::PToP64 => format!("(long) ({op})"),
2112+
Bitcast::I64ToL | Bitcast::P64ToP => format!("(int) ({op})"),
2113+
Bitcast::I32ToP
2114+
| Bitcast::PToI32
2115+
| Bitcast::I32ToL
2116+
| Bitcast::LToI32
2117+
| Bitcast::LToP
2118+
| Bitcast::PToL
2119+
| Bitcast::None => op.to_owned(),
2120+
2121+
Bitcast::Sequence(sequence) => {
2122+
let [first, second] = &**sequence;
2123+
perform_cast(&perform_cast(op, first), second)
2124+
}
2125+
}
2126+
}
2127+
21022128
fn int_type(int: Int) -> &'static str {
21032129
match int {
21042130
Int::U8 => "byte",

0 commit comments

Comments
 (0)