bind/bytecode_encoder/
encode_v1.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::bytecode_encoder::error::BindRulesEncodeError;
6use crate::compiler::instruction::{Condition, Instruction, InstructionInfo};
7use crate::compiler::{BindRules, BindRulesDecodeError, Symbol, SymbolicInstructionInfo};
8
9use bitfield::bitfield;
10use byteorder::ByteOrder;
11use num_derive::FromPrimitive;
12use std::fmt;
13
14/// Functions for encoding the old bytecode format. These functions need to be
15/// deleted once the old bytecode is fully deprecated.
16
17/// These should match the values in <ddk/binding.h>, e.g. OP_ABORT = 0
18#[derive(FromPrimitive, PartialEq, Clone, Copy)]
19pub enum RawOp {
20    Abort = 0,
21    Match,
22    Goto,
23    Label = 5,
24}
25
26/// These should match the values in <ddk/binding.h>, e.g. COND_AL = 0
27#[derive(FromPrimitive, PartialEq, Clone, Copy, Debug)]
28pub enum RawCondition {
29    Always = 0,
30    Equal,
31    NotEqual,
32}
33
34bitfield! {
35    /// Each instruction is three 32 bit unsigned integers divided as follows.
36    /// lsb                    msb
37    /// COAABBBB VVVVVVVV DDDDDDDD Condition Opcode paramA paramB Value Debug
38    ///
39    /// The Debug field contains the following information:
40    ///  - line: The source code line which the instruction was compiled from.
41    ///  - ast_location: Where in the AST the instruction was compiled from, encoded by the
42    ///      RawAstLocation enum.
43    ///  - extra: Additional debugging information, meaning depends on the value of ast_location.
44    ///      If ast_location is AcceptStatementFailure, extra is the key of the accept statement.
45    ///      Otherwise, extra is unused.
46    pub struct RawInstruction([u32]);
47    u32;
48    pub condition, set_condition: 31, 28;
49    pub operation, set_operation: 27, 24;
50    pub parameter_a, set_parameter_a: 23, 16;
51    pub parameter_b, set_parameter_b: 15, 0;
52    pub value, set_value: 63, 32;
53    pub line, set_line: 95, 88;
54    pub ast_location, set_ast_location: 87, 80;
55    pub extra, set_extra: 79, 64;
56}
57
58impl fmt::Display for RawInstruction<[u32; 3]> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(
61            f,
62            "c: {}, o: {}, a: {}, b: {:#06x}, v: {:#010x}",
63            self.condition(),
64            self.operation(),
65            self.parameter_a(),
66            self.parameter_b(),
67            self.value()
68        )
69    }
70}
71
72pub fn to_raw_instruction(
73    instruction: Instruction,
74) -> Result<RawInstruction<[u32; 3]>, BindRulesEncodeError> {
75    let (c, o, a, b, v) = match instruction {
76        Instruction::Abort(condition) => {
77            let (c, b, v) = encode_condition(condition)?;
78            Ok((c, RawOp::Abort as u32, 0, b, v))
79        }
80        Instruction::Match(condition) => {
81            let (c, b, v) = encode_condition(condition)?;
82            Ok((c, RawOp::Match as u32, 0, b, v))
83        }
84        Instruction::Goto(condition, a) => {
85            let (c, b, v) = encode_condition(condition)?;
86            Ok((c, RawOp::Goto as u32, a, b, v))
87        }
88        Instruction::Label(a) => Ok((RawCondition::Always as u32, RawOp::Label as u32, a, 0, 0)),
89    }?;
90
91    let mut raw_instruction = RawInstruction([0, 0, 0]);
92    raw_instruction.set_condition(c);
93    raw_instruction.set_operation(o);
94    raw_instruction.set_parameter_a(a);
95    raw_instruction.set_parameter_b(b);
96    raw_instruction.set_value(v);
97    Ok(raw_instruction)
98}
99
100fn encode_condition(condition: Condition) -> Result<(u32, u32, u32), BindRulesEncodeError> {
101    match condition {
102        Condition::Always => Ok((RawCondition::Always as u32, 0, 0)),
103        Condition::Equal(b, v) => {
104            let b_sym = encode_symbol(b)?;
105            let v_sym = encode_symbol(v)?;
106            Ok((RawCondition::Equal as u32, b_sym, v_sym))
107        }
108        Condition::NotEqual(b, v) => {
109            let b_sym = encode_symbol(b)?;
110            let v_sym = encode_symbol(v)?;
111            Ok((RawCondition::NotEqual as u32, b_sym, v_sym))
112        }
113    }
114}
115
116pub fn encode_symbol(symbol: Symbol) -> Result<u32, BindRulesEncodeError> {
117    // The old bytecode format can only support numeric values.
118    match symbol {
119        Symbol::DeprecatedKey(value) => Ok(value),
120        Symbol::NumberValue(value64) => match u32::try_from(value64) {
121            Ok(value32) => Ok(value32),
122            _ => Err(BindRulesEncodeError::IntegerOutOfRange),
123        },
124        _ => Err(BindRulesEncodeError::UnsupportedSymbol),
125    }
126}
127
128pub fn encode_instruction(
129    info: InstructionInfo,
130) -> Result<RawInstruction<[u32; 3]>, BindRulesEncodeError> {
131    let mut raw_instruction = to_raw_instruction(info.instruction)?;
132    raw_instruction.set_line(info.debug.line);
133    raw_instruction.set_ast_location(info.debug.ast_location as u32);
134    raw_instruction.set_extra(info.debug.extra);
135    Ok(raw_instruction)
136}
137
138pub fn decode_from_bytecode_v1(
139    bytes: &Vec<u8>,
140) -> Result<Vec<RawInstruction<[u32; 3]>>, BindRulesDecodeError> {
141    if bytes.len() % 12 != 0 {
142        return Err(BindRulesDecodeError::InvalidBinaryLength);
143    }
144    let mut instructions = Vec::<RawInstruction<[u32; 3]>>::new();
145    for i in (0..bytes.len()).step_by(12) {
146        let first = byteorder::LittleEndian::read_u32(&bytes[i..(i + 4)]);
147        let second = byteorder::LittleEndian::read_u32(&bytes[(i + 4)..(i + 8)]);
148        let third = byteorder::LittleEndian::read_u32(&bytes[(i + 8)..(i + 12)]);
149        instructions.push(RawInstruction([first, second, third]));
150    }
151    Ok(instructions)
152}
153
154pub fn encode_to_bytecode_v1(bind_rules: BindRules) -> Result<Vec<u8>, BindRulesEncodeError> {
155    let result = bind_rules
156        .instructions
157        .into_iter()
158        .map(|inst| encode_instruction(inst.to_instruction()))
159        .collect::<Result<Vec<_>, BindRulesEncodeError>>()?;
160    Ok(result
161        .into_iter()
162        .flat_map(|RawInstruction([a, b, c])| {
163            [a.to_le_bytes(), b.to_le_bytes(), c.to_le_bytes()].concat()
164        })
165        .collect::<Vec<_>>())
166}
167
168pub fn encode_to_string_v1<'a>(
169    instructions: Vec<SymbolicInstructionInfo<'a>>,
170) -> Result<String, BindRulesEncodeError> {
171    let result = instructions
172        .into_iter()
173        .map(|inst| encode_instruction(inst.to_instruction()))
174        .collect::<Result<Vec<_>, BindRulesEncodeError>>()?;
175    #[allow(clippy::format_collect, reason = "mass allow for https://fxbug.dev/381896734")]
176    Ok(result
177        .into_iter()
178        .map(|RawInstruction([word0, word1, word2])| {
179            format!("{{{:#x},{:#x},{:#x}}},", word0, word1, word2)
180        })
181        .collect::<String>())
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::compiler::SymbolicInstruction;
188    use std::collections::HashMap;
189
190    #[test]
191    fn test_raw_instruction() {
192        let mut raw = RawInstruction([0, 0]);
193        assert_eq!(raw.0[0], 0);
194        assert_eq!(raw.0[1], 0);
195        assert_eq!(raw.condition(), 0);
196        assert_eq!(raw.operation(), 0);
197        assert_eq!(raw.parameter_a(), 0);
198        assert_eq!(raw.parameter_b(), 0);
199        assert_eq!(raw.value(), 0);
200
201        raw.set_condition(1);
202        assert_eq!(raw.0[0], 1 << 28);
203        assert_eq!(raw.0[1], 0);
204        assert_eq!(raw.condition(), 1);
205        assert_eq!(raw.operation(), 0);
206        assert_eq!(raw.parameter_a(), 0);
207        assert_eq!(raw.parameter_b(), 0);
208        assert_eq!(raw.value(), 0);
209
210        raw.set_operation(2);
211        assert_eq!(raw.0[0], (1 << 28) | (2 << 24));
212        assert_eq!(raw.0[1], 0);
213        assert_eq!(raw.condition(), 1);
214        assert_eq!(raw.operation(), 2);
215        assert_eq!(raw.parameter_a(), 0);
216        assert_eq!(raw.parameter_b(), 0);
217        assert_eq!(raw.value(), 0);
218
219        raw.set_parameter_a(3);
220        assert_eq!(raw.0[0], (1 << 28) | (2 << 24) | (3 << 16));
221        assert_eq!(raw.0[1], 0);
222        assert_eq!(raw.condition(), 1);
223        assert_eq!(raw.operation(), 2);
224        assert_eq!(raw.parameter_a(), 3);
225        assert_eq!(raw.parameter_b(), 0);
226        assert_eq!(raw.value(), 0);
227
228        raw.set_parameter_b(4);
229        assert_eq!(raw.0[0], (1 << 28) | (2 << 24) | (3 << 16) | 4);
230        assert_eq!(raw.0[1], 0);
231        assert_eq!(raw.condition(), 1);
232        assert_eq!(raw.operation(), 2);
233        assert_eq!(raw.parameter_a(), 3);
234        assert_eq!(raw.parameter_b(), 4);
235        assert_eq!(raw.value(), 0);
236
237        raw.set_value(5);
238        assert_eq!(raw.0[0], (1 << 28) | (2 << 24) | (3 << 16) | 4);
239        assert_eq!(raw.0[1], 5);
240        assert_eq!(raw.condition(), 1);
241        assert_eq!(raw.operation(), 2);
242        assert_eq!(raw.parameter_a(), 3);
243        assert_eq!(raw.parameter_b(), 4);
244        assert_eq!(raw.value(), 5)
245    }
246
247    #[test]
248    fn test_abort_value() {
249        let instruction = Instruction::Abort(Condition::Always);
250        let raw_instruction = to_raw_instruction(instruction).unwrap();
251        assert_eq!(raw_instruction.0[0], 0 << 4);
252        assert_eq!(raw_instruction.0[1], 0);
253        assert_eq!(raw_instruction.operation(), 0)
254    }
255
256    #[test]
257    fn test_match_value() {
258        let instruction = Instruction::Match(Condition::Always);
259        let raw_instruction = to_raw_instruction(instruction).unwrap();
260        assert_eq!(raw_instruction.0[0], 1 << 24);
261        assert_eq!(raw_instruction.0[1], 0);
262        assert_eq!(raw_instruction.operation(), 1)
263    }
264
265    #[test]
266    fn test_goto_value() {
267        let instruction = Instruction::Goto(Condition::Always, 0);
268        let raw_instruction = to_raw_instruction(instruction).unwrap();
269        assert_eq!(raw_instruction.0[0], 2 << 24);
270        assert_eq!(raw_instruction.0[1], 0);
271        assert_eq!(raw_instruction.operation(), 2)
272    }
273
274    #[test]
275    fn test_label_value() {
276        let instruction = Instruction::Label(0);
277        let raw_instruction = to_raw_instruction(instruction).unwrap();
278        assert_eq!(raw_instruction.0[0], 5 << 24);
279        assert_eq!(raw_instruction.0[1], 0);
280        assert_eq!(raw_instruction.operation(), 5)
281    }
282
283    #[test]
284    fn test_complicated_value() {
285        let instruction = Instruction::Goto(
286            Condition::Equal(Symbol::NumberValue(23), Symbol::NumberValue(1234)),
287            42,
288        );
289        let raw_instruction = to_raw_instruction(instruction).unwrap();
290        assert_eq!(raw_instruction.0[0], (1 << 28) | (2 << 24) | (42 << 16) | 23);
291        assert_eq!(raw_instruction.0[1], 1234);
292        assert_eq!(raw_instruction.condition(), 1);
293        assert_eq!(raw_instruction.operation(), 2);
294        assert_eq!(raw_instruction.parameter_a(), 42);
295        assert_eq!(raw_instruction.parameter_b(), 23);
296        assert_eq!(raw_instruction.value(), 1234);
297    }
298
299    #[test]
300    fn test_unsupported_symbols() {
301        let bind_rules = BindRules {
302            instructions: vec![SymbolicInstructionInfo {
303                location: None,
304                instruction: SymbolicInstruction::AbortIfNotEqual {
305                    lhs: Symbol::StringValue("kingbird".to_string()),
306                    rhs: Symbol::StringValue("flycatcher".to_string()),
307                },
308            }],
309            symbol_table: HashMap::new(),
310            use_new_bytecode: false,
311            enable_debug: false,
312        };
313
314        assert_eq!(Err(BindRulesEncodeError::UnsupportedSymbol), encode_to_bytecode_v1(bind_rules));
315
316        let bind_rules = BindRules {
317            instructions: vec![SymbolicInstructionInfo {
318                location: None,
319                instruction: SymbolicInstruction::AbortIfNotEqual {
320                    lhs: Symbol::NumberValue(u64::MAX),
321                    rhs: Symbol::NumberValue(0),
322                },
323            }],
324            symbol_table: HashMap::new(),
325            use_new_bytecode: false,
326            enable_debug: false,
327        };
328
329        assert_eq!(Err(BindRulesEncodeError::IntegerOutOfRange), encode_to_bytecode_v1(bind_rules));
330    }
331
332    #[test]
333    fn test_decode_wrong_size() {
334        let bytes = vec![1u8; 3];
335        assert_eq!(
336            decode_from_bytecode_v1(&bytes).err(),
337            Some(BindRulesDecodeError::InvalidBinaryLength)
338        );
339
340        let bytes = vec![1u8; 13];
341        assert_eq!(
342            decode_from_bytecode_v1(&bytes).err(),
343            Some(BindRulesDecodeError::InvalidBinaryLength)
344        );
345    }
346
347    #[test]
348    fn test_encode_and_decode() {
349        let bytes = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
350        let instructions = decode_from_bytecode_v1(&bytes).unwrap();
351        let new_bytes = instructions
352            .into_iter()
353            .flat_map(|RawInstruction([a, b, c])| {
354                [a.to_le_bytes(), b.to_le_bytes(), c.to_le_bytes()].concat()
355            })
356            .collect::<Vec<_>>();
357        assert_eq!(bytes, new_bytes);
358    }
359}