Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/constant.rs
1693 views
1
//! Constants
2
//!
3
//! The constant pool defined here allows Cranelift to avoid emitting the same constant multiple
4
//! times. As constants are inserted in the pool, a handle is returned; the handle is a Cranelift
5
//! Entity. Inserting the same data multiple times will always return the same handle.
6
//!
7
//! Future work could include:
8
//! - ensuring alignment of constants within the pool,
9
//! - bucketing constants by size.
10
11
use crate::ir::Constant;
12
use crate::ir::immediates::{Ieee128, IntoBytes, V128Imm};
13
use alloc::collections::BTreeMap;
14
use alloc::vec::Vec;
15
use core::fmt;
16
use core::slice::Iter;
17
use core::str::{FromStr, from_utf8};
18
use cranelift_entity::EntityRef;
19
20
#[cfg(feature = "enable-serde")]
21
use serde_derive::{Deserialize, Serialize};
22
23
/// This type describes the actual constant data. Note that the bytes stored in this structure are
24
/// expected to be in little-endian order; this is due to ease-of-use when interacting with
25
/// WebAssembly values, which are [little-endian by design].
26
///
27
/// [little-endian by design]: https://github.com/WebAssembly/design/blob/master/Portability.md
28
#[derive(Clone, Hash, Eq, PartialEq, Debug, Default, PartialOrd, Ord)]
29
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30
pub struct ConstantData(Vec<u8>);
31
32
impl FromIterator<u8> for ConstantData {
33
fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
34
let v = iter.into_iter().collect();
35
Self(v)
36
}
37
}
38
39
impl From<Vec<u8>> for ConstantData {
40
fn from(v: Vec<u8>) -> Self {
41
Self(v)
42
}
43
}
44
45
impl From<&[u8]> for ConstantData {
46
fn from(v: &[u8]) -> Self {
47
Self(v.to_vec())
48
}
49
}
50
51
impl From<V128Imm> for ConstantData {
52
fn from(v: V128Imm) -> Self {
53
Self(v.to_vec())
54
}
55
}
56
57
impl From<Ieee128> for ConstantData {
58
fn from(v: Ieee128) -> Self {
59
Self(v.into_bytes())
60
}
61
}
62
63
impl TryFrom<&ConstantData> for Ieee128 {
64
type Error = <[u8; 16] as TryFrom<&'static [u8]>>::Error;
65
66
fn try_from(value: &ConstantData) -> Result<Self, Self::Error> {
67
Ok(Ieee128::with_bits(u128::from_le_bytes(
68
value.as_slice().try_into()?,
69
)))
70
}
71
}
72
73
impl ConstantData {
74
/// Return the number of bytes in the constant.
75
pub fn len(&self) -> usize {
76
self.0.len()
77
}
78
79
/// Check if the constant contains any bytes.
80
pub fn is_empty(&self) -> bool {
81
self.0.is_empty()
82
}
83
84
/// Return the data as a slice.
85
pub fn as_slice(&self) -> &[u8] {
86
self.0.as_slice()
87
}
88
89
/// Convert the data to a vector.
90
pub fn into_vec(self) -> Vec<u8> {
91
self.0
92
}
93
94
/// Iterate over the constant's bytes.
95
pub fn iter(&self) -> Iter<'_, u8> {
96
self.0.iter()
97
}
98
99
/// Add new bytes to the constant data.
100
pub fn append(mut self, bytes: impl IntoBytes) -> Self {
101
let mut to_add = bytes.into_bytes();
102
self.0.append(&mut to_add);
103
self
104
}
105
106
/// Expand the size of the constant data to `expected_size` number of bytes by adding zeroes
107
/// in the high-order byte slots.
108
pub fn expand_to(mut self, expected_size: usize) -> Self {
109
if self.len() > expected_size {
110
panic!("The constant data is already expanded beyond {expected_size} bytes")
111
}
112
self.0.resize(expected_size, 0);
113
self
114
}
115
}
116
117
impl fmt::Display for ConstantData {
118
/// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.
119
/// This function will flip the stored order of bytes--little-endian--to the more readable
120
/// big-endian ordering.
121
///
122
/// ```
123
/// use cranelift_codegen::ir::ConstantData;
124
/// let data = ConstantData::from([3, 2, 1, 0, 0].as_ref()); // note the little-endian order
125
/// assert_eq!(data.to_string(), "0x0000010203");
126
/// ```
127
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128
if !self.is_empty() {
129
write!(f, "0x")?;
130
for b in self.0.iter().rev() {
131
write!(f, "{b:02x}")?;
132
}
133
}
134
Ok(())
135
}
136
}
137
138
impl FromStr for ConstantData {
139
type Err = &'static str;
140
141
/// Parse a hexadecimal string to `ConstantData`. This is the inverse of `Display::fmt`.
142
///
143
/// ```
144
/// use cranelift_codegen::ir::ConstantData;
145
/// let c: ConstantData = "0x000102".parse().unwrap();
146
/// assert_eq!(c.into_vec(), [2, 1, 0]);
147
/// ```
148
fn from_str(s: &str) -> Result<Self, &'static str> {
149
if s.len() <= 2 || &s[0..2] != "0x" {
150
return Err("Expected a hexadecimal string, e.g. 0x1234");
151
}
152
153
// clean and check the string
154
let cleaned: Vec<u8> = s[2..]
155
.as_bytes()
156
.iter()
157
.filter(|&&b| b as char != '_')
158
.cloned()
159
.collect(); // remove 0x prefix and any intervening _ characters
160
161
if cleaned.is_empty() {
162
Err("Hexadecimal string must have some digits")
163
} else if cleaned.len() % 2 != 0 {
164
Err("Hexadecimal string must have an even number of digits")
165
} else if cleaned.len() > 32 {
166
Err("Hexadecimal string has too many digits to fit in a 128-bit vector")
167
} else {
168
let mut buffer = Vec::with_capacity((s.len() - 2) / 2);
169
for i in (0..cleaned.len()).step_by(2) {
170
let pair = from_utf8(&cleaned[i..i + 2])
171
.or_else(|_| Err("Unable to parse hexadecimal pair as UTF-8"))?;
172
let byte = u8::from_str_radix(pair, 16)
173
.or_else(|_| Err("Unable to parse as hexadecimal"))?;
174
buffer.insert(0, byte);
175
}
176
Ok(Self(buffer))
177
}
178
}
179
}
180
181
/// Maintains the mapping between a constant handle (i.e. [`Constant`]) and
182
/// its constant data (i.e. [`ConstantData`]).
183
#[derive(Clone, PartialEq, Hash)]
184
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
185
pub struct ConstantPool {
186
/// This mapping maintains the insertion order as long as Constants are created with
187
/// sequentially increasing integers.
188
///
189
/// It is important that, by construction, no entry in that list gets removed. If that ever
190
/// need to happen, don't forget to update the `Constant` generation scheme.
191
handles_to_values: BTreeMap<Constant, ConstantData>,
192
193
/// Mapping of hashed `ConstantData` to the index into the other hashmap.
194
///
195
/// This allows for deduplication of entries into the `handles_to_values` mapping.
196
values_to_handles: BTreeMap<ConstantData, Constant>,
197
}
198
199
impl ConstantPool {
200
/// Create a new constant pool instance.
201
pub fn new() -> Self {
202
Self {
203
handles_to_values: BTreeMap::new(),
204
values_to_handles: BTreeMap::new(),
205
}
206
}
207
208
/// Empty the constant pool of all data.
209
pub fn clear(&mut self) {
210
self.handles_to_values.clear();
211
self.values_to_handles.clear();
212
}
213
214
/// Insert constant data into the pool, returning a handle for later referencing; when constant
215
/// data is inserted that is a duplicate of previous constant data, the existing handle will be
216
/// returned.
217
pub fn insert(&mut self, constant_value: ConstantData) -> Constant {
218
if let Some(cst) = self.values_to_handles.get(&constant_value) {
219
return *cst;
220
}
221
222
let constant_handle = Constant::new(self.len());
223
self.set(constant_handle, constant_value);
224
constant_handle
225
}
226
227
/// Retrieve the constant data given a handle.
228
pub fn get(&self, constant_handle: Constant) -> &ConstantData {
229
assert!(self.handles_to_values.contains_key(&constant_handle));
230
self.handles_to_values.get(&constant_handle).unwrap()
231
}
232
233
/// Link a constant handle to its value. This does not de-duplicate data but does avoid
234
/// replacing any existing constant values. use `set` to tie a specific `const42` to its value;
235
/// use `insert` to add a value and return the next available `const` entity.
236
pub fn set(&mut self, constant_handle: Constant, constant_value: ConstantData) {
237
let replaced = self
238
.handles_to_values
239
.insert(constant_handle, constant_value.clone());
240
assert!(
241
replaced.is_none(),
242
"attempted to overwrite an existing constant {:?}: {:?} => {:?}",
243
constant_handle,
244
&constant_value,
245
replaced.unwrap()
246
);
247
self.values_to_handles
248
.insert(constant_value, constant_handle);
249
}
250
251
/// Iterate over the constants in insertion order.
252
pub fn iter(&self) -> impl Iterator<Item = (&Constant, &ConstantData)> {
253
self.handles_to_values.iter()
254
}
255
256
/// Iterate over mutable entries in the constant pool in insertion order.
257
pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut ConstantData> {
258
self.handles_to_values.values_mut()
259
}
260
261
/// Return the number of constants in the pool.
262
pub fn len(&self) -> usize {
263
self.handles_to_values.len()
264
}
265
266
/// Return the combined size of all of the constant values in the pool.
267
pub fn byte_size(&self) -> usize {
268
self.handles_to_values.values().map(|c| c.len()).sum()
269
}
270
}
271
272
#[cfg(test)]
273
mod tests {
274
use super::*;
275
use std::string::ToString;
276
277
#[test]
278
fn empty() {
279
let sut = ConstantPool::new();
280
assert_eq!(sut.len(), 0);
281
}
282
283
#[test]
284
fn insert() {
285
let mut sut = ConstantPool::new();
286
sut.insert(vec![1, 2, 3].into());
287
sut.insert(vec![4, 5, 6].into());
288
assert_eq!(sut.len(), 2);
289
}
290
291
#[test]
292
fn insert_duplicate() {
293
let mut sut = ConstantPool::new();
294
let a = sut.insert(vec![1, 2, 3].into());
295
sut.insert(vec![4, 5, 6].into());
296
let b = sut.insert(vec![1, 2, 3].into());
297
assert_eq!(a, b);
298
}
299
300
#[test]
301
fn clear() {
302
let mut sut = ConstantPool::new();
303
sut.insert(vec![1, 2, 3].into());
304
assert_eq!(sut.len(), 1);
305
306
sut.clear();
307
assert_eq!(sut.len(), 0);
308
}
309
310
#[test]
311
fn iteration_order() {
312
let mut sut = ConstantPool::new();
313
sut.insert(vec![1, 2, 3].into());
314
sut.insert(vec![4, 5, 6].into());
315
sut.insert(vec![1, 2, 3].into());
316
let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
317
assert_eq!(data, vec![&vec![1, 2, 3].into(), &vec![4, 5, 6].into()]);
318
}
319
320
#[test]
321
fn get() {
322
let mut sut = ConstantPool::new();
323
let data = vec![1, 2, 3];
324
let handle = sut.insert(data.clone().into());
325
assert_eq!(sut.get(handle), &data.into());
326
}
327
328
#[test]
329
fn set() {
330
let mut sut = ConstantPool::new();
331
let handle = Constant::with_number(42).unwrap();
332
let data = vec![1, 2, 3];
333
sut.set(handle, data.clone().into());
334
assert_eq!(sut.get(handle), &data.into());
335
}
336
337
#[test]
338
#[should_panic]
339
fn disallow_overwriting_constant() {
340
let mut sut = ConstantPool::new();
341
let handle = Constant::with_number(42).unwrap();
342
sut.set(handle, vec![].into());
343
sut.set(handle, vec![1].into());
344
}
345
346
#[test]
347
#[should_panic]
348
fn get_nonexistent_constant() {
349
let sut = ConstantPool::new();
350
let a = Constant::with_number(42).unwrap();
351
sut.get(a); // panics, only use constants returned by ConstantPool
352
}
353
354
#[test]
355
fn display_constant_data() {
356
assert_eq!(ConstantData::from([0].as_ref()).to_string(), "0x00");
357
assert_eq!(ConstantData::from([42].as_ref()).to_string(), "0x2a");
358
assert_eq!(
359
ConstantData::from([3, 2, 1, 0].as_ref()).to_string(),
360
"0x00010203"
361
);
362
assert_eq!(
363
ConstantData::from(3735928559u32.to_le_bytes().as_ref()).to_string(),
364
"0xdeadbeef"
365
);
366
assert_eq!(
367
ConstantData::from(0x0102030405060708u64.to_le_bytes().as_ref()).to_string(),
368
"0x0102030405060708"
369
);
370
}
371
372
#[test]
373
fn iterate_over_constant_data() {
374
let c = ConstantData::from([1, 2, 3].as_ref());
375
let mut iter = c.iter();
376
assert_eq!(iter.next(), Some(&1));
377
assert_eq!(iter.next(), Some(&2));
378
assert_eq!(iter.next(), Some(&3));
379
assert_eq!(iter.next(), None);
380
}
381
382
#[test]
383
fn add_to_constant_data() {
384
let d = ConstantData::from([1, 2].as_ref());
385
let e = d.append(i16::from(3u8));
386
assert_eq!(e.into_vec(), vec![1, 2, 3, 0])
387
}
388
389
#[test]
390
fn extend_constant_data() {
391
let d = ConstantData::from([1, 2].as_ref());
392
assert_eq!(d.expand_to(4).into_vec(), vec![1, 2, 0, 0])
393
}
394
395
#[test]
396
#[should_panic]
397
fn extend_constant_data_to_invalid_length() {
398
ConstantData::from([1, 2].as_ref()).expand_to(1);
399
}
400
401
#[test]
402
fn parse_constant_data_and_restringify() {
403
// Verify that parsing of `from` succeeds and stringifies to `to`.
404
fn parse_ok(from: &str, to: &str) {
405
let parsed = from.parse::<ConstantData>().unwrap();
406
assert_eq!(parsed.to_string(), to);
407
}
408
409
// Verify that parsing of `from` fails with `error_msg`.
410
fn parse_err(from: &str, error_msg: &str) {
411
let parsed = from.parse::<ConstantData>();
412
assert!(
413
parsed.is_err(),
414
"Expected a parse error but parsing succeeded: {from}"
415
);
416
assert_eq!(parsed.err().unwrap(), error_msg);
417
}
418
419
parse_ok("0x00", "0x00");
420
parse_ok("0x00000042", "0x00000042");
421
parse_ok(
422
"0x0102030405060708090a0b0c0d0e0f00",
423
"0x0102030405060708090a0b0c0d0e0f00",
424
);
425
parse_ok("0x_0000_0043_21", "0x0000004321");
426
427
parse_err("", "Expected a hexadecimal string, e.g. 0x1234");
428
parse_err("0x", "Expected a hexadecimal string, e.g. 0x1234");
429
parse_err(
430
"0x042",
431
"Hexadecimal string must have an even number of digits",
432
);
433
parse_err(
434
"0x00000000000000000000000000000000000000000000000000",
435
"Hexadecimal string has too many digits to fit in a 128-bit vector",
436
);
437
parse_err("0xrstu", "Unable to parse as hexadecimal");
438
parse_err("0x__", "Hexadecimal string must have some digits");
439
}
440
441
#[test]
442
fn verify_stored_bytes_in_constant_data() {
443
assert_eq!("0x01".parse::<ConstantData>().unwrap().into_vec(), [1]);
444
assert_eq!(ConstantData::from([1, 0].as_ref()).0, [1, 0]);
445
assert_eq!(ConstantData::from(vec![1, 0, 0, 0]).0, [1, 0, 0, 0]);
446
}
447
448
#[test]
449
fn check_constant_data_endianness_as_uimm128() {
450
fn parse_to_uimm128(from: &str) -> Vec<u8> {
451
from.parse::<ConstantData>()
452
.unwrap()
453
.expand_to(16)
454
.into_vec()
455
}
456
457
assert_eq!(
458
parse_to_uimm128("0x42"),
459
[0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
460
);
461
assert_eq!(
462
parse_to_uimm128("0x00"),
463
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
464
);
465
assert_eq!(
466
parse_to_uimm128("0x12345678"),
467
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
468
);
469
assert_eq!(
470
parse_to_uimm128("0x1234_5678"),
471
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
472
);
473
}
474
475
#[test]
476
fn constant_ieee128() {
477
let value = Ieee128::with_bits(0x000102030405060708090a0b0c0d0e0f);
478
let constant = ConstantData::from(value);
479
assert_eq!(
480
constant.as_slice(),
481
&[
482
0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0
483
]
484
);
485
assert_eq!(Ieee128::try_from(&constant).unwrap().bits(), value.bits());
486
}
487
}
488
489