Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/pulley_shared/lower/isle.rs
1693 views
1
//! ISLE integration glue code for Pulley lowering.
2
3
// Pull in the ISLE generated code.
4
pub mod generated_code;
5
use generated_code::MInst;
6
use inst::InstAndKind;
7
8
// Types that the generated ISLE code uses via `use super::*`.
9
use crate::ir::{condcodes::*, immediates::*, types::*, *};
10
use crate::isa::pulley_shared::{
11
inst::{
12
FReg, OperandSize, PulleyCall, ReturnCallInfo, VReg, WritableFReg, WritableVReg,
13
WritableXReg, XReg,
14
},
15
lower::{Cond, regs},
16
*,
17
};
18
use crate::machinst::{
19
CallArgList, CallInfo, CallRetList, MachInst, Reg, VCodeConstant, VCodeConstantData,
20
abi::{ArgPair, RetPair, StackAMode},
21
isle::*,
22
};
23
use alloc::boxed::Box;
24
use pulley_interpreter::U6;
25
use regalloc2::PReg;
26
use smallvec::SmallVec;
27
28
type Unit = ();
29
type VecArgPair = Vec<ArgPair>;
30
type VecRetPair = Vec<RetPair>;
31
type BoxCallInfo = Box<CallInfo<PulleyCall>>;
32
type BoxCallIndInfo = Box<CallInfo<XReg>>;
33
type BoxCallIndirectHostInfo = Box<CallInfo<ExternalName>>;
34
type BoxReturnCallInfo = Box<ReturnCallInfo<ExternalName>>;
35
type BoxReturnCallIndInfo = Box<ReturnCallInfo<XReg>>;
36
type BoxExternalName = Box<ExternalName>;
37
type UpperXRegSet = pulley_interpreter::UpperRegSet<pulley_interpreter::XReg>;
38
type PcRelOffset = pulley_interpreter::PcRelOffset;
39
40
#[expect(
41
unused_imports,
42
reason = "used on other backends, used here to suppress warning elsewhere"
43
)]
44
use crate::machinst::isle::UnwindInst as _;
45
46
pub(crate) struct PulleyIsleContext<'a, 'b, I, B>
47
where
48
I: VCodeInst,
49
B: LowerBackend,
50
{
51
pub lower_ctx: &'a mut Lower<'b, I>,
52
pub backend: &'a B,
53
}
54
55
impl<'a, 'b, P> PulleyIsleContext<'a, 'b, InstAndKind<P>, PulleyBackend<P>>
56
where
57
P: PulleyTargetKind,
58
{
59
fn new(lower_ctx: &'a mut Lower<'b, InstAndKind<P>>, backend: &'a PulleyBackend<P>) -> Self {
60
Self { lower_ctx, backend }
61
}
62
63
pub(crate) fn dfg(&self) -> &crate::ir::DataFlowGraph {
64
&self.lower_ctx.f.dfg
65
}
66
}
67
68
impl<P> generated_code::Context for PulleyIsleContext<'_, '_, InstAndKind<P>, PulleyBackend<P>>
69
where
70
P: PulleyTargetKind,
71
{
72
crate::isle_lower_prelude_methods!(InstAndKind<P>);
73
74
fn gen_call_info(
75
&mut self,
76
sig: Sig,
77
name: ExternalName,
78
mut uses: CallArgList,
79
defs: CallRetList,
80
try_call_info: Option<TryCallInfo>,
81
) -> BoxCallInfo {
82
let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
83
let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
84
self.lower_ctx
85
.abi_mut()
86
.accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
87
88
// The first four integer arguments to a call can be handled via
89
// special pulley call instructions. Assert here that
90
// `uses` is sorted in order and then take out x0-x3 if
91
// they're present and move them from `uses` to
92
// `dest.args` to be handled differently during register
93
// allocation.
94
let mut args = SmallVec::new();
95
uses.sort_by_key(|arg| arg.preg);
96
uses.retain(|arg| {
97
if arg.preg != regs::x0()
98
&& arg.preg != regs::x1()
99
&& arg.preg != regs::x2()
100
&& arg.preg != regs::x3()
101
{
102
return true;
103
}
104
args.push(XReg::new(arg.vreg).unwrap());
105
false
106
});
107
let dest = PulleyCall { name, args };
108
Box::new(
109
self.lower_ctx
110
.gen_call_info(sig, dest, uses, defs, try_call_info),
111
)
112
}
113
114
fn gen_call_ind_info(
115
&mut self,
116
sig: Sig,
117
dest: Reg,
118
uses: CallArgList,
119
defs: CallRetList,
120
try_call_info: Option<TryCallInfo>,
121
) -> BoxCallIndInfo {
122
let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
123
let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
124
self.lower_ctx
125
.abi_mut()
126
.accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
127
128
let dest = XReg::new(dest).unwrap();
129
Box::new(
130
self.lower_ctx
131
.gen_call_info(sig, dest, uses, defs, try_call_info),
132
)
133
}
134
135
fn gen_call_host_info(
136
&mut self,
137
sig: Sig,
138
dest: ExternalName,
139
uses: CallArgList,
140
defs: CallRetList,
141
try_call_info: Option<TryCallInfo>,
142
) -> BoxCallIndirectHostInfo {
143
let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
144
let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
145
self.lower_ctx
146
.abi_mut()
147
.accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
148
149
Box::new(
150
self.lower_ctx
151
.gen_call_info(sig, dest, uses, defs, try_call_info),
152
)
153
}
154
155
fn gen_return_call_info(
156
&mut self,
157
sig: Sig,
158
dest: ExternalName,
159
uses: CallArgList,
160
) -> BoxReturnCallInfo {
161
let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
162
self.lower_ctx
163
.abi_mut()
164
.accumulate_tail_args_size(new_stack_arg_size);
165
166
Box::new(ReturnCallInfo {
167
dest,
168
uses,
169
new_stack_arg_size,
170
})
171
}
172
173
fn gen_return_call_ind_info(
174
&mut self,
175
sig: Sig,
176
dest: Reg,
177
uses: CallArgList,
178
) -> BoxReturnCallIndInfo {
179
let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
180
self.lower_ctx
181
.abi_mut()
182
.accumulate_tail_args_size(new_stack_arg_size);
183
184
Box::new(ReturnCallInfo {
185
dest: XReg::new(dest).unwrap(),
186
uses,
187
new_stack_arg_size,
188
})
189
}
190
191
fn vreg_new(&mut self, r: Reg) -> VReg {
192
VReg::new(r).unwrap()
193
}
194
fn writable_vreg_new(&mut self, r: WritableReg) -> WritableVReg {
195
r.map(|wr| VReg::new(wr).unwrap())
196
}
197
fn writable_vreg_to_vreg(&mut self, arg0: WritableVReg) -> VReg {
198
arg0.to_reg()
199
}
200
fn writable_vreg_to_writable_reg(&mut self, arg0: WritableVReg) -> WritableReg {
201
arg0.map(|vr| vr.to_reg())
202
}
203
fn vreg_to_reg(&mut self, arg0: VReg) -> Reg {
204
*arg0
205
}
206
fn xreg_new(&mut self, r: Reg) -> XReg {
207
XReg::new(r).unwrap()
208
}
209
fn writable_xreg_new(&mut self, r: WritableReg) -> WritableXReg {
210
r.map(|wr| XReg::new(wr).unwrap())
211
}
212
fn writable_xreg_to_xreg(&mut self, arg0: WritableXReg) -> XReg {
213
arg0.to_reg()
214
}
215
fn writable_xreg_to_writable_reg(&mut self, arg0: WritableXReg) -> WritableReg {
216
arg0.map(|xr| xr.to_reg())
217
}
218
fn xreg_to_reg(&mut self, arg0: XReg) -> Reg {
219
*arg0
220
}
221
fn freg_new(&mut self, r: Reg) -> FReg {
222
FReg::new(r).unwrap()
223
}
224
fn writable_freg_new(&mut self, r: WritableReg) -> WritableFReg {
225
r.map(|wr| FReg::new(wr).unwrap())
226
}
227
fn writable_freg_to_freg(&mut self, arg0: WritableFReg) -> FReg {
228
arg0.to_reg()
229
}
230
fn writable_freg_to_writable_reg(&mut self, arg0: WritableFReg) -> WritableReg {
231
arg0.map(|fr| fr.to_reg())
232
}
233
fn freg_to_reg(&mut self, arg0: FReg) -> Reg {
234
*arg0
235
}
236
237
#[inline]
238
fn emit(&mut self, arg0: &MInst) -> Unit {
239
self.lower_ctx.emit(arg0.clone().into());
240
}
241
242
fn sp_reg(&mut self) -> XReg {
243
XReg::new(regs::stack_reg()).unwrap()
244
}
245
246
fn cond_invert(&mut self, cond: &Cond) -> Cond {
247
cond.invert()
248
}
249
250
fn u6_from_u8(&mut self, imm: u8) -> Option<U6> {
251
U6::new(imm)
252
}
253
254
fn endianness(&mut self, flags: MemFlags) -> Endianness {
255
flags.endianness(self.backend.isa_flags.endianness())
256
}
257
258
fn is_native_endianness(&mut self, endianness: &Endianness) -> bool {
259
*endianness == self.backend.isa_flags.endianness()
260
}
261
262
fn pointer_width(&mut self) -> PointerWidth {
263
P::pointer_width()
264
}
265
266
fn memflags_nontrapping(&mut self, flags: MemFlags) -> bool {
267
flags.trap_code().is_none()
268
}
269
270
fn memflags_is_wasm(&mut self, flags: MemFlags) -> bool {
271
flags.trap_code() == Some(TrapCode::HEAP_OUT_OF_BOUNDS)
272
&& self.endianness(flags) == Endianness::Little
273
}
274
275
fn g32_offset(
276
&mut self,
277
load_offset: i32,
278
load_ty: Type,
279
bound_check_offset: u64,
280
) -> Option<u16> {
281
// NB: for more docs on this see the ISLE definition.
282
let load_offset = u64::try_from(load_offset).ok()?;
283
let load_bytes = u64::from(load_ty.bytes());
284
if bound_check_offset != load_offset + load_bytes {
285
return None;
286
}
287
u16::try_from(load_offset).ok()
288
}
289
}
290
291
/// The main entry point for lowering with ISLE.
292
pub(crate) fn lower<P>(
293
lower_ctx: &mut Lower<InstAndKind<P>>,
294
backend: &PulleyBackend<P>,
295
inst: Inst,
296
) -> Option<InstOutput>
297
where
298
P: PulleyTargetKind,
299
{
300
// TODO: reuse the ISLE context across lowerings so we can reuse its
301
// internal heap allocations.
302
let mut isle_ctx = PulleyIsleContext::new(lower_ctx, backend);
303
generated_code::constructor_lower(&mut isle_ctx, inst)
304
}
305
306
/// The main entry point for branch lowering with ISLE.
307
pub(crate) fn lower_branch<P>(
308
lower_ctx: &mut Lower<InstAndKind<P>>,
309
backend: &PulleyBackend<P>,
310
branch: Inst,
311
targets: &[MachLabel],
312
) -> Option<()>
313
where
314
P: PulleyTargetKind,
315
{
316
// TODO: reuse the ISLE context across lowerings so we can reuse its
317
// internal heap allocations.
318
let mut isle_ctx = PulleyIsleContext::new(lower_ctx, backend);
319
generated_code::constructor_lower_branch(&mut isle_ctx, branch, targets)
320
}
321
322