1use cranelift_codegen::{
4 ir::{
5 self, AbiParam, ArgumentPurpose, InstBuilder, MemFlagsData, StackSlotData, StackSlotKind,
6 },
7 isa::TargetFrontendConfig,
8};
9use cranelift_frontend::FunctionBuilder;
10use smallvec::{SmallVec, smallvec};
11use target_lexicon::Architecture;
12use wasmer_compiler::abi::{
13 PairSlot, ReturnAbi, ReturnSlot, classify_return_type_aarch64, classify_return_type_riscv64,
14 classify_return_type_x86_64,
15};
16use wasmer_types::{FunctionType, Type};
17
18use crate::translator::type_to_irtype;
19
20pub(crate) fn classify_returns(arch: Architecture, types: &[Type]) -> ReturnAbi {
22 match arch {
23 Architecture::X86_64 => classify_return_type_x86_64(types),
24 Architecture::Aarch64(_) => classify_return_type_aarch64(types),
25 Architecture::Riscv64(_) => classify_return_type_riscv64(types),
26 _ => unreachable!("unexpected architecture: {arch}"),
27 }
28}
29
30fn natural_type(ty: Type, config: TargetFrontendConfig) -> ir::Type {
31 type_to_irtype(ty, config).expect("supported WebAssembly signature type")
32}
33
34fn slot_type(slot: ReturnSlot, config: TargetFrontendConfig) -> ir::Type {
35 match slot {
36 ReturnSlot::Natural(ty) => natural_type(ty, config),
37 ReturnSlot::Raw(Type::F32) => ir::types::I32,
38 ReturnSlot::Raw(Type::F64) => ir::types::I64,
39 ReturnSlot::Raw(ty) => natural_type(ty, config),
40 }
41}
42
43fn pair_type(pair: PairSlot) -> ir::Type {
44 match pair {
45 PairSlot::F32Vector(_, _) => ir::types::F32X2,
46 PairSlot::Raw(_, _) => ir::types::I64,
47 }
48}
49
50pub(crate) fn signature_to_ir(
52 signature: &FunctionType,
53 config: TargetFrontendConfig,
54 arch: Architecture,
55) -> ir::Signature {
56 let return_abi = classify_returns(arch, signature.results());
57 let mut sig = ir::Signature::new(config.default_call_conv);
58
59 if matches!(return_abi, ReturnAbi::Sret(_)) {
60 sig.params.push(AbiParam::special(
61 config.pointer_type(),
62 ArgumentPurpose::StructReturn,
63 ));
64 }
65 sig.params.push(AbiParam::special(
66 config.pointer_type(),
67 ArgumentPurpose::VMContext,
68 ));
69 sig.params.extend(
70 signature
71 .params()
72 .iter()
73 .map(|&ty| AbiParam::new(natural_type(ty, config))),
74 );
75
76 match return_abi {
77 ReturnAbi::Void => {}
78 ReturnAbi::Single(ty) => sig.returns.push(AbiParam::new(natural_type(ty, config))),
79 ReturnAbi::Pair(a, b) => {
80 sig.returns.push(AbiParam::new(slot_type(a, config)));
81 sig.returns.push(AbiParam::new(slot_type(b, config)));
82 }
83 ReturnAbi::PackedPair(pair) => sig.returns.push(AbiParam::new(pair_type(pair))),
84 ReturnAbi::PackedFirst(pair, slot) => {
85 sig.returns.push(AbiParam::new(pair_type(pair)));
86 sig.returns.push(AbiParam::new(slot_type(slot, config)));
87 }
88 ReturnAbi::PackedLast(slot, pair) => {
89 sig.returns.push(AbiParam::new(slot_type(slot, config)));
90 sig.returns.push(AbiParam::new(pair_type(pair)));
91 }
92 ReturnAbi::PackedQuads(a, b) => {
93 sig.returns.push(AbiParam::new(pair_type(a)));
94 sig.returns.push(AbiParam::new(pair_type(b)));
95 }
96 ReturnAbi::Unpacked(types) => sig.returns.extend(
97 types
98 .into_iter()
99 .map(|ty| AbiParam::new(natural_type(ty, config))),
100 ),
101 ReturnAbi::Sret(_) => {}
103 }
104 sig
105}
106
107fn bitcast(builder: &mut FunctionBuilder, ty: ir::Type, value: ir::Value) -> ir::Value {
108 if builder.func.dfg.value_type(value) == ty {
109 value
110 } else {
111 let mut flags = MemFlagsData::new();
112 flags.set_endianness(ir::Endianness::Little);
113 builder.ins().bitcast(ty, flags, value)
114 }
115}
116
117fn pack_slot(builder: &mut FunctionBuilder, value: ir::Value, slot: ReturnSlot) -> ir::Value {
118 match slot {
119 ReturnSlot::Natural(Type::V128) => bitcast(builder, ir::types::I8X16, value),
120 ReturnSlot::Natural(_) => value,
121 ReturnSlot::Raw(Type::F32) => bitcast(builder, ir::types::I32, value),
122 ReturnSlot::Raw(Type::F64) => bitcast(builder, ir::types::I64, value),
123 ReturnSlot::Raw(_) => value,
124 }
125}
126
127fn unpack_slot(builder: &mut FunctionBuilder, value: ir::Value, slot: ReturnSlot) -> ir::Value {
128 match slot {
129 ReturnSlot::Natural(_) => value,
130 ReturnSlot::Raw(Type::F32) => bitcast(builder, ir::types::F32, value),
131 ReturnSlot::Raw(Type::F64) => bitcast(builder, ir::types::F64, value),
132 ReturnSlot::Raw(_) => value,
133 }
134}
135
136fn pack_pair(
137 builder: &mut FunctionBuilder,
138 first: ir::Value,
139 second: ir::Value,
140 pair: PairSlot,
141) -> ir::Value {
142 match pair {
143 PairSlot::Raw(_, _) => {
144 let low = bitcast(builder, ir::types::I32, first);
145 let high = bitcast(builder, ir::types::I32, second);
146 let low = builder.ins().uextend(ir::types::I64, low);
147 let high = builder.ins().uextend(ir::types::I64, high);
148 let high = builder.ins().ishl_imm_u(high, 32);
149 builder.ins().bor(low, high)
150 }
151 PairSlot::F32Vector(_, _) => {
152 let low = bitcast(builder, ir::types::I32, first);
153 let high = bitcast(builder, ir::types::I32, second);
154 let low = builder.ins().uextend(ir::types::I64, low);
155 let high = builder.ins().uextend(ir::types::I64, high);
156 let high = builder.ins().ishl_imm_u(high, 32);
157 let bits = builder.ins().bor(low, high);
158 bitcast(builder, ir::types::F32X2, bits)
159 }
160 }
161}
162
163#[derive(Clone, Debug)]
165pub(crate) struct ReturnAreaLayout {
166 pub(crate) offsets: Vec<i32>,
168 pub(crate) size: u32,
170 pub(crate) align_shift: u8,
172}
173
174fn type_size(ty: Type) -> u32 {
175 match ty {
176 Type::I32 | Type::F32 | Type::ExceptionRef => 4,
177 Type::I64 | Type::F64 => 8,
178 Type::ExternRef | Type::FuncRef => 8,
180 Type::V128 => 16,
181 }
182}
183
184pub(crate) fn return_area_layout(types: &[Type]) -> ReturnAreaLayout {
186 let mut offset = 0u32;
187 let mut align = 1u32;
188 let mut offsets = Vec::with_capacity(types.len());
189 for &ty in types {
190 let size = type_size(ty);
191 align = align.max(size);
192 offset = offset.next_multiple_of(size);
193 offsets.push(i32::try_from(offset).unwrap());
194 offset += size;
195 }
196 let size = offset.next_multiple_of(align);
197 ReturnAreaLayout {
198 offsets,
199 size,
200 align_shift: align.trailing_zeros() as u8,
201 }
202}
203
204pub(crate) fn allocate_return_area(
206 builder: &mut FunctionBuilder,
207 types: &[Type],
208 config: TargetFrontendConfig,
209) -> (ir::Value, ReturnAreaLayout) {
210 let layout = return_area_layout(types);
211 let slot = builder.create_sized_stack_slot(StackSlotData::new(
212 StackSlotKind::ExplicitSlot,
213 layout.size,
214 layout.align_shift,
215 ));
216 let ptr = builder.ins().stack_addr(config.pointer_type(), slot, 0);
217 (ptr, layout)
218}
219
220pub(crate) fn store_sret(
222 builder: &mut FunctionBuilder,
223 ptr: ir::Value,
224 layout: &ReturnAreaLayout,
225 values: &[ir::Value],
226) {
227 let flags = MemFlagsData::trusted();
228 for (&value, &offset) in values.iter().zip(&layout.offsets) {
229 builder.ins().store(flags, value, ptr, offset);
230 }
231}
232
233pub(crate) fn load_sret(
235 builder: &mut FunctionBuilder,
236 ptr: ir::Value,
237 layout: &ReturnAreaLayout,
238 types: &[Type],
239 config: TargetFrontendConfig,
240) -> SmallVec<[ir::Value; 4]> {
241 let flags = MemFlagsData::trusted();
242 types
243 .iter()
244 .zip(&layout.offsets)
245 .map(|(&ty, &offset)| {
246 builder
247 .ins()
248 .load(natural_type(ty, config), flags, ptr, offset)
249 })
250 .collect()
251}
252
253pub(crate) fn pack_register_returns(
255 builder: &mut FunctionBuilder,
256 abi: &ReturnAbi,
257 values: &[ir::Value],
258) -> SmallVec<[ir::Value; 4]> {
259 match abi {
260 ReturnAbi::Void => SmallVec::new(),
261 ReturnAbi::Single(ty) => smallvec![if *ty == Type::V128 {
262 bitcast(builder, ir::types::I8X16, values[0])
263 } else {
264 values[0]
265 }],
266 ReturnAbi::Pair(a, b) => smallvec![
267 pack_slot(builder, values[0], *a),
268 pack_slot(builder, values[1], *b)
269 ],
270 ReturnAbi::PackedPair(pair) => {
271 smallvec![pack_pair(builder, values[0], values[1], *pair)]
272 }
273 ReturnAbi::PackedFirst(pair, slot) => smallvec![
274 pack_pair(builder, values[0], values[1], *pair),
275 pack_slot(builder, values[2], *slot)
276 ],
277 ReturnAbi::PackedLast(slot, pair) => smallvec![
278 pack_slot(builder, values[0], *slot),
279 pack_pair(builder, values[1], values[2], *pair)
280 ],
281 ReturnAbi::PackedQuads(a, b) => smallvec![
282 pack_pair(builder, values[0], values[1], *a),
283 pack_pair(builder, values[2], values[3], *b)
284 ],
285 ReturnAbi::Unpacked(_) => values.iter().copied().collect(),
286 ReturnAbi::Sret(_) => panic!("sret values must be stored, not packed"),
287 }
288}
289
290pub(crate) fn unpack_register_returns(
292 builder: &mut FunctionBuilder,
293 abi: &ReturnAbi,
294 values: &[ir::Value],
295 config: TargetFrontendConfig,
296) -> SmallVec<[ir::Value; 4]> {
297 let unpack_pair_with_config = |builder: &mut FunctionBuilder, value, pair| match pair {
298 PairSlot::Raw(first, second) => {
299 let low = builder.ins().ireduce(ir::types::I32, value);
300 let high = builder.ins().ushr_imm_u(value, 32);
301 let high = builder.ins().ireduce(ir::types::I32, high);
302 (
303 bitcast(builder, natural_type(first, config), low),
304 bitcast(builder, natural_type(second, config), high),
305 )
306 }
307 PairSlot::F32Vector(_, _) => {
308 let bits = bitcast(builder, ir::types::I64, value);
309 let low = builder.ins().ireduce(ir::types::I32, bits);
310 let high = builder.ins().ushr_imm_u(bits, 32);
311 let high = builder.ins().ireduce(ir::types::I32, high);
312 (
313 bitcast(builder, ir::types::F32, low),
314 bitcast(builder, ir::types::F32, high),
315 )
316 }
317 };
318 match abi {
319 ReturnAbi::Void => SmallVec::new(),
320 ReturnAbi::Single(_) => smallvec![values[0]],
321 ReturnAbi::Pair(a, b) => smallvec![
322 unpack_slot(builder, values[0], *a),
323 unpack_slot(builder, values[1], *b)
324 ],
325 ReturnAbi::PackedPair(pair) => {
326 let (a, b) = unpack_pair_with_config(builder, values[0], *pair);
327 smallvec![a, b]
328 }
329 ReturnAbi::PackedFirst(pair, slot) => {
330 let (a, b) = unpack_pair_with_config(builder, values[0], *pair);
331 smallvec![a, b, unpack_slot(builder, values[1], *slot)]
332 }
333 ReturnAbi::PackedLast(slot, pair) => {
334 let (a, b) = unpack_pair_with_config(builder, values[1], *pair);
335 smallvec![unpack_slot(builder, values[0], *slot), a, b]
336 }
337 ReturnAbi::PackedQuads(a, b) => {
338 let (a0, a1) = unpack_pair_with_config(builder, values[0], *a);
339 let (b0, b1) = unpack_pair_with_config(builder, values[1], *b);
340 smallvec![a0, a1, b0, b1]
341 }
342 ReturnAbi::Unpacked(_) => values.iter().copied().collect(),
343 ReturnAbi::Sret(_) => panic!("sret values must be loaded, not unpacked"),
344 }
345}