From 96c25326588c40aae9b07cac46f209d0a3f9600b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 5 Mar 2024 06:17:01 -0800 Subject: [PATCH 1/2] C: Use pointer types Teach the C backend to use C pointer types. This is similar to #870 for Rust. C is less concerned about provenance, but it makes the ABI easier to understand and reduces the amount of casting needed in the generated code. While here, I also optimized the code for list deallocation slightly. --- crates/c/src/lib.rs | 75 +++++++++++++++++++++++------------------- crates/core/src/abi.rs | 28 ++++++++-------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index f94b04950..381761f8a 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1455,12 +1455,17 @@ impl InterfaceGenerator<'_> { } TypeDefKind::List(t) => { + self.src.c_helpers("size_t list_len = ptr->len;\n"); + uwriteln!(self.src.c_helpers, "if (list_len > 0) {{"); + let mut t_name = String::new(); + self.gen.push_type_name(t, &mut t_name); self.src - .c_helpers("for (size_t i = 0; i < ptr->len; i++) {\n"); - self.free(t, "&ptr->ptr[i]"); + .c_helpers(&format!("{t_name} *list_ptr = ptr->ptr;\n")); + self.src + .c_helpers("for (size_t i = 0; i < list_len; i++) {\n"); + self.free(t, "&list_ptr[i]"); self.src.c_helpers("}\n"); - uwriteln!(self.src.c_helpers, "if (ptr->len > 0) {{"); - uwriteln!(self.src.c_helpers, "free(ptr->ptr);"); + uwriteln!(self.src.c_helpers, "free(list_ptr);"); uwriteln!(self.src.c_helpers, "}}"); } @@ -2111,13 +2116,13 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); self.import_return_pointer_area_align = self.import_return_pointer_area_align.max(align); - uwriteln!(self.src, "int32_t {} = (int32_t) &ret_area;", ptr); + uwriteln!(self.src, "uint8_t *{} = (uint8_t *) &ret_area;", ptr); } else { self.gen.gen.return_pointer_area_size = self.gen.gen.return_pointer_area_size.max(size); self.gen.gen.return_pointer_area_align = self.gen.gen.return_pointer_area_align.max(align); // Declare a statically-allocated return area. - uwriteln!(self.src, "int32_t {} = (int32_t) &RET_AREA;", ptr); + uwriteln!(self.src, "uint8_t *{} = (uint8_t *) &RET_AREA;", ptr); } ptr @@ -2562,8 +2567,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::EnumLift { .. } => results.push(operands.pop().unwrap()), Instruction::ListCanonLower { .. } | Instruction::StringLower { .. } => { - results.push(format!("(int32_t) ({}).ptr", operands[0])); - results.push(format!("(int32_t) ({}).len", operands[0])); + results.push(format!("(uint8_t *) ({}).ptr", operands[0])); + results.push(format!("({}).len", operands[0])); } Instruction::ListCanonLift { element, ty, .. } => { self.assert_no_droppable_borrows("list", &Type::Id(*ty)); @@ -2571,14 +2576,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { let list_name = self.gen.gen.type_name(&Type::Id(*ty)); let elem_name = self.gen.gen.type_name(element); results.push(format!( - "({}) {{ ({}*)({}), (size_t)({}) }}", + "({}) {{ ({}*)({}), ({}) }}", list_name, elem_name, operands[0], operands[1] )); } Instruction::StringLift { .. } => { let list_name = self.gen.gen.type_name(&Type::String); results.push(format!( - "({}) {{ ({}*)({}), (size_t)({}) }}", + "({}) {{ ({}*)({}), ({}) }}", list_name, self.gen.gen.char_type(), operands[0], @@ -2588,8 +2593,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::ListLower { .. } => { let _body = self.blocks.pop().unwrap(); - results.push(format!("(int32_t) ({}).ptr", operands[0])); - results.push(format!("(int32_t) ({}).len", operands[0])); + results.push(format!("(uint8_t *) ({}).ptr", operands[0])); + results.push(format!("({}).len", operands[0])); } Instruction::ListLift { element, ty, .. } => { @@ -2599,7 +2604,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let list_name = self.gen.gen.type_name(&Type::Id(*ty)); let elem_name = self.gen.gen.type_name(element); results.push(format!( - "({}) {{ ({}*)({}), (size_t)({}) }}", + "({}) {{ ({}*)({}), ({}) }}", list_name, elem_name, operands[0], operands[1] )); } @@ -2841,22 +2846,22 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - Instruction::I32Load { offset } - | Instruction::PointerLoad { offset } - | Instruction::LengthLoad { offset } => { - self.load("int32_t", *offset, operands, results) - } + Instruction::I32Load { offset } => self.load("int32_t", *offset, operands, results), Instruction::I64Load { offset } => self.load("int64_t", *offset, operands, results), Instruction::F32Load { offset } => self.load("float", *offset, operands, results), Instruction::F64Load { offset } => self.load("double", *offset, operands, results), - Instruction::I32Store { offset } - | Instruction::PointerStore { offset } - | Instruction::LengthStore { offset } => self.store("int32_t", *offset, operands), + Instruction::PointerLoad { offset } => { + self.load("uint8_t *", *offset, operands, results) + } + Instruction::LengthLoad { offset } => self.load("size_t", *offset, operands, results), + Instruction::I32Store { offset } => self.store("int32_t", *offset, operands), Instruction::I64Store { offset } => self.store("int64_t", *offset, operands), Instruction::F32Store { offset } => self.store("float", *offset, operands), Instruction::F64Store { offset } => self.store("double", *offset, operands), Instruction::I32Store8 { offset } => self.store("int8_t", *offset, operands), Instruction::I32Store16 { offset } => self.store("int16_t", *offset, operands), + Instruction::PointerStore { offset } => self.store("uint8_t *", *offset, operands), + Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), Instruction::I32Load8U { offset } => { self.load_ext("uint8_t", *offset, operands, results) @@ -2872,11 +2877,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { } Instruction::GuestDeallocate { .. } => { - uwriteln!(self.src, "free((void*) ({}));", operands[0]); + uwriteln!(self.src, "free({});", operands[0]); } Instruction::GuestDeallocateString => { uwriteln!(self.src, "if (({}) > 0) {{", operands[1]); - uwriteln!(self.src, "free((void*) ({}));", operands[0]); + uwriteln!(self.src, "free({});", operands[0]); uwriteln!(self.src, "}}"); } Instruction::GuestDeallocateVariant { blocks } => { @@ -2897,19 +2902,19 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::GuestDeallocateList { element } => { let (body, results) = self.blocks.pop().unwrap(); assert!(results.is_empty()); - let ptr = self.locals.tmp("ptr"); let len = self.locals.tmp("len"); - uwriteln!(self.src, "int32_t {ptr} = {};", operands[0]); - uwriteln!(self.src, "int32_t {len} = {};", operands[1]); + uwriteln!(self.src, "size_t {len} = {};", operands[1]); + uwriteln!(self.src, "if ({len} > 0) {{"); + let ptr = self.locals.tmp("ptr"); + uwriteln!(self.src, "uint8_t *{ptr} = {};", operands[0]); let i = self.locals.tmp("i"); - uwriteln!(self.src, "for (int32_t {i} = 0; {i} < {len}; {i}++) {{"); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); let size = self.gen.gen.sizes.size(element); - uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};"); + uwriteln!(self.src, "uint8_t *base = {ptr} + {i} * {size};"); uwriteln!(self.src, "(void) base;"); uwrite!(self.src, "{body}"); uwriteln!(self.src, "}}"); - uwriteln!(self.src, "if ({len} > 0) {{"); - uwriteln!(self.src, "free((void*) ({ptr}));"); + uwriteln!(self.src, "free({ptr});"); uwriteln!(self.src, "}}"); } @@ -2935,14 +2940,16 @@ fn perform_cast(op: &str, cast: &Bitcast) -> String { Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => { format!("(int64_t) {}", op) } - Bitcast::I64ToI32 | Bitcast::I64ToL | Bitcast::P64ToP => { + Bitcast::I64ToI32 | Bitcast::I64ToL => { format!("(int32_t) {}", op) } Bitcast::I64ToP64 | Bitcast::P64ToI64 => { format!("{}", op) } - Bitcast::I32ToP - | Bitcast::PToI32 + Bitcast::P64ToP | Bitcast::I32ToP => { + format!("(uint8_t *) {}", op) + } + Bitcast::PToI32 | Bitcast::I32ToL | Bitcast::LToI32 | Bitcast::LToP @@ -3031,7 +3038,7 @@ fn wasm_type(ty: WasmType) -> &'static str { WasmType::I64 => "int64_t", WasmType::F32 => "float", WasmType::F64 => "double", - WasmType::Pointer => "uintptr_t", + WasmType::Pointer => "uint8_t *", WasmType::PointerOrI64 => "int64_t", WasmType::Length => "size_t", } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 7c96283fb..ef4e5e10e 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -84,32 +84,32 @@ def_instruction! { // Memory load/store instructions - /// Pops an `i32` from the stack and loads a little-endian `i32` from + /// Pops a pointer from the stack and loads a little-endian `i32` from /// it, using the specified constant offset. I32Load { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `i8` from + /// Pops a pointer from the stack and loads a little-endian `i8` from /// it, using the specified constant offset. The value loaded is the /// zero-extended to 32-bits I32Load8U { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `i8` from + /// Pops a pointer from the stack and loads a little-endian `i8` from /// it, using the specified constant offset. The value loaded is the /// sign-extended to 32-bits I32Load8S { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `i16` from + /// Pops a pointer from the stack and loads a little-endian `i16` from /// it, using the specified constant offset. The value loaded is the /// zero-extended to 32-bits I32Load16U { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `i16` from + /// Pops a pointer from the stack and loads a little-endian `i16` from /// it, using the specified constant offset. The value loaded is the /// sign-extended to 32-bits I32Load16S { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `i64` from + /// Pops a pointer from the stack and loads a little-endian `i64` from /// it, using the specified constant offset. I64Load { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `f32` from + /// Pops a pointer from the stack and loads a little-endian `f32` from /// it, using the specified constant offset. F32Load { offset: i32 } : [1] => [1], - /// Pops an `i32` from the stack and loads a little-endian `f64` from + /// Pops a pointer from the stack and loads a little-endian `f64` from /// it, using the specified constant offset. F64Load { offset: i32 } : [1] => [1], @@ -118,27 +118,27 @@ def_instruction! { /// Like `I32Load` or `I64Load`, but for loading array length values. LengthLoad { offset: i32 } : [1] => [1], - /// Pops an `i32` address from the stack and then an `i32` value. + /// Pops a pointer from the stack and then an `i32` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. I32Store { offset: i32 } : [2] => [0], - /// Pops an `i32` address from the stack and then an `i32` value. + /// Pops a pointer from the stack and then an `i32` value. /// Stores the low 8 bits of the value in little-endian at the pointer /// specified plus the constant `offset`. I32Store8 { offset: i32 } : [2] => [0], - /// Pops an `i32` address from the stack and then an `i32` value. + /// Pops a pointer from the stack and then an `i32` value. /// Stores the low 16 bits of the value in little-endian at the pointer /// specified plus the constant `offset`. I32Store16 { offset: i32 } : [2] => [0], - /// Pops an `i32` address from the stack and then an `i64` value. + /// Pops a pointer from the stack and then an `i64` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. I64Store { offset: i32 } : [2] => [0], - /// Pops an `i32` address from the stack and then an `f32` value. + /// Pops a pointer from the stack and then an `f32` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. F32Store { offset: i32 } : [2] => [0], - /// Pops an `i32` address from the stack and then an `f64` value. + /// Pops a pointer from the stack and then an `f64` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. F64Store { offset: i32 } : [2] => [0], From a9ee87cc536ef00039b32d17e528d795a6d172fa Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 6 Mar 2024 10:54:56 -0800 Subject: [PATCH 2/2] Fix casting for variants. --- crates/c/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 381761f8a..6481ea781 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -2943,18 +2943,18 @@ fn perform_cast(op: &str, cast: &Bitcast) -> String { Bitcast::I64ToI32 | Bitcast::I64ToL => { format!("(int32_t) {}", op) } + // P64 is currently represented as int64_t, so no conversion is needed. Bitcast::I64ToP64 | Bitcast::P64ToI64 => { format!("{}", op) } - Bitcast::P64ToP | Bitcast::I32ToP => { + Bitcast::P64ToP | Bitcast::I32ToP | Bitcast::LToP => { format!("(uint8_t *) {}", op) } - Bitcast::PToI32 - | Bitcast::I32ToL - | Bitcast::LToI32 - | Bitcast::LToP - | Bitcast::PToL - | Bitcast::None => op.to_string(), + + // Cast to uintptr_t to avoid implicit pointer-to-int conversions. + Bitcast::PToI32 | Bitcast::PToL => format!("(uintptr_t) {}", op), + + Bitcast::I32ToL | Bitcast::LToI32 | Bitcast::None => op.to_string(), Bitcast::Sequence(sequence) => { let [first, second] = &**sequence;