1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
///! Taken from the
///! [librustc_target](https://github.com/rust-lang/rust/tree/master/src/librustc_target) crate.
mod align;
mod integer;
mod size;

use crate::spec::Target;

pub use align::Align;
pub use integer::Integer;
pub use size::Size;

/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) for a target, which contains
/// everything needed to compute layouts.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct TargetDataLayout {
    pub endian: Endian,
    pub i1_align: AbiAndPrefAlign,
    pub i8_align: AbiAndPrefAlign,
    pub i16_align: AbiAndPrefAlign,
    pub i32_align: AbiAndPrefAlign,
    pub i64_align: AbiAndPrefAlign,
    pub i128_align: AbiAndPrefAlign,
    pub f32_align: AbiAndPrefAlign,
    pub f64_align: AbiAndPrefAlign,
    pub pointer_size: Size,
    pub pointer_align: AbiAndPrefAlign,
    pub aggregate_align: AbiAndPrefAlign,

    /// Alignments for vector types.
    pub vector_align: Vec<(Size, AbiAndPrefAlign)>,

    pub instruction_address_space: u32,
}

impl Default for TargetDataLayout {
    /// Creates an instance of `TargetDataLayout`.
    fn default() -> TargetDataLayout {
        let align = |bits| Align::from_bits(bits).unwrap();
        TargetDataLayout {
            endian: Endian::Big,
            i1_align: AbiAndPrefAlign::new(align(8)),
            i8_align: AbiAndPrefAlign::new(align(8)),
            i16_align: AbiAndPrefAlign::new(align(16)),
            i32_align: AbiAndPrefAlign::new(align(32)),
            i64_align: AbiAndPrefAlign {
                abi: align(32),
                pref: align(64),
            },
            i128_align: AbiAndPrefAlign {
                abi: align(32),
                pref: align(64),
            },
            f32_align: AbiAndPrefAlign::new(align(32)),
            f64_align: AbiAndPrefAlign::new(align(64)),
            pointer_size: Size::from_bits(64),
            pointer_align: AbiAndPrefAlign::new(align(64)),
            aggregate_align: AbiAndPrefAlign {
                abi: align(0),
                pref: align(64),
            },
            vector_align: vec![
                (Size::from_bits(64), AbiAndPrefAlign::new(align(64))),
                (Size::from_bits(128), AbiAndPrefAlign::new(align(128))),
            ],
            instruction_address_space: 0,
        }
    }
}

impl TargetDataLayout {
    pub fn parse(target: &Target) -> Result<TargetDataLayout, String> {
        // Parse an address space index from a string.
        let parse_address_space = |s: &str, cause: &str| {
            s.parse::<u32>().map_err(|err| {
                format!(
                    "invalid address space `{}` for `{}` in \"data-layout\": {}",
                    s, cause, err
                )
            })
        };

        // Parse a bit count from a string.
        let parse_bits = |s: &str, kind: &str, cause: &str| {
            s.parse::<u64>().map_err(|err| {
                format!(
                    "invalid {} `{}` for `{}` in \"data-layout\": {}",
                    kind, s, cause, err
                )
            })
        };

        // Parse a size string.
        let size = |s: &str, cause: &str| parse_bits(s, "size", cause).map(Size::from_bits);

        // Parse an alignment string.
        let align = |s: &[&str], cause: &str| {
            if s.is_empty() {
                return Err(format!(
                    "missing alignment for `{}` in \"data-layout\"",
                    cause
                ));
            }
            let align_from_bits = |bits| {
                Align::from_bits(bits).map_err(|err| {
                    format!(
                        "invalid alignment for `{}` in \"data-layout\": {}",
                        cause, err
                    )
                })
            };
            let abi = parse_bits(s[0], "alignment", cause)?;
            let pref = s
                .get(1)
                .map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
            Ok(AbiAndPrefAlign {
                abi: align_from_bits(abi)?,
                pref: align_from_bits(pref)?,
            })
        };

        let mut dl = TargetDataLayout::default();
        let mut i128_align_src = 64;
        for spec in target.data_layout.split('-') {
            let spec_parts = spec.split(':').collect::<Vec<_>>();

            match &*spec_parts {
                ["e"] => dl.endian = Endian::Little,
                ["E"] => dl.endian = Endian::Big,
                [p] if p.starts_with('P') => {
                    dl.instruction_address_space = parse_address_space(&p[1..], "P")?
                }
                ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
                ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
                ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
                [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
                    dl.pointer_size = size(s, p)?;
                    dl.pointer_align = align(a, p)?;
                }
                [s, ref a @ ..] if s.starts_with('i') => {
                    let bits = match s[1..].parse::<u64>() {
                        Ok(bits) => bits,
                        Err(_) => {
                            size(&s[1..], "i")?; // For the user error.
                            continue;
                        }
                    };
                    let a = align(a, s)?;
                    match bits {
                        1 => dl.i1_align = a,
                        8 => dl.i8_align = a,
                        16 => dl.i16_align = a,
                        32 => dl.i32_align = a,
                        64 => dl.i64_align = a,
                        _ => {}
                    }
                    if bits >= i128_align_src && bits <= 128 {
                        // Default alignment for i128 is decided by taking the alignment of
                        // largest-sized i{64..=128}.
                        i128_align_src = bits;
                        dl.i128_align = a;
                    }
                }
                [s, ref a @ ..] if s.starts_with('v') => {
                    let v_size = size(&s[1..], "v")?;
                    let a = align(a, s)?;
                    if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
                        v.1 = a;
                        continue;
                    }
                    // No existing entry, add a new one.
                    dl.vector_align.push((v_size, a));
                }
                _ => {} // Ignore everything else.
            }
        }

        // Perform consistency checks against the Target information.
        let endian_str = match dl.endian {
            Endian::Little => "little",
            Endian::Big => "big",
        };
        if endian_str != target.target_endian {
            return Err(format!(
                "inconsistent target specification: \"data-layout\" claims \
                                architecture is {}-endian, while \"target-endian\" is `{}`",
                endian_str, target.target_endian
            ));
        }

        if dl.pointer_size.bits().to_string() != target.target_pointer_width {
            return Err(format!(
                "inconsistent target specification: \"data-layout\" claims \
                                pointers are {}-bit, while \"target-pointer-width\" is `{}`",
                dl.pointer_size.bits(),
                target.target_pointer_width
            ));
        }

        Ok(dl)
    }

    // /// Returns exclusive upper bound on object size.
    // ///
    // /// The theoretical maximum object size is defined as the maximum positive `isize` value.
    // /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly
    // /// index every address within an object along with one byte past the end, along with allowing
    // /// `isize` to store the difference between any two pointers into an object.
    // ///
    // /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer
    // /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is
    // /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable
    // /// address space on 64-bit ARMv8 and x86_64.
    // pub fn obj_size_bound(&self) -> u64 {
    //     match self.pointer_size.bits() {
    //         16 => 1 << 15,
    //         32 => 1 << 31,
    //         64 => 1 << 47,
    //         bits => panic!("obj_size_bound: unknown pointer bit size {}", bits),
    //     }
    // }

    pub fn ptr_sized_integer(&self) -> Integer {
        use Integer::*;
        match self.pointer_size.bits() {
            16 => I16,
            32 => I32,
            64 => I64,
            bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits),
        }
    }

    // pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
    //     for &(size, align) in &self.vector_align {
    //         if size == vec_size {
    //             return align;
    //         }
    //     }
    //     // Default to natural alignment, which is what LLVM does.
    //     // That is, use the size, rounded up to a power of 2.
    //     AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
    // }
}

// pub trait HasDataLayout {
//     fn data_layout(&self) -> &TargetDataLayout;
// }
//
// impl HasDataLayout for TargetDataLayout {
//     fn data_layout(&self) -> &TargetDataLayout {
//         self
//     }
// }

/// Endianness of the target
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Endian {
    Little,
    Big,
}

/// A pair of alignments, ABI-mandated and preferred.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct AbiAndPrefAlign {
    pub abi: Align,
    pub pref: Align,
}

impl AbiAndPrefAlign {
    pub fn new(align: Align) -> AbiAndPrefAlign {
        AbiAndPrefAlign {
            abi: align,
            pref: align,
        }
    }

    // pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
    //     AbiAndPrefAlign {
    //         abi: self.abi.min(other.abi),
    //         pref: self.pref.min(other.pref),
    //     }
    // }
    //
    // pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
    //     AbiAndPrefAlign {
    //         abi: self.abi.max(other.abi),
    //         pref: self.pref.max(other.pref),
    //     }
    // }
}