Skip to main content

walrus/
const_expr.rs

1//! Handling wasm constant values
2
3use crate::emit::EmitContext;
4use crate::ir::Value;
5use crate::parse::IndicesToIds;
6use crate::{FunctionId, GlobalId, Result};
7use crate::{HeapType, RefType};
8use anyhow::bail;
9
10/// A constant which is produced in WebAssembly, typically used in global
11/// initializers or element/data offsets.
12#[derive(Debug, Clone)]
13pub enum ConstExpr {
14    /// An immediate constant value
15    Value(Value),
16    /// A constant value referenced by the global specified
17    Global(GlobalId),
18    /// A null reference
19    RefNull(RefType),
20    /// A function initializer
21    RefFunc(FunctionId),
22    /// Extended constant expression (sequence of instructions)
23    Extended(Vec<ConstOp>),
24}
25
26/// Operations allowed in extended constant expressions
27#[derive(Debug, Copy, Clone)]
28pub enum ConstOp {
29    /// An i32 constant value
30    I32Const(i32),
31    /// An i64 constant value
32    I64Const(i64),
33    /// An f32 constant value
34    F32Const(f32),
35    /// An f64 constant value
36    F64Const(f64),
37    /// A v128 constant value
38    V128Const(u128),
39    /// Get the value of a global
40    GlobalGet(GlobalId),
41    /// A null reference
42    RefNull(RefType),
43    /// A function reference
44    RefFunc(FunctionId),
45    /// i32 addition
46    I32Add,
47    /// i32 subtraction
48    I32Sub,
49    /// i32 multiplication
50    I32Mul,
51    /// i64 addition
52    I64Add,
53    /// i64 subtraction
54    I64Sub,
55    /// i64 multiplication
56    I64Mul,
57    /// Create i31ref from i32
58    RefI31,
59}
60
61impl ConstExpr {
62    pub(crate) fn eval(init: &wasmparser::ConstExpr, ids: &IndicesToIds) -> Result<ConstExpr> {
63        use wasmparser::Operator::*;
64        let mut reader = init.get_operators_reader();
65        let mut ops = Vec::new();
66
67        loop {
68            let op = reader.read()?;
69            match op {
70                End => break,
71                I32Const { value } => ops.push(ConstOp::I32Const(value)),
72                I64Const { value } => ops.push(ConstOp::I64Const(value)),
73                F32Const { value } => ops.push(ConstOp::F32Const(f32::from_bits(value.bits()))),
74                F64Const { value } => ops.push(ConstOp::F64Const(f64::from_bits(value.bits()))),
75                V128Const { value } => ops.push(ConstOp::V128Const(v128_to_u128(&value))),
76                GlobalGet { global_index } => {
77                    ops.push(ConstOp::GlobalGet(ids.get_global(global_index)?))
78                }
79                RefNull { hty } => {
80                    let heap_type = HeapType::try_from(hty)?;
81                    let ref_type = RefType {
82                        nullable: true,
83                        heap_type,
84                    };
85                    ops.push(ConstOp::RefNull(ref_type));
86                }
87                RefFunc { function_index } => {
88                    ops.push(ConstOp::RefFunc(ids.get_func(function_index)?))
89                }
90                I32Add => ops.push(ConstOp::I32Add),
91                I32Sub => ops.push(ConstOp::I32Sub),
92                I32Mul => ops.push(ConstOp::I32Mul),
93                I64Add => ops.push(ConstOp::I64Add),
94                I64Sub => ops.push(ConstOp::I64Sub),
95                I64Mul => ops.push(ConstOp::I64Mul),
96                RefI31 => ops.push(ConstOp::RefI31),
97                _ => bail!("unsupported operation in constant expression: {:?}", op),
98            }
99        }
100
101        reader.finish()?;
102
103        // Optimize: if there's only one simple operation, use the simple form
104        if ops.len() == 1 {
105            match &ops[0] {
106                ConstOp::I32Const(v) => return Ok(ConstExpr::Value(Value::I32(*v))),
107                ConstOp::I64Const(v) => return Ok(ConstExpr::Value(Value::I64(*v))),
108                ConstOp::F32Const(v) => return Ok(ConstExpr::Value(Value::F32(*v))),
109                ConstOp::F64Const(v) => return Ok(ConstExpr::Value(Value::F64(*v))),
110                ConstOp::V128Const(v) => return Ok(ConstExpr::Value(Value::V128(*v))),
111                ConstOp::GlobalGet(g) => return Ok(ConstExpr::Global(*g)),
112                ConstOp::RefNull(ty) => return Ok(ConstExpr::RefNull(*ty)),
113                ConstOp::RefFunc(f) => return Ok(ConstExpr::RefFunc(*f)),
114                _ => {}
115            }
116        }
117
118        Ok(ConstExpr::Extended(ops))
119    }
120
121    pub(crate) fn to_wasmencoder_type(&self, cx: &EmitContext) -> wasm_encoder::ConstExpr {
122        use wasm_encoder::{Encode, Instruction};
123        match self {
124            ConstExpr::Value(v) => match v {
125                Value::I32(v) => wasm_encoder::ConstExpr::i32_const(*v),
126                Value::I64(v) => wasm_encoder::ConstExpr::i64_const(*v),
127                Value::F32(v) => wasm_encoder::ConstExpr::f32_const((*v).into()),
128                Value::F64(v) => wasm_encoder::ConstExpr::f64_const((*v).into()),
129                Value::V128(v) => wasm_encoder::ConstExpr::v128_const(*v as i128),
130            },
131            ConstExpr::Global(g) => {
132                wasm_encoder::ConstExpr::global_get(cx.indices.get_global_index(*g))
133            }
134            ConstExpr::RefNull(ty) => {
135                wasm_encoder::ConstExpr::ref_null(ty.heap_type.to_wasmencoder_heap_type())
136            }
137            ConstExpr::RefFunc(f) => {
138                wasm_encoder::ConstExpr::ref_func(cx.indices.get_func_index(*f))
139            }
140            ConstExpr::Extended(ops) => {
141                let mut bytes = Vec::new();
142                for op in ops {
143                    match op {
144                        ConstOp::I32Const(v) => Instruction::I32Const(*v).encode(&mut bytes),
145                        ConstOp::I64Const(v) => Instruction::I64Const(*v).encode(&mut bytes),
146                        ConstOp::F32Const(v) => {
147                            Instruction::F32Const((*v).into()).encode(&mut bytes)
148                        }
149                        ConstOp::F64Const(v) => {
150                            Instruction::F64Const((*v).into()).encode(&mut bytes)
151                        }
152                        ConstOp::V128Const(v) => {
153                            Instruction::V128Const(*v as i128).encode(&mut bytes)
154                        }
155                        ConstOp::GlobalGet(g) => {
156                            Instruction::GlobalGet(cx.indices.get_global_index(*g))
157                                .encode(&mut bytes)
158                        }
159                        ConstOp::RefNull(ty) => {
160                            Instruction::RefNull(ty.heap_type.to_wasmencoder_heap_type())
161                                .encode(&mut bytes)
162                        }
163                        ConstOp::RefFunc(f) => {
164                            Instruction::RefFunc(cx.indices.get_func_index(*f)).encode(&mut bytes)
165                        }
166                        ConstOp::I32Add => Instruction::I32Add.encode(&mut bytes),
167                        ConstOp::I32Sub => Instruction::I32Sub.encode(&mut bytes),
168                        ConstOp::I32Mul => Instruction::I32Mul.encode(&mut bytes),
169                        ConstOp::I64Add => Instruction::I64Add.encode(&mut bytes),
170                        ConstOp::I64Sub => Instruction::I64Sub.encode(&mut bytes),
171                        ConstOp::I64Mul => Instruction::I64Mul.encode(&mut bytes),
172                        ConstOp::RefI31 => Instruction::RefI31.encode(&mut bytes),
173                    }
174                }
175                // Don't add End instruction - wasm_encoder::ConstExpr::raw adds it automatically
176                wasm_encoder::ConstExpr::raw(bytes)
177            }
178        }
179    }
180}
181
182pub(crate) fn v128_to_u128(value: &wasmparser::V128) -> u128 {
183    let n = value.bytes();
184    (n[0] as u128)
185        | ((n[1] as u128) << 8)
186        | ((n[2] as u128) << 16)
187        | ((n[3] as u128) << 24)
188        | ((n[4] as u128) << 32)
189        | ((n[5] as u128) << 40)
190        | ((n[6] as u128) << 48)
191        | ((n[7] as u128) << 56)
192        | ((n[8] as u128) << 64)
193        | ((n[9] as u128) << 72)
194        | ((n[10] as u128) << 80)
195        | ((n[11] as u128) << 88)
196        | ((n[12] as u128) << 96)
197        | ((n[13] as u128) << 104)
198        | ((n[14] as u128) << 112)
199        | ((n[15] as u128) << 120)
200}