Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/meta/src/cdsl/instructions.rs
1693 views
1
use std::fmt;
2
use std::rc::Rc;
3
4
use crate::cdsl::camel_case;
5
use crate::cdsl::formats::InstructionFormat;
6
use crate::cdsl::operands::Operand;
7
use crate::cdsl::typevar::TypeVar;
8
9
pub(crate) type AllInstructions = Vec<Instruction>;
10
11
pub(crate) struct InstructionGroupBuilder<'all_inst> {
12
all_instructions: &'all_inst mut AllInstructions,
13
}
14
15
impl<'all_inst> InstructionGroupBuilder<'all_inst> {
16
pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
17
Self { all_instructions }
18
}
19
20
pub fn push(&mut self, builder: InstructionBuilder) {
21
let inst = builder.build();
22
self.all_instructions.push(inst);
23
}
24
}
25
26
#[derive(Debug)]
27
pub(crate) struct PolymorphicInfo {
28
pub use_typevar_operand: bool,
29
pub ctrl_typevar: TypeVar,
30
}
31
32
#[derive(Debug)]
33
pub(crate) struct InstructionContent {
34
/// Instruction mnemonic, also becomes opcode name.
35
pub name: String,
36
pub camel_name: String,
37
38
/// Documentation string.
39
pub doc: String,
40
41
/// Input operands. This can be a mix of SSA value operands and other operand kinds.
42
pub operands_in: Vec<Operand>,
43
/// Output operands. The output operands must be SSA values or `variable_args`.
44
pub operands_out: Vec<Operand>,
45
46
/// Instruction format.
47
pub format: Rc<InstructionFormat>,
48
49
/// One of the input or output operands is a free type variable. None if the instruction is not
50
/// polymorphic, set otherwise.
51
pub polymorphic_info: Option<PolymorphicInfo>,
52
53
/// Indices in operands_in of input operands that are values.
54
pub value_opnums: Vec<usize>,
55
/// Indices in operands_in of input operands that are immediates or entities.
56
pub imm_opnums: Vec<usize>,
57
/// Indices in operands_out of output operands that are values.
58
pub value_results: Vec<usize>,
59
60
/// True for instructions that terminate the block.
61
pub is_terminator: bool,
62
/// True for all branch or jump instructions.
63
pub is_branch: bool,
64
/// Is this a call instruction?
65
pub is_call: bool,
66
/// Is this a return instruction?
67
pub is_return: bool,
68
/// Can this instruction read from memory?
69
pub can_load: bool,
70
/// Can this instruction write to memory?
71
pub can_store: bool,
72
/// Can this instruction cause a trap?
73
pub can_trap: bool,
74
/// Does this instruction have other side effects besides can_* flags?
75
pub other_side_effects: bool,
76
/// Despite having other side effects, is this instruction okay to GVN?
77
pub side_effects_idempotent: bool,
78
}
79
80
impl InstructionContent {
81
pub fn snake_name(&self) -> &str {
82
if &self.name == "return" {
83
"return_"
84
} else {
85
&self.name
86
}
87
}
88
}
89
90
pub(crate) type Instruction = Rc<InstructionContent>;
91
92
impl fmt::Display for InstructionContent {
93
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
94
if !self.operands_out.is_empty() {
95
let operands_out = self
96
.operands_out
97
.iter()
98
.map(|op| op.name)
99
.collect::<Vec<_>>()
100
.join(", ");
101
fmt.write_str(&operands_out)?;
102
fmt.write_str(" = ")?;
103
}
104
105
fmt.write_str(&self.name)?;
106
107
if !self.operands_in.is_empty() {
108
let operands_in = self
109
.operands_in
110
.iter()
111
.map(|op| op.name)
112
.collect::<Vec<_>>()
113
.join(", ");
114
fmt.write_str(" ")?;
115
fmt.write_str(&operands_in)?;
116
}
117
118
Ok(())
119
}
120
}
121
122
pub(crate) struct InstructionBuilder {
123
name: String,
124
doc: String,
125
format: Rc<InstructionFormat>,
126
operands_in: Option<Vec<Operand>>,
127
operands_out: Option<Vec<Operand>>,
128
129
// See Instruction comments for the meaning of these fields.
130
is_terminator: bool,
131
is_branch: bool,
132
is_call: bool,
133
is_return: bool,
134
can_load: bool,
135
can_store: bool,
136
can_trap: bool,
137
other_side_effects: bool,
138
side_effects_idempotent: bool,
139
}
140
141
impl InstructionBuilder {
142
pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
143
Self {
144
name: name.into(),
145
doc: doc.into(),
146
format: format.clone(),
147
operands_in: None,
148
operands_out: None,
149
150
is_terminator: false,
151
is_branch: false,
152
is_call: false,
153
is_return: false,
154
can_load: false,
155
can_store: false,
156
can_trap: false,
157
other_side_effects: false,
158
side_effects_idempotent: false,
159
}
160
}
161
162
pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
163
assert!(self.operands_in.is_none());
164
self.operands_in = Some(operands);
165
self
166
}
167
168
pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
169
assert!(self.operands_out.is_none());
170
self.operands_out = Some(operands);
171
self
172
}
173
174
/// Mark this instruction as a block terminator.
175
pub fn terminates_block(mut self) -> Self {
176
self.is_terminator = true;
177
self
178
}
179
180
/// Mark this instruction as a branch instruction. This also implies that the instruction is a
181
/// block terminator.
182
pub fn branches(mut self) -> Self {
183
self.is_branch = true;
184
self.terminates_block()
185
}
186
187
/// Mark this instruction as a call instruction.
188
pub fn call(mut self) -> Self {
189
self.is_call = true;
190
self
191
}
192
193
/// Mark this instruction as a return instruction. This also implies that the instruction is a
194
/// block terminator.
195
pub fn returns(mut self) -> Self {
196
self.is_return = true;
197
self.terminates_block()
198
}
199
200
/// Mark this instruction as one that can load from memory.
201
pub fn can_load(mut self) -> Self {
202
self.can_load = true;
203
self
204
}
205
206
/// Mark this instruction as one that can store to memory.
207
pub fn can_store(mut self) -> Self {
208
self.can_store = true;
209
self
210
}
211
212
/// Mark this instruction as possibly trapping.
213
pub fn can_trap(mut self) -> Self {
214
self.can_trap = true;
215
self
216
}
217
218
/// Mark this instruction as one that has side-effects.
219
pub fn other_side_effects(mut self) -> Self {
220
self.other_side_effects = true;
221
self
222
}
223
224
/// Mark this instruction as one whose side-effects may be de-duplicated.
225
pub fn side_effects_idempotent(mut self) -> Self {
226
self.side_effects_idempotent = true;
227
self
228
}
229
230
fn build(self) -> Instruction {
231
let operands_in = self.operands_in.unwrap_or_default();
232
let operands_out = self.operands_out.unwrap_or_default();
233
234
let mut value_opnums = Vec::new();
235
let mut imm_opnums = Vec::new();
236
for (i, op) in operands_in.iter().enumerate() {
237
if op.is_value() {
238
value_opnums.push(i);
239
} else if op.is_immediate_or_entityref() {
240
imm_opnums.push(i);
241
} else {
242
assert!(op.is_varargs());
243
}
244
}
245
246
let value_results = operands_out
247
.iter()
248
.enumerate()
249
.filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
250
.collect();
251
252
verify_format(&self.name, &operands_in, &self.format);
253
254
let polymorphic_info =
255
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
256
257
let camel_name = camel_case(&self.name);
258
259
Rc::new(InstructionContent {
260
name: self.name,
261
camel_name,
262
doc: self.doc,
263
operands_in,
264
operands_out,
265
format: self.format,
266
polymorphic_info,
267
value_opnums,
268
value_results,
269
imm_opnums,
270
is_terminator: self.is_terminator,
271
is_branch: self.is_branch,
272
is_call: self.is_call,
273
is_return: self.is_return,
274
can_load: self.can_load,
275
can_store: self.can_store,
276
can_trap: self.can_trap,
277
other_side_effects: self.other_side_effects,
278
side_effects_idempotent: self.side_effects_idempotent,
279
})
280
}
281
}
282
283
/// Checks that the input operands actually match the given format.
284
fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat) {
285
// A format is defined by:
286
// - its number of input value operands,
287
// - its number and names of input immediate operands,
288
// - whether it has a value list or not.
289
let mut num_values = 0;
290
let mut num_blocks = 0;
291
let mut num_raw_blocks = 0;
292
let mut num_immediates = 0;
293
294
for operand in operands_in.iter() {
295
if operand.is_varargs() {
296
assert!(
297
format.has_value_list,
298
"instruction {} has varargs, but its format {} doesn't have a value list; you may \
299
need to use a different format.",
300
inst_name, format.name
301
);
302
}
303
if operand.is_value() {
304
num_values += 1;
305
}
306
if operand.kind.is_block() {
307
num_blocks += 1;
308
} else if operand.kind.is_raw_block() {
309
num_raw_blocks += 1;
310
} else if operand.is_immediate_or_entityref() {
311
if let Some(format_field) = format.imm_fields.get(num_immediates) {
312
assert_eq!(
313
format_field.kind.rust_field_name,
314
operand.kind.rust_field_name,
315
"{}th operand of {} should be {} (according to format), not {} (according to \
316
inst definition). You may need to use a different format.",
317
num_immediates,
318
inst_name,
319
format_field.kind.rust_field_name,
320
operand.kind.rust_field_name
321
);
322
num_immediates += 1;
323
}
324
}
325
}
326
327
assert_eq!(
328
num_values, format.num_value_operands,
329
"inst {} doesn't have as many value input operands as its format {} declares; you may need \
330
to use a different format.",
331
inst_name, format.name
332
);
333
334
assert_eq!(
335
num_blocks, format.num_block_operands,
336
"inst {} doesn't have as many block input operands as its format {} declares; you may need \
337
to use a different format.",
338
inst_name, format.name,
339
);
340
341
assert_eq!(
342
num_raw_blocks, format.num_raw_block_operands,
343
"inst {} doesn't have as many raw-block input operands as its format {} declares; you may need \
344
to use a different format.",
345
inst_name, format.name,
346
);
347
348
assert_eq!(
349
num_immediates,
350
format.imm_fields.len(),
351
"inst {} doesn't have as many immediate input \
352
operands as its format {} declares; you may need to use a different format.",
353
inst_name,
354
format.name
355
);
356
}
357
358
/// Check if this instruction is polymorphic, and verify its use of type variables.
359
fn verify_polymorphic(
360
operands_in: &[Operand],
361
operands_out: &[Operand],
362
format: &InstructionFormat,
363
value_opnums: &[usize],
364
) -> Option<PolymorphicInfo> {
365
// The instruction is polymorphic if it has one free input or output operand.
366
let is_polymorphic = operands_in
367
.iter()
368
.any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
369
|| operands_out
370
.iter()
371
.any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
372
373
if !is_polymorphic {
374
return None;
375
}
376
377
// Verify the use of type variables.
378
let tv_op = format.typevar_operand;
379
let mut maybe_error_message = None;
380
if let Some(tv_op) = tv_op {
381
if tv_op < value_opnums.len() {
382
let op_num = value_opnums[tv_op];
383
let tv = operands_in[op_num].type_var().unwrap();
384
let free_typevar = tv.free_typevar();
385
if (free_typevar.is_some() && tv == &free_typevar.unwrap())
386
|| tv.singleton_type().is_some()
387
{
388
match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
389
Ok(_other_typevars) => {
390
return Some(PolymorphicInfo {
391
use_typevar_operand: true,
392
ctrl_typevar: tv.clone(),
393
});
394
}
395
Err(error_message) => {
396
maybe_error_message = Some(error_message);
397
}
398
}
399
}
400
}
401
};
402
403
// If we reached here, it means the type variable indicated as the typevar operand couldn't
404
// control every other input and output type variable. We need to look at the result type
405
// variables.
406
if operands_out.is_empty() {
407
// No result means no other possible type variable, so it's a type inference failure.
408
match maybe_error_message {
409
Some(msg) => panic!("{}", msg),
410
None => panic!("typevar_operand must be a free type variable"),
411
}
412
}
413
414
// Otherwise, try to infer the controlling type variable by looking at the first result.
415
let tv = operands_out[0].type_var().unwrap();
416
let free_typevar = tv.free_typevar();
417
if free_typevar.is_some() && tv != &free_typevar.unwrap() {
418
panic!("first result must be a free type variable");
419
}
420
421
// At this point, if the next unwrap() fails, it means the output type couldn't be used as a
422
// controlling type variable either; panicking is the right behavior.
423
is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
424
425
Some(PolymorphicInfo {
426
use_typevar_operand: false,
427
ctrl_typevar: tv.clone(),
428
})
429
}
430
431
/// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type
432
/// variable.
433
///
434
/// All polymorphic inputs must either be derived from `ctrl_typevar` or be independent free type
435
/// variables only used once.
436
///
437
/// All polymorphic results must be derived from `ctrl_typevar`.
438
///
439
/// Return a vector of other type variables used, or a string explaining what went wrong.
440
fn is_ctrl_typevar_candidate(
441
ctrl_typevar: &TypeVar,
442
operands_in: &[Operand],
443
operands_out: &[Operand],
444
) -> Result<Vec<TypeVar>, String> {
445
let mut other_typevars = Vec::new();
446
447
// Check value inputs.
448
for input in operands_in {
449
if !input.is_value() {
450
continue;
451
}
452
453
let typ = input.type_var().unwrap();
454
let free_typevar = typ.free_typevar();
455
456
// Non-polymorphic or derived from ctrl_typevar is OK.
457
if free_typevar.is_none() {
458
continue;
459
}
460
let free_typevar = free_typevar.unwrap();
461
if &free_typevar == ctrl_typevar {
462
continue;
463
}
464
465
// No other derived typevars allowed.
466
if typ != &free_typevar {
467
return Err(format!(
468
"{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
469
input, typ.name, ctrl_typevar, free_typevar
470
));
471
}
472
473
// Other free type variables can only be used once each.
474
for other_tv in &other_typevars {
475
if &free_typevar == other_tv {
476
return Err(format!(
477
"non-controlling type variable {} can't be used more than once",
478
free_typevar.name
479
));
480
}
481
}
482
483
other_typevars.push(free_typevar);
484
}
485
486
// Check outputs.
487
for result in operands_out {
488
if !result.is_value() {
489
continue;
490
}
491
492
let typ = result.type_var().unwrap();
493
let free_typevar = typ.free_typevar();
494
495
// Non-polymorphic or derived from ctrl_typevar is OK.
496
if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
497
continue;
498
}
499
500
return Err("type variable in output not derived from ctrl_typevar".into());
501
}
502
503
Ok(other_typevars)
504
}
505
506