dynasm\arch\riscv/
matching.rs

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
use proc_macro_error2::emit_error;
use proc_macro2::Span;

use super::{Context, RiscVTarget};
use super::ast::{ParsedInstruction, RawArg, RegListCount, MatchData, FlatArg, RegListFlat, Register, RegId, RegFamily};
use super::riscvdata::{Opdata, Matcher, ISAFlags, get_mnemonic_data};
use super::debug::format_opdata_list;

use crate::common::JumpKind;
use crate::parse_helpers::{as_ident, as_signed_number};

/// Try finding an appropriate instruction definition that matches the given instruction / arguments.
pub(super) fn match_instruction(ctx: &mut Context, mut instruction: ParsedInstruction) -> Result<MatchData, Option<String>> {
    // sanitize Raw args from parsing for any impossible constructs
    sanitize_args(&mut instruction.args, &ctx.target)?;

    let opdata = get_mnemonic_data(&instruction.name).ok_or_else(|| Some(format!("Unknown instruction mnemonic '{}'", instruction.name)))?;

    let mut rejected_because_features = false;

    // iterate through the supported instruction formats. If one matches, lower the args to
    // FlatArgs and return the combined Matchdata
    for data in opdata {
        // skip data not intended for our target
        if ctx.target.is_32_bit() && !data.isa_flags.contains(ISAFlags::RV32) {
            continue;
        }
        if ctx.target.is_64_bit() && !data.isa_flags.contains(ISAFlags::RV64) {
            continue;
        }
        if !data.ext_flags.iter().any(|f| ctx.features.contains(*f)) {
            rejected_because_features = true;
            continue;
        }

        if let Some(mut match_data) = match_args(&instruction.args, data) {
            flatten_args(instruction.args, &mut match_data);

            return Ok(match_data)
        }
    }

    let mut error = format!("'{}': instruction format mismatch, expected one of the following forms:\n{}", &instruction.name, format_opdata_list(&instruction.name, opdata, ctx.target));
    if rejected_because_features {
        error.push_str("\nNote: some instruction formats were rejected because of inactive ISA extension sets.");
    }

    Err(Some(error))
}

/// Sanitizes arguments, ensuring that
/// register lists always contain the expected {ra, s0-s11} registers
/// Extern relocations are not allowed
/// No registers above 15 are used on E-profile RISCV
/// Canonicalize `0(register)` references as without offset (like `(register)`)
fn sanitize_args(args: &mut [RawArg], target: &RiscVTarget) -> Result<(), Option<String>> {
    for arg in args {
        match arg {
            RawArg::Register { reg, span } => sanitize_register(reg, *span, target)?,
            RawArg::Reference { base, span, offset } => {
                sanitize_register(base, *span, target)?;
                if base.family() != RegFamily::INTEGER {
                    emit_error!(span, "Base register needs to be a regular (integer) register");
                    return Err(None);
                }

                if let Some(o) = offset.as_ref() {
                    if as_signed_number(o) == Some(0) {
                        *offset = None
                    }
                }
            },
            RawArg::LabelReference { span, base, jump } => {
                sanitize_register(base, *span, target)?;
                if base.family() != RegFamily::INTEGER {
                    emit_error!(span, "Base register needs to be a regular (integer) register");
                    return Err(None);
                }

                if let JumpKind::Bare(_) = jump.kind {
                    emit_error!(jump.span(), "Extern relocations are not allowed in riscv64");
                    return Err(None);
                }
            },
            RawArg::JumpTarget { jump } => {
                if let JumpKind::Bare(_) = jump.kind {
                    emit_error!(jump.span(), "Extern relocations are not allowed in riscv64");
                    return Err(None);
                }
            },
            RawArg::RegisterList { first, count, span } => {
                if first.as_id() != Some(RegId::X1) {
                    emit_error!(span, "The first item in a register list should be 'ra' (x1)");
                    return Err(None);
                }

                match count {
                    RegListCount::Static(_) => (),
                    RegListCount::Dynamic(_) => (),
                    RegListCount::Single(first) => {
                        if first.as_id() != Some(RegId::X8) {
                            emit_error!(span, "The second item in a register list should be 's0' (x8)");
                            return Err(None);
                        }
                        *count = RegListCount::Static(5);
                    },
                    RegListCount::Double(first, last) => {
                        if first.as_id() != Some(RegId::X8) {
                            emit_error!(span, "The second item in a register list should be 's0' (x8)");
                            return Err(None);
                        }
                        let amount = match last {
                            Register::Dynamic(_, _) => {
                                emit_error!(span, "Please use the {ra; amount} format to specify the amount of registers in a list dynamically");
                                return Err(None);
                            },
                            Register::Static(RegId::X9) => 6,
                            Register::Static(RegId::X18) => 7,
                            Register::Static(RegId::X19) => 8,
                            Register::Static(RegId::X20) => 9,
                            Register::Static(RegId::X21) => 10,
                            Register::Static(RegId::X22) => 11,
                            Register::Static(RegId::X23) => 12,
                            Register::Static(RegId::X24) => 13,
                            Register::Static(RegId::X25) => 14,
                            Register::Static(RegId::X27) => 15,
                            Register::Static(_) => {
                                emit_error!(span, "Cannot end a register list on this register. The last register should be a saved register that is not s10 or s0");
                                return Err(None);
                            }
                        };
                        if target.is_embedded() && amount > 6 {
                            emit_error!(span, "Registers above x15 cannot be used on RV-E profiles");
                            return Err(None)
                        }

                        *count = RegListCount::Static(amount);
                    }
                }
            },
            _ => ()
        }
    }

    Ok(())
}

/// Sanitize a single register, checking that on RV32/64E only the top 16 registers are used.
fn sanitize_register(register: &Register, span: Span, target: &RiscVTarget) -> Result<(), Option<String>> {
    // RV32E/RV64E only define 16 integer registers.
    // (They do still define 32 floating point registers if F is used)
    if target.is_embedded() && register.family() == RegFamily::INTEGER {
        if let Some(code) = register.code() {
            if code >= 16 {
                emit_error!(span, "The second item in a register list should be 's0' (x8)");
                return Err(None);
            }
        }
    }
    Ok(())
}


impl MatchData {
    pub fn new(data: &'static Opdata) -> MatchData {
        MatchData {
            data,
            args: Vec::new()
        }
    }
}


impl Matcher {
    /// Returns if this matcher matches the given argument
    pub fn matches(&self, arg: &RawArg) -> bool {
        match arg {
            RawArg::Immediate { value } => match self {
                Matcher::Imm => true,
                Matcher::Offset => true,
                Matcher::Ident => as_ident(value).is_some(),
                Matcher::Lit(literal) => as_ident(value).map_or(false, |v| v == literal.as_str()),
                _ => false
            },
            RawArg::JumpTarget { jump } => *self == Matcher::Offset,
            RawArg::Register { reg, .. }=> match self {
                Matcher::X => reg.family() == RegFamily::INTEGER,
                Matcher::F => reg.family() == RegFamily::FP,
                Matcher::Reg(regid) => reg.as_id() == Some(*regid),
                _ => false,
            },
            RawArg::Reference { offset, base, .. } => match self {
                Matcher::Ref => offset.is_none(),
                Matcher::RefOffset => true,
                Matcher::RefSp => base.as_id() == Some(RegId::X2),
                _ => false,
            },
            RawArg::LabelReference { .. } => matches!(self, Matcher::RefLabel),
            RawArg::RegisterList { first, count, .. } => matches!(self, Matcher::Xlist),
        }
    }
}


/// Check if the parsed instruction arguments match the data matching template
pub fn match_args(args: &[RawArg], data: &'static Opdata) -> Option<MatchData> {
    let mut args = args.iter();

    // check if each matcher matches an appropriate arg
    for matcher in data.matchers {
        if let Some(arg) = args.next() {
            if !matcher.matches(arg) {
                return None;
            }
        } else {
            return None;
        }
    }

    // and return success if there's no more args remaining to match
    if args.next().is_some() {
        None
    } else {
        Some(MatchData::new(data))
    }
}


/// Populate MatchData with FlatArgs
fn flatten_args(args: Vec<RawArg>, data: &mut MatchData) {
    for (arg, matcher) in args.into_iter().zip(data.data.matchers.iter()) {
        match arg {
            RawArg::Immediate { value } => match matcher {
                Matcher::Lit(_) => (),
                _ => data.args.push(FlatArg::Immediate { value }),
            },
            RawArg::JumpTarget { jump } => {
                data.args.push(FlatArg::JumpTarget { jump });
            },
            RawArg::Register { span, reg } => match matcher {
                Matcher::Reg(_) => (),
                _ => data.args.push(FlatArg::Register { span, reg })
            },
            RawArg::Reference { span, offset, base } => match matcher {
                Matcher::RefOffset => {
                    data.args.push(FlatArg::Register { span, reg: base });
                    if let Some(offset) = offset {
                        data.args.push(FlatArg::Immediate { value: offset });
                    } else {
                        data.args.push(FlatArg::Default);
                    }
                },
                Matcher::Ref => {
                    data.args.push(FlatArg::Register { span, reg: base });
                },
                Matcher::RefSp => {
                    if let Some(offset) = offset {
                        data.args.push(FlatArg::Immediate { value: offset });
                    } else {
                        data.args.push(FlatArg::Default);
                    }
                },
                _ => unreachable!("Expected reference")
            },
            RawArg::LabelReference { span, jump, base } => {
                data.args.push(FlatArg::Register {span, reg: base });
                data.args.push(FlatArg::JumpTarget { jump });
            },
            RawArg::RegisterList {span, first, count } => {
                let count = match count {
                    RegListCount::Static(c) => RegListFlat::Static(c),
                    RegListCount::Dynamic(expr) => RegListFlat::Dynamic(expr),
                    _ => unreachable!("RegListCount ought to be sanitized at this point.")
                };
                data.args.push(FlatArg::RegisterList { span, count });
            }
        }
    }
}