Browse Source

Refactor scale decoder into codegen (#1211)

Signed-off-by: xermicus <cyrill@parity.io>
Co-authored-by: Lucas Steuernagel <38472950+LucasSte@users.noreply.github.com>
Cyrill Leutwiler 2 năm trước cách đây
mục cha
commit
25b4877293

+ 4 - 4
integration/substrate/builtins2.spec.ts

@@ -34,17 +34,17 @@ describe('Deploy builtins2 contract and test', () => {
 
         let { output: gas_left } = await query(conn, alice, contract, "burnGas", [0], undefined, convertWeight(gasLimit).v2Weight);
         let gas = BigInt(gas_left!.toString());
-        expect(gasLimit.refTime.toBigInt()).toBeGreaterThan(gas);
-        let previous_diff = gasLimit.refTime.toBigInt() - gas;
+        expect(gasLimit.toJSON().refTime).toBeGreaterThan(gas);
+        let previous_diff = BigInt(gasLimit.toJSON().refTime) - gas;
 
         // Gas metering is based on execution time:
         // Expect each call to burn between 10000..1000000 more gas than the previous iteration.
         for (let i = 1; i < 100; i++) {
             let { output: gas_left } = await query(conn, alice, contract, "burnGas", [i], undefined, convertWeight(gasLimit).v2Weight);
             let gas = BigInt(gas_left!.toString());
-            expect(gasLimit.refTime.toBigInt()).toBeGreaterThan(gas);
+            expect(gasLimit.toJSON().refTime).toBeGreaterThan(gas);
 
-            let diff = gasLimit.refTime.toBigInt() - gas;
+            let diff = BigInt(gasLimit.toJSON().refTime) - gas;
             expect(diff).toBeGreaterThan(previous_diff);
             expect(diff - previous_diff).toBeLessThan(1e6);
             expect(diff - previous_diff).toBeGreaterThan(1e4);

+ 1 - 1
integration/substrate/ink/caller/Cargo.toml

@@ -5,7 +5,7 @@ authors = ["Cyrill Leutwiler <cyrill@parity.io>"]
 edition = "2021"
 
 [dependencies]
-ink = { version = "4.0.0-beta.1", default-features = false }
+ink = { version = "4.0.0-rc", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
 scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true }

+ 1 - 1
integration/substrate/package.json

@@ -36,4 +36,4 @@
     "@polkadot/types": "^9.9",
     "@polkadot/util-crypto": "^10.1"
   }
-}
+}

+ 3 - 6
src/codegen/dispatch.rs

@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::codegen::encoding::create_encoder;
 use crate::codegen::{
     cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy, ReturnCode},
     solana_deploy::solana_deploy,
@@ -15,7 +14,7 @@ use num_bigint::{BigInt, Sign};
 use num_traits::Zero;
 use solang_parser::{pt, pt::Loc};
 
-use super::encoding::abi_encode;
+use super::encoding::{abi_decode, abi_encode};
 
 /// Create the dispatch for the Solana target
 pub(super) fn function_dispatch(
@@ -316,8 +315,7 @@ fn add_function_dispatch_case(
         .iter()
         .map(|e| e.ty.clone())
         .collect::<Vec<Type>>();
-    let encoder = create_encoder(ns, false);
-    let decoded = encoder.abi_decode(
+    let decoded = abi_decode(
         &Loc::Codegen,
         argsdata,
         &tys,
@@ -405,9 +403,8 @@ fn add_constructor_dispatch_case(
             .iter()
             .map(|e| e.ty.clone())
             .collect::<Vec<Type>>();
-        let encoder = create_encoder(ns, false);
         let truncated_len = Expression::Trunc(Loc::Codegen, Type::Uint(32), Box::new(argslen));
-        returns = encoder.abi_decode(
+        returns = abi_decode(
             &Loc::Codegen,
             argsdata,
             &tys,

+ 89 - 654
src/codegen/encoding/borsh_encoding.rs

@@ -1,22 +1,17 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
-use crate::codegen::encoding::buffer_validator::BufferValidator;
-use crate::codegen::encoding::{
-    allocate_array, allow_direct_copy, calculate_array_bytes_size,
-    calculate_direct_copy_bytes_size, finish_array_loop, increment_four, retrieve_array_length,
-    set_array_loop, AbiEncoding,
-};
+use crate::codegen::encoding::AbiEncoding;
 use crate::codegen::vartable::Vartable;
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{ArrayLength, Namespace, RetrieveType, StructType, Type, Type::Uint};
+use crate::sema::ast::StructType;
+use crate::sema::ast::{Namespace, Type, Type::Uint};
 use num_bigint::BigInt;
-use num_traits::{One, Zero};
-use solang_parser::pt::{Loc, Loc::Codegen};
+use solang_parser::pt::Loc::Codegen;
 use std::collections::HashMap;
-use std::ops::{Add, AddAssign, MulAssign};
+use std::ops::AddAssign;
 
-use super::index_array;
+use super::buffer_validator::BufferValidator;
 
 /// This struct implements the trait AbiEncoding for Borsh encoding
 pub(super) struct BorshEncoding {
@@ -83,683 +78,123 @@ impl AbiEncoding for BorshEncoding {
         Expression::NumberLiteral(Codegen, Uint(32), size)
     }
 
-    fn abi_decode(
-        &self,
-        loc: &Loc,
-        buffer: &Expression,
-        types: &[Type],
-        ns: &Namespace,
-        vartab: &mut Vartable,
-        cfg: &mut ControlFlowGraph,
-        buffer_size_expr: Option<Expression>,
-    ) -> Vec<Expression> {
-        assert!(!self.packed_encoder);
-        let buffer_size = vartab.temp_anonymous(&Uint(32));
-        if let Some(length_expression) = buffer_size_expr {
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: buffer_size,
-                    expr: length_expression,
-                },
-            );
-        } else {
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: buffer_size,
-                    expr: Expression::Builtin(
-                        Codegen,
-                        vec![Uint(32)],
-                        Builtin::ArrayLength,
-                        vec![buffer.clone()],
-                    ),
-                },
-            );
-        }
-
-        let mut validator = BufferValidator::new(buffer_size, types);
-
-        let mut read_items: Vec<Expression> = vec![Expression::Poison; types.len()];
-        let mut offset = Expression::NumberLiteral(*loc, Uint(32), BigInt::zero());
-
-        validator.initialize_validation(&offset, ns, vartab, cfg);
-
-        for (item_no, item) in types.iter().enumerate() {
-            validator.set_argument_number(item_no);
-            validator.validate_buffer(&offset, ns, vartab, cfg);
-            let (read_item, advance) =
-                self.read_from_buffer(buffer, &offset, item, &mut validator, ns, vartab, cfg);
-            read_items[item_no] = read_item;
-            offset = Expression::Add(*loc, Uint(32), false, Box::new(offset), Box::new(advance));
-        }
-
-        validator.validate_all_bytes_read(offset, ns, vartab, cfg);
-
-        read_items
-    }
-
-    fn storage_cache_insert(&mut self, arg_no: usize, expr: Expression) {
-        self.storage_cache.insert(arg_no, expr);
-    }
-
-    fn storage_cache_remove(&mut self, arg_no: usize) -> Option<Expression> {
-        self.storage_cache.remove(&arg_no)
-    }
-
-    fn is_packed(&self) -> bool {
-        self.packed_encoder
-    }
-}
-
-impl BorshEncoding {
-    pub fn new(packed: bool) -> BorshEncoding {
-        BorshEncoding {
-            storage_cache: HashMap::new(),
-            packed_encoder: packed,
-        }
-    }
-
-    /// Read a value of type 'ty' from the buffer at a given offset. Returns an expression
-    /// containing the read value and the number of bytes read.
-    fn read_from_buffer(
+    fn retrieve_array_length(
         &self,
         buffer: &Expression,
         offset: &Expression,
-        ty: &Type,
-        validator: &mut BufferValidator,
-        ns: &Namespace,
         vartab: &mut Vartable,
         cfg: &mut ControlFlowGraph,
-    ) -> (Expression, Expression) {
-        match ty {
-            Type::Uint(width) | Type::Int(width) => {
-                let encoding_size = width.next_power_of_two();
-
-                let size = Expression::NumberLiteral(Codegen, Uint(32), (encoding_size / 8).into());
-                validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
-
-                let read_value = Expression::Builtin(
-                    Codegen,
-                    vec![ty.clone()],
-                    Builtin::ReadFromBuffer,
-                    vec![buffer.clone(), offset.clone()],
-                );
-                let read_var = vartab.temp_anonymous(ty);
-
-                cfg.add(
-                    vartab,
-                    Instr::Set {
-                        loc: Codegen,
-                        res: read_var,
-                        expr: if encoding_size == *width {
-                            read_value
-                        } else {
-                            Expression::Trunc(Codegen, ty.clone(), Box::new(read_value))
-                        },
-                    },
-                );
-
-                let read_expr = Expression::Variable(Codegen, ty.clone(), read_var);
-                (read_expr, size)
-            }
-
-            Type::Bool
-            | Type::Address(_)
-            | Type::Contract(_)
-            | Type::Enum(_)
-            | Type::Value
-            | Type::Bytes(_) => {
-                let read_bytes = ty.memory_size_of(ns);
-
-                let size = Expression::NumberLiteral(Codegen, Uint(32), read_bytes);
-                validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
-
-                let read_value = Expression::Builtin(
-                    Codegen,
-                    vec![ty.clone()],
-                    Builtin::ReadFromBuffer,
-                    vec![buffer.clone(), offset.clone()],
-                );
-
-                let read_var = vartab.temp_anonymous(ty);
-                cfg.add(
-                    vartab,
-                    Instr::Set {
-                        loc: Codegen,
-                        res: read_var,
-                        expr: read_value,
-                    },
-                );
-
-                let read_expr = Expression::Variable(Codegen, ty.clone(), read_var);
-
-                (read_expr, size)
-            }
-
-            Type::DynamicBytes | Type::String => {
-                // String and Dynamic bytes are encoded as size (uint32) + elements
-                validator.validate_offset(increment_four(offset.clone()), ns, vartab, cfg);
-
-                let array_length = retrieve_array_length(buffer, offset, vartab, cfg);
-
-                let size = increment_four(Expression::Variable(Codegen, Uint(32), array_length));
-                let offset_to_validate = Expression::Add(
-                    Codegen,
-                    Uint(32),
-                    false,
-                    Box::new(size.clone()),
-                    Box::new(offset.clone()),
-                );
-
-                validator.validate_offset(offset_to_validate, ns, vartab, cfg);
-                let allocated_array = allocate_array(ty, array_length, vartab, cfg);
-
-                let advanced_pointer = Expression::AdvancePointer {
-                    pointer: Box::new(buffer.clone()),
-                    bytes_offset: Box::new(increment_four(offset.clone())),
-                };
-
-                cfg.add(
-                    vartab,
-                    Instr::MemCopy {
-                        source: advanced_pointer,
-                        destination: Expression::Variable(Codegen, ty.clone(), allocated_array),
-                        bytes: Expression::Variable(Codegen, Uint(32), array_length),
-                    },
-                );
-
-                (
-                    Expression::Variable(Codegen, ty.clone(), allocated_array),
-                    size,
-                )
-            }
-
-            Type::UserType(type_no) => {
-                let usr_type = ns.user_types[*type_no].ty.clone();
-                self.read_from_buffer(buffer, offset, &usr_type, validator, ns, vartab, cfg)
-            }
-
-            Type::ExternalFunction { .. } => {
-                let selector_size = Type::FunctionSelector.memory_size_of(ns);
-                // Extneral function has selector + address
-                let size = Expression::NumberLiteral(
-                    Codegen,
-                    Uint(32),
-                    BigInt::from(ns.address_length).add(&selector_size),
-                );
-
-                validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
-
-                let selector = Expression::Builtin(
+    ) -> (usize, Expression) {
+        let array_length = vartab.temp_anonymous(&Uint(32));
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: array_length,
+                expr: Expression::Builtin(
                     Codegen,
-                    vec![Type::FunctionSelector],
+                    vec![Uint(32)],
                     Builtin::ReadFromBuffer,
                     vec![buffer.clone(), offset.clone()],
-                );
-
-                let new_offset = Expression::Add(
-                    Codegen,
-                    Uint(32),
-                    false,
-                    offset.clone().into(),
-                    Expression::NumberLiteral(Codegen, Uint(32), selector_size).into(),
-                );
-
-                let address = Expression::Builtin(
-                    Codegen,
-                    vec![Type::Address(false)],
-                    Builtin::ReadFromBuffer,
-                    vec![buffer.clone(), new_offset],
-                );
-
-                let external_func = Expression::Cast(
-                    Codegen,
-                    ty.clone(),
-                    Box::new(Expression::StructLiteral(
-                        Codegen,
-                        Type::Struct(StructType::ExternalFunction),
-                        vec![selector, address],
-                    )),
-                );
-
-                (external_func, size)
-            }
-
-            Type::Array(elem_ty, dims) => self.decode_array(
-                buffer, offset, ty, elem_ty, dims, validator, ns, vartab, cfg,
-            ),
-
-            Type::Slice(elem_ty) => {
-                let dims = vec![ArrayLength::Dynamic];
-                self.decode_array(
-                    buffer, offset, ty, elem_ty, &dims, validator, ns, vartab, cfg,
-                )
-            }
-
-            Type::Struct(struct_ty) => self.decode_struct(
-                buffer,
-                offset.clone(),
-                ty,
-                struct_ty,
-                validator,
-                ns,
-                vartab,
-                cfg,
-            ),
-
-            Type::Rational
-            | Type::Ref(_)
-            | Type::StorageRef(..)
-            | Type::BufferPointer
-            | Type::Unresolved
-            | Type::InternalFunction { .. }
-            | Type::Unreachable
-            | Type::Void
-            | Type::FunctionSelector
-            | Type::Mapping(..) => unreachable!("Type should not appear on an encoded buffer"),
-        }
+                ),
+            },
+        );
+        (
+            array_length,
+            Expression::NumberLiteral(Codegen, Uint(32), 4.into()),
+        )
     }
 
-    /// Given the buffer and the offers, decode an array.
-    /// The function returns an expression containing the array and the number of bytes read.
-    fn decode_array(
+    fn decode_external_function(
         &self,
         buffer: &Expression,
         offset: &Expression,
-        array_ty: &Type,
-        elem_ty: &Type,
-        dims: &[ArrayLength],
+        ty: &Type,
         validator: &mut BufferValidator,
         ns: &Namespace,
         vartab: &mut Vartable,
         cfg: &mut ControlFlowGraph,
     ) -> (Expression, Expression) {
-        // Checks if we can memcpy the elements from the buffer directly to the allocated array
-        if allow_direct_copy(array_ty, elem_ty, dims, ns) {
-            // Calculate number of elements
-            let (bytes_size, offset, var_no) =
-                if matches!(dims.last(), Some(&ArrayLength::Fixed(_))) {
-                    let elem_no = calculate_direct_copy_bytes_size(dims, elem_ty, ns);
-                    let allocated_vector = vartab.temp_anonymous(array_ty);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            loc: Codegen,
-                            res: allocated_vector,
-                            expr: Expression::ArrayLiteral(
-                                Codegen,
-                                array_ty.clone(),
-                                vec![],
-                                vec![],
-                            ),
-                        },
-                    );
-
-                    (
-                        Expression::NumberLiteral(Codegen, Uint(32), elem_no),
-                        offset.clone(),
-                        allocated_vector,
-                    )
-                } else {
-                    validator.validate_offset(increment_four(offset.clone()), ns, vartab, cfg);
-                    let array_length = retrieve_array_length(buffer, offset, vartab, cfg);
-
-                    let allocated_array = allocate_array(array_ty, array_length, vartab, cfg);
-
-                    let size = calculate_array_bytes_size(array_length, elem_ty, ns);
-                    (size, increment_four(offset.clone()), allocated_array)
-                };
-
-            validator.validate_offset_plus_size(&offset, &bytes_size, ns, vartab, cfg);
-
-            let source_address = Expression::AdvancePointer {
-                pointer: Box::new(buffer.clone()),
-                bytes_offset: Box::new(offset),
-            };
+        let selector_size = Type::FunctionSelector.memory_size_of(ns);
+        // External function has selector + address
+        let size = Expression::NumberLiteral(
+            Codegen,
+            Uint(32),
+            BigInt::from(ns.address_length) + &selector_size,
+        );
+        validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
 
-            let array_expr = Expression::Variable(Codegen, array_ty.clone(), var_no);
-            cfg.add(
-                vartab,
-                Instr::MemCopy {
-                    source: source_address,
-                    destination: array_expr.clone(),
-                    bytes: bytes_size.clone(),
-                },
-            );
+        let selector = Expression::Builtin(
+            Codegen,
+            vec![Type::FunctionSelector],
+            Builtin::ReadFromBuffer,
+            vec![buffer.clone(), offset.clone()],
+        );
 
-            let bytes_size = if matches!(dims.last(), Some(ArrayLength::Dynamic)) {
-                increment_four(bytes_size)
-            } else {
-                bytes_size
-            };
+        let new_offset =
+            offset
+                .clone()
+                .add_u32(Expression::NumberLiteral(Codegen, Uint(32), selector_size));
 
-            (array_expr, bytes_size)
-        } else {
-            let mut indexes: Vec<usize> = Vec::new();
-            let array_var = vartab.temp_anonymous(array_ty);
+        let address = Expression::Builtin(
+            Codegen,
+            vec![Type::Address(false)],
+            Builtin::ReadFromBuffer,
+            vec![buffer.clone(), new_offset],
+        );
 
-            // The function decode_complex_array assumes that, if the dimension is fixed,
-            // there is no need to allocate an array
-            if matches!(dims.last(), Some(ArrayLength::Fixed(_))) {
-                cfg.add(
-                    vartab,
-                    Instr::Set {
-                        loc: Codegen,
-                        res: array_var,
-                        expr: Expression::ArrayLiteral(Codegen, array_ty.clone(), vec![], vec![]),
-                    },
-                );
-            }
+        let external_func = Expression::Cast(
+            Codegen,
+            ty.clone(),
+            Box::new(Expression::StructLiteral(
+                Codegen,
+                Type::Struct(StructType::ExternalFunction),
+                vec![selector, address],
+            )),
+        );
 
-            let offset_var = vartab.temp_anonymous(&Uint(32));
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: offset_var,
-                    expr: offset.clone(),
-                },
-            );
-            let array_var_expr = Expression::Variable(Codegen, array_ty.clone(), array_var);
-            let offset_expr = Expression::Variable(Codegen, Uint(32), offset_var);
-            self.decode_complex_array(
-                &array_var_expr,
-                buffer,
-                offset_var,
-                &offset_expr,
-                dims.len() - 1,
-                elem_ty,
-                dims,
-                validator,
-                ns,
-                vartab,
-                cfg,
-                &mut indexes,
-            );
-            // Subtract the original offset from
-            // the offset variable to obtain the vector size in bytes
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: offset_var,
-                    expr: Expression::Subtract(
-                        Codegen,
-                        Uint(32),
-                        false,
-                        Box::new(offset_expr.clone()),
-                        Box::new(offset.clone()),
-                    ),
-                },
-            );
-            (array_var_expr, offset_expr)
-        }
+        (external_func, size)
     }
 
-    /// Decodes a complex array from a borsh encoded buffer
-    /// Complex arrays are either dynamic arrays or arrays of dynamic types, like structs.
-    /// If this is an array of structs, whose representation in memory is padded, the array is
-    /// also complex, because it cannot be memcpy'ed
-    fn decode_complex_array(
+    fn calculate_string_size(
         &self,
-        array_var: &Expression,
-        buffer: &Expression,
-        offset_var: usize,
-        offset_expr: &Expression,
-        dimension: usize,
-        elem_ty: &Type,
-        dims: &[ArrayLength],
-        validator: &mut BufferValidator,
-        ns: &Namespace,
-        vartab: &mut Vartable,
-        cfg: &mut ControlFlowGraph,
-        indexes: &mut Vec<usize>,
-    ) {
-        // If we have a 'int[3][4][] vec', we can only validate the buffer after we have
-        // allocated the outer dimension, i.e., we are about to read a 'int[3][4]' item.
-        // Arrays whose elements are dynamic cannot be verified.
-        if validator.validation_necessary()
-            && !dims[0..(dimension + 1)]
-                .iter()
-                .any(|d| *d == ArrayLength::Dynamic)
-            && !elem_ty.is_dynamic(ns)
-        {
-            let mut elems = BigInt::one();
-            for item in &dims[0..(dimension + 1)] {
-                elems.mul_assign(item.array_length().unwrap());
-            }
-            elems.mul_assign(elem_ty.memory_size_of(ns));
-            let elems_size = Expression::NumberLiteral(Codegen, Uint(32), elems);
-            validator.validate_offset_plus_size(offset_expr, &elems_size, ns, vartab, cfg);
-            validator.validate_array();
-        }
-
-        // Dynamic dimensions mean that the subarray we are processing must be allocated in memory.
-        if dims[dimension] == ArrayLength::Dynamic {
-            let offset_to_validate = increment_four(offset_expr.clone());
-            validator.validate_offset(offset_to_validate, ns, vartab, cfg);
-            let array_length = retrieve_array_length(buffer, offset_expr, vartab, cfg);
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: offset_var,
-                    expr: increment_four(offset_expr.clone()),
-                },
-            );
-            let new_ty = Type::Array(Box::new(elem_ty.clone()), dims[0..(dimension + 1)].to_vec());
-            let allocated_array = allocate_array(&new_ty, array_length, vartab, cfg);
-
-            if indexes.is_empty() {
-                if let Expression::Variable(_, _, var_no) = array_var {
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            loc: Codegen,
-                            res: *var_no,
-                            expr: Expression::Variable(Codegen, new_ty.clone(), allocated_array),
-                        },
-                    );
-                } else {
-                    unreachable!("array_var must be a variable");
-                }
-            } else {
-                // TODO: This is wired up for multidimensional dynamic arrays, but they do no work yet
-                // Check https://github.com/hyperledger/solang/issues/932 for more information
-                let sub_arr = index_array(array_var.clone(), dims, indexes, true);
-                cfg.add(
-                    vartab,
-                    Instr::Store {
-                        dest: sub_arr,
-                        data: Expression::Variable(Codegen, new_ty.clone(), allocated_array),
-                    },
-                );
-            }
-        }
-
-        let for_loop = set_array_loop(array_var, dims, dimension, indexes, vartab, cfg);
-        cfg.set_basic_block(for_loop.body_block);
-        if 0 == dimension {
-            let (read_expr, advance) =
-                self.read_from_buffer(buffer, offset_expr, elem_ty, validator, ns, vartab, cfg);
-            let ptr = index_array(array_var.clone(), dims, indexes, true);
+        expr: &Expression,
+        _vartab: &mut Vartable,
+        _cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        // When encoding a variable length array, the total size is "length (u32)" + elements
+        let length = Expression::Builtin(
+            Codegen,
+            vec![Uint(32)],
+            Builtin::ArrayLength,
+            vec![expr.clone()],
+        );
 
-            cfg.add(
-                vartab,
-                Instr::Store {
-                    dest: ptr,
-                    data: if matches!(read_expr.ty(), Type::Struct(_)) {
-                        // Type::Struct is a pointer to a struct. If we are dealing with a vector
-                        // of structs, we need to dereference the pointer before storing it at a
-                        // given vector index.
-                        Expression::Load(Codegen, read_expr.ty(), Box::new(read_expr))
-                    } else {
-                        read_expr
-                    },
-                },
-            );
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    loc: Codegen,
-                    res: offset_var,
-                    expr: Expression::Add(
-                        Codegen,
-                        Uint(32),
-                        false,
-                        Box::new(advance),
-                        Box::new(offset_expr.clone()),
-                    ),
-                },
-            );
+        if self.is_packed() {
+            length
         } else {
-            self.decode_complex_array(
-                array_var,
-                buffer,
-                offset_var,
-                offset_expr,
-                dimension - 1,
-                elem_ty,
-                dims,
-                validator,
-                ns,
-                vartab,
-                cfg,
-                indexes,
-            );
+            length.add_u32(Expression::NumberLiteral(Codegen, Uint(32), 4.into()))
         }
-
-        finish_array_loop(&for_loop, vartab, cfg);
     }
 
-    /// Read a struct from the buffer
-    fn decode_struct(
-        &self,
-        buffer: &Expression,
-        mut offset: Expression,
-        expr_ty: &Type,
-        struct_ty: &StructType,
-        validator: &mut BufferValidator,
-        ns: &Namespace,
-        vartab: &mut Vartable,
-        cfg: &mut ControlFlowGraph,
-    ) -> (Expression, Expression) {
-        let size = if let Some(no_padding_size) = ns.calculate_struct_non_padded_size(struct_ty) {
-            let padded_size = struct_ty.struct_padded_size(ns);
-            // If the size without padding equals the size with padding,
-            // we can memcpy this struct directly.
-            if padded_size.eq(&no_padding_size) {
-                let size = Expression::NumberLiteral(Codegen, Uint(32), no_padding_size);
-                validator.validate_offset_plus_size(&offset, &size, ns, vartab, cfg);
-                let source_address = Expression::AdvancePointer {
-                    pointer: Box::new(buffer.clone()),
-                    bytes_offset: Box::new(offset),
-                };
-                let allocated_struct = vartab.temp_anonymous(expr_ty);
-                cfg.add(
-                    vartab,
-                    Instr::Set {
-                        loc: Codegen,
-                        res: allocated_struct,
-                        expr: Expression::StructLiteral(Codegen, expr_ty.clone(), vec![]),
-                    },
-                );
-                let struct_var = Expression::Variable(Codegen, expr_ty.clone(), allocated_struct);
-                cfg.add(
-                    vartab,
-                    Instr::MemCopy {
-                        source: source_address,
-                        destination: struct_var.clone(),
-                        bytes: size.clone(),
-                    },
-                );
-                return (struct_var, size);
-            } else {
-                // This struct has a fixed size, but we cannot memcpy it due to
-                // its padding in memory
-                Some(Expression::NumberLiteral(
-                    Codegen,
-                    Uint(32),
-                    no_padding_size,
-                ))
-            }
-        } else {
-            None
-        };
-
-        let struct_tys = struct_ty
-            .definition(ns)
-            .fields
-            .iter()
-            .map(|item| item.ty.clone())
-            .collect::<Vec<Type>>();
-
-        // If it was not possible to validate the struct beforehand, we validate each field
-        // during recursive calls to 'read_from_buffer'
-        let mut struct_validator = validator.create_sub_validator(&struct_tys);
-
-        let qty = struct_ty.definition(ns).fields.len();
+    fn storage_cache_insert(&mut self, arg_no: usize, expr: Expression) {
+        self.storage_cache.insert(arg_no, expr);
+    }
 
-        if validator.validation_necessary() {
-            struct_validator.initialize_validation(&offset, ns, vartab, cfg);
-        }
+    fn storage_cache_remove(&mut self, arg_no: usize) -> Option<Expression> {
+        self.storage_cache.remove(&arg_no)
+    }
 
-        let (mut read_expr, mut advance) = self.read_from_buffer(
-            buffer,
-            &offset,
-            &struct_tys[0],
-            &mut struct_validator,
-            ns,
-            vartab,
-            cfg,
-        );
-        let mut runtime_size = advance.clone();
+    fn is_packed(&self) -> bool {
+        self.packed_encoder
+    }
+}
 
-        let mut read_items = vec![Expression::Poison; qty];
-        read_items[0] = read_expr;
-        for i in 1..qty {
-            struct_validator.set_argument_number(i);
-            struct_validator.validate_buffer(&offset, ns, vartab, cfg);
-            offset = Expression::Add(
-                Codegen,
-                Uint(32),
-                false,
-                Box::new(offset.clone()),
-                Box::new(advance),
-            );
-            (read_expr, advance) = self.read_from_buffer(
-                buffer,
-                &offset,
-                &struct_tys[i],
-                &mut struct_validator,
-                ns,
-                vartab,
-                cfg,
-            );
-            read_items[i] = read_expr;
-            runtime_size = Expression::Add(
-                Codegen,
-                Uint(32),
-                false,
-                Box::new(runtime_size),
-                Box::new(advance.clone()),
-            );
+impl BorshEncoding {
+    pub fn new(packed: bool) -> BorshEncoding {
+        BorshEncoding {
+            storage_cache: HashMap::new(),
+            packed_encoder: packed,
         }
-
-        let allocated_struct = vartab.temp_anonymous(expr_ty);
-        cfg.add(
-            vartab,
-            Instr::Set {
-                loc: Codegen,
-                res: allocated_struct,
-                expr: Expression::StructLiteral(Codegen, expr_ty.clone(), read_items),
-            },
-        );
-
-        let struct_var = Expression::Variable(Codegen, expr_ty.clone(), allocated_struct);
-        (struct_var, size.unwrap_or(runtime_size))
     }
 }

+ 1 - 1
src/codegen/encoding/buffer_validator.rs

@@ -13,7 +13,7 @@ use std::ops::AddAssign;
 /// When we are decoding serialized data from a bytes array, we must constantly verify if
 /// we are not reading past its ending. This struct helps us decrease the number of checks we do,
 /// by merging checks when we can determine the size of what to read beforehand.
-pub(super) struct BufferValidator<'a> {
+pub(crate) struct BufferValidator<'a> {
     /// Saves the codegen::Expression that contains the buffer length.
     buffer_length: Expression,
     /// The types we are supposed to decode

+ 650 - 76
src/codegen/encoding/mod.rs

@@ -3,9 +3,9 @@
 /// Any supported encoding scheme should be implemented here.
 /// The module is organized as follows:
 ///
-/// - `fn abi_encode()` and `fn abi_decode()` are entry point for whereever there is
-/// something to be encoded or decoded.
-/// - The `AbiEncoding` defines the encoding and decoding API and must be implemented by all schemes.
+/// - `fn abi_encode()` and `fn abi_decode()` are entry points for wherever there is
+///   something to be encoded or decoded.
+/// - `AbiEncoding` defines the encoding and decoding API and must be implemented by all schemes.
 /// - There are some helper functions to work with more complex types.
 ///   Any such helper function should work fine regardless of the encoding scheme being used.
 mod borsh_encoding;
@@ -26,6 +26,8 @@ use num_traits::{One, Zero};
 use solang_parser::pt::{Loc, Loc::Codegen};
 use std::ops::{AddAssign, MulAssign, Sub};
 
+use self::buffer_validator::BufferValidator;
+
 /// Insert encoding instructions into the `cfg` for any `Expression` in `args`.
 /// Returns a pointer to the encoded data and the size as a 32bit integer.
 pub(super) fn abi_encode(
@@ -58,6 +60,65 @@ pub(super) fn abi_encode(
     (buffer, size)
 }
 
+/// Insert decoding routines into the `cfg` for the `Expression`s in `args`.
+/// Returns a vector containing the encoded data.
+pub(super) fn abi_decode(
+    loc: &Loc,
+    buffer: &Expression,
+    types: &[Type],
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    buffer_size_expr: Option<Expression>,
+) -> Vec<Expression> {
+    let buffer_size = vartab.temp_anonymous(&Uint(32));
+    if let Some(length_expression) = buffer_size_expr {
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: buffer_size,
+                expr: length_expression,
+            },
+        );
+    } else {
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: buffer_size,
+                expr: Expression::Builtin(
+                    Codegen,
+                    vec![Uint(32)],
+                    Builtin::ArrayLength,
+                    vec![buffer.clone()],
+                ),
+            },
+        );
+    }
+
+    let mut validator = BufferValidator::new(buffer_size, types);
+
+    let mut read_items: Vec<Expression> = vec![Expression::Poison; types.len()];
+    let mut offset = Expression::NumberLiteral(*loc, Uint(32), BigInt::zero());
+
+    validator.initialize_validation(&offset, ns, vartab, cfg);
+    let encoder = create_encoder(ns, false);
+
+    for (item_no, item) in types.iter().enumerate() {
+        validator.set_argument_number(item_no);
+        validator.validate_buffer(&offset, ns, vartab, cfg);
+        let (read_item, advance) =
+            encoder.read_from_buffer(buffer, &offset, item, &mut validator, ns, vartab, cfg);
+        read_items[item_no] = read_item;
+        offset = Expression::Add(*loc, Uint(32), false, Box::new(offset), Box::new(advance));
+    }
+
+    validator.validate_all_bytes_read(offset, ns, vartab, cfg);
+
+    read_items
+}
+
 /// Calculate the size of a set of arguments to encoding functions
 fn calculate_size_args(
     encoder: &mut Box<dyn AbiEncoding>,
@@ -253,7 +314,7 @@ pub(super) trait AbiEncoding {
             (offset.clone(), None)
         } else {
             let size = self.encode_size(&len, buffer, offset, vartab, cfg);
-            (increment_by(offset.clone(), size.clone()), Some(size))
+            (offset.clone().add_u32(size.clone()), Some(size))
         };
         // ptr + offset + size_of_integer
         let dest_address = Expression::AdvancePointer {
@@ -269,7 +330,7 @@ pub(super) trait AbiEncoding {
             },
         );
         if let Some(size) = size {
-            increment_by(len, size)
+            len.add_u32(size)
         } else {
             len
         }
@@ -380,8 +441,10 @@ pub(super) trait AbiEncoding {
                         (offset.clone(), None)
                     } else {
                         let encoded_size = self.encode_size(&value, buffer, offset, vartab, cfg);
-                        let new_offset = increment_by(offset.clone(), encoded_size.clone());
-                        (new_offset, Some(encoded_size))
+                        (
+                            offset.clone().add_u32(encoded_size.clone()),
+                            Some(encoded_size),
+                        )
                     };
 
                     if let Expression::Variable(_, _, size_temp) = value {
@@ -492,7 +555,7 @@ pub(super) trait AbiEncoding {
                 Instr::Set {
                     loc: Codegen,
                     res: offset_var,
-                    expr: increment_by(offset_expr, encoded_size),
+                    expr: offset_expr.add_u32(encoded_size),
                 },
             );
         }
@@ -546,6 +609,571 @@ pub(super) trait AbiEncoding {
         cfg: &mut ControlFlowGraph,
     ) -> Expression;
 
+    /// Read a value of type 'ty' from the buffer at a given offset. Returns an expression
+    /// containing the read value and the number of bytes read.
+    fn read_from_buffer(
+        &self,
+        buffer: &Expression,
+        offset: &Expression,
+        ty: &Type,
+        validator: &mut BufferValidator,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (Expression, Expression) {
+        match ty {
+            Type::Uint(width) | Type::Int(width) => {
+                let encoding_size = width.next_power_of_two();
+
+                let size = Expression::NumberLiteral(Codegen, Uint(32), (encoding_size / 8).into());
+                validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
+
+                let read_value = Expression::Builtin(
+                    Codegen,
+                    vec![ty.clone()],
+                    Builtin::ReadFromBuffer,
+                    vec![buffer.clone(), offset.clone()],
+                );
+                let read_var = vartab.temp_anonymous(ty);
+
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Codegen,
+                        res: read_var,
+                        expr: if encoding_size == *width {
+                            read_value
+                        } else {
+                            Expression::Trunc(Codegen, ty.clone(), Box::new(read_value))
+                        },
+                    },
+                );
+
+                let read_expr = Expression::Variable(Codegen, ty.clone(), read_var);
+                (read_expr, size)
+            }
+
+            Type::Bool
+            | Type::Address(_)
+            | Type::Contract(_)
+            | Type::Enum(_)
+            | Type::Value
+            | Type::Bytes(_) => {
+                let read_bytes = ty.memory_size_of(ns);
+
+                let size = Expression::NumberLiteral(Codegen, Uint(32), read_bytes);
+                validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
+
+                let read_value = Expression::Builtin(
+                    Codegen,
+                    vec![ty.clone()],
+                    Builtin::ReadFromBuffer,
+                    vec![buffer.clone(), offset.clone()],
+                );
+
+                let read_var = vartab.temp_anonymous(ty);
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Codegen,
+                        res: read_var,
+                        expr: read_value,
+                    },
+                );
+
+                let read_expr = Expression::Variable(Codegen, ty.clone(), read_var);
+
+                (read_expr, size)
+            }
+
+            Type::DynamicBytes | Type::String => {
+                // String and Dynamic bytes are encoded as size + elements
+                let (array_length_var, size_length) =
+                    self.retrieve_array_length(buffer, offset, vartab, cfg);
+                let array_start = offset.clone().add_u32(size_length.clone());
+                validator.validate_offset(array_start.clone(), ns, vartab, cfg);
+                let array_length = Expression::Variable(Codegen, Uint(32), array_length_var);
+                let total_size = array_length.clone().add_u32(size_length);
+                validator.validate_offset(
+                    offset.clone().add_u32(total_size.clone()),
+                    ns,
+                    vartab,
+                    cfg,
+                );
+
+                let allocated_array = allocate_array(ty, array_length_var, vartab, cfg);
+                let advanced_pointer = Expression::AdvancePointer {
+                    pointer: buffer.clone().into(),
+                    bytes_offset: array_start.into(),
+                };
+                cfg.add(
+                    vartab,
+                    Instr::MemCopy {
+                        source: advanced_pointer,
+                        destination: Expression::Variable(Codegen, ty.clone(), allocated_array),
+                        bytes: array_length,
+                    },
+                );
+                (
+                    Expression::Variable(Codegen, ty.clone(), allocated_array),
+                    total_size,
+                )
+            }
+
+            Type::UserType(type_no) => {
+                let usr_type = ns.user_types[*type_no].ty.clone();
+                self.read_from_buffer(buffer, offset, &usr_type, validator, ns, vartab, cfg)
+            }
+
+            Type::ExternalFunction { .. } => {
+                self.decode_external_function(buffer, offset, ty, validator, ns, vartab, cfg)
+            }
+
+            Type::Array(elem_ty, dims) => self.decode_array(
+                buffer, offset, ty, elem_ty, dims, validator, ns, vartab, cfg,
+            ),
+
+            Type::Slice(elem_ty) => {
+                let dims = vec![ArrayLength::Dynamic];
+                self.decode_array(
+                    buffer, offset, ty, elem_ty, &dims, validator, ns, vartab, cfg,
+                )
+            }
+
+            Type::Struct(struct_ty) => self.decode_struct(
+                buffer,
+                offset.clone(),
+                ty,
+                struct_ty,
+                validator,
+                ns,
+                vartab,
+                cfg,
+            ),
+
+            Type::Rational
+            | Type::Ref(_)
+            | Type::StorageRef(..)
+            | Type::BufferPointer
+            | Type::Unresolved
+            | Type::InternalFunction { .. }
+            | Type::Unreachable
+            | Type::Void
+            | Type::FunctionSelector
+            | Type::Mapping(..) => unreachable!("Type should not appear on an encoded buffer"),
+        }
+    }
+
+    /// Retrieve a dynamic array length from the encoded buffer. It returns the variable number in which
+    /// the length has been stored and the size width of the vector length.
+    fn retrieve_array_length(
+        &self,
+        buffer: &Expression,
+        offset: &Expression,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (usize, Expression);
+
+    /// Given the buffer and the offset, decode an array.
+    /// The function returns an expression containing the array and the number of bytes read.
+    fn decode_array(
+        &self,
+        buffer: &Expression,
+        offset: &Expression,
+        array_ty: &Type,
+        elem_ty: &Type,
+        dims: &[ArrayLength],
+        validator: &mut BufferValidator,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (Expression, Expression) {
+        // Checks if we can memcpy the elements from the buffer directly to the allocated array
+        if allow_direct_copy(array_ty, elem_ty, dims, ns) {
+            // Calculate number of elements
+            let (array_bytes_size, size_width, offset, var_no) =
+                if matches!(dims.last(), Some(&ArrayLength::Fixed(_))) {
+                    let elem_no = calculate_direct_copy_bytes_size(dims, elem_ty, ns);
+                    let allocated_vector = vartab.temp_anonymous(array_ty);
+                    let expr = Expression::ArrayLiteral(Codegen, array_ty.clone(), vec![], vec![]);
+                    cfg.add(
+                        vartab,
+                        Instr::Set {
+                            loc: Codegen,
+                            res: allocated_vector,
+                            expr,
+                        },
+                    );
+                    (
+                        Expression::NumberLiteral(Codegen, Uint(32), elem_no),
+                        Expression::NumberLiteral(Codegen, Uint(32), 0.into()),
+                        offset.clone(),
+                        allocated_vector,
+                    )
+                } else {
+                    let (array_length, size_width) =
+                        self.retrieve_array_length(buffer, offset, vartab, cfg);
+                    let array_start = offset.clone().add_u32(size_width.clone());
+                    validator.validate_offset(array_start.clone(), ns, vartab, cfg);
+                    (
+                        calculate_array_bytes_size(array_length, elem_ty, ns),
+                        size_width,
+                        array_start,
+                        allocate_array(array_ty, array_length, vartab, cfg),
+                    )
+                };
+
+            validator.validate_offset_plus_size(&offset, &array_bytes_size, ns, vartab, cfg);
+
+            let source_address = Expression::AdvancePointer {
+                pointer: Box::new(buffer.clone()),
+                bytes_offset: Box::new(offset),
+            };
+
+            let array_expr = Expression::Variable(Codegen, array_ty.clone(), var_no);
+            cfg.add(
+                vartab,
+                Instr::MemCopy {
+                    source: source_address,
+                    destination: array_expr.clone(),
+                    bytes: array_bytes_size.clone(),
+                },
+            );
+
+            let bytes_size = if matches!(dims.last(), Some(ArrayLength::Dynamic)) {
+                array_bytes_size.add_u32(size_width)
+            } else {
+                array_bytes_size
+            };
+
+            (array_expr, bytes_size)
+        } else {
+            let mut indexes: Vec<usize> = Vec::new();
+            let array_var = vartab.temp_anonymous(array_ty);
+
+            // The function decode_complex_array assumes that, if the dimension is fixed,
+            // there is no need to allocate an array
+            if matches!(dims.last(), Some(ArrayLength::Fixed(_))) {
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Codegen,
+                        res: array_var,
+                        expr: Expression::ArrayLiteral(Codegen, array_ty.clone(), vec![], vec![]),
+                    },
+                );
+            }
+
+            let offset_var = vartab.temp_anonymous(&Uint(32));
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Codegen,
+                    res: offset_var,
+                    expr: offset.clone(),
+                },
+            );
+            let array_var_expr = Expression::Variable(Codegen, array_ty.clone(), array_var);
+            let offset_expr = Expression::Variable(Codegen, Uint(32), offset_var);
+            self.decode_complex_array(
+                &array_var_expr,
+                buffer,
+                offset_var,
+                &offset_expr,
+                dims.len() - 1,
+                elem_ty,
+                dims,
+                validator,
+                ns,
+                vartab,
+                cfg,
+                &mut indexes,
+            );
+            // Subtract the original offset from
+            // the offset variable to obtain the vector size in bytes
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Codegen,
+                    res: offset_var,
+                    expr: Expression::Subtract(
+                        Codegen,
+                        Uint(32),
+                        false,
+                        Box::new(offset_expr.clone()),
+                        Box::new(offset.clone()),
+                    ),
+                },
+            );
+            (array_var_expr, offset_expr)
+        }
+    }
+
+    /// Decodes a complex array from a borsh encoded buffer
+    /// Complex arrays are either dynamic arrays or arrays of dynamic types, like structs.
+    /// If this is an array of structs, whose representation in memory is padded, the array is
+    /// also complex, because it cannot be memcpy'ed
+    fn decode_complex_array(
+        &self,
+        array_var: &Expression,
+        buffer: &Expression,
+        offset_var: usize,
+        offset_expr: &Expression,
+        dimension: usize,
+        elem_ty: &Type,
+        dims: &[ArrayLength],
+        validator: &mut BufferValidator,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+        indexes: &mut Vec<usize>,
+    ) {
+        // If we have a 'int[3][4][] vec', we can only validate the buffer after we have
+        // allocated the outer dimension, i.e., we are about to read a 'int[3][4]' item.
+        // Arrays whose elements are dynamic cannot be verified.
+        if validator.validation_necessary()
+            && !dims[0..(dimension + 1)]
+                .iter()
+                .any(|d| *d == ArrayLength::Dynamic)
+            && !elem_ty.is_dynamic(ns)
+        {
+            let mut elems = BigInt::one();
+            for item in &dims[0..(dimension + 1)] {
+                elems.mul_assign(item.array_length().unwrap());
+            }
+            elems.mul_assign(elem_ty.memory_size_of(ns));
+            let elems_size = Expression::NumberLiteral(Codegen, Uint(32), elems);
+            validator.validate_offset_plus_size(offset_expr, &elems_size, ns, vartab, cfg);
+            validator.validate_array();
+        }
+
+        // Dynamic dimensions mean that the subarray we are processing must be allocated in memory.
+        if dims[dimension] == ArrayLength::Dynamic {
+            let (array_length, size_length) =
+                self.retrieve_array_length(buffer, offset_expr, vartab, cfg);
+            let array_start = offset_expr.clone().add_u32(size_length);
+            validator.validate_offset(array_start.clone(), ns, vartab, cfg);
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Codegen,
+                    res: offset_var,
+                    expr: array_start,
+                },
+            );
+            let new_ty = Type::Array(Box::new(elem_ty.clone()), dims[0..(dimension + 1)].to_vec());
+            let allocated_array = allocate_array(&new_ty, array_length, vartab, cfg);
+
+            if indexes.is_empty() {
+                if let Expression::Variable(_, _, var_no) = array_var {
+                    cfg.add(
+                        vartab,
+                        Instr::Set {
+                            loc: Codegen,
+                            res: *var_no,
+                            expr: Expression::Variable(Codegen, new_ty.clone(), allocated_array),
+                        },
+                    );
+                } else {
+                    unreachable!("array_var must be a variable");
+                }
+            } else {
+                // TODO: This is wired up for multidimensional dynamic arrays, but they do no work yet
+                // Check https://github.com/hyperledger/solang/issues/932 for more information
+                let sub_arr = index_array(array_var.clone(), dims, indexes, true);
+                cfg.add(
+                    vartab,
+                    Instr::Store {
+                        dest: sub_arr,
+                        data: Expression::Variable(Codegen, new_ty.clone(), allocated_array),
+                    },
+                );
+            }
+        }
+
+        let for_loop = set_array_loop(array_var, dims, dimension, indexes, vartab, cfg);
+        cfg.set_basic_block(for_loop.body_block);
+        if 0 == dimension {
+            let (read_expr, advance) =
+                self.read_from_buffer(buffer, offset_expr, elem_ty, validator, ns, vartab, cfg);
+            let ptr = index_array(array_var.clone(), dims, indexes, true);
+
+            cfg.add(
+                vartab,
+                Instr::Store {
+                    dest: ptr,
+                    data: if matches!(read_expr.ty(), Type::Struct(_)) {
+                        // Type::Struct is a pointer to a struct. If we are dealing with a vector
+                        // of structs, we need to dereference the pointer before storing it at a
+                        // given vector index.
+                        Expression::Load(Codegen, read_expr.ty(), Box::new(read_expr))
+                    } else {
+                        read_expr
+                    },
+                },
+            );
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Codegen,
+                    res: offset_var,
+                    expr: Expression::Add(
+                        Codegen,
+                        Uint(32),
+                        false,
+                        Box::new(advance),
+                        Box::new(offset_expr.clone()),
+                    ),
+                },
+            );
+        } else {
+            self.decode_complex_array(
+                array_var,
+                buffer,
+                offset_var,
+                offset_expr,
+                dimension - 1,
+                elem_ty,
+                dims,
+                validator,
+                ns,
+                vartab,
+                cfg,
+                indexes,
+            );
+        }
+
+        finish_array_loop(&for_loop, vartab, cfg);
+    }
+
+    /// Read a struct from the buffer
+    fn decode_struct(
+        &self,
+        buffer: &Expression,
+        mut offset: Expression,
+        expr_ty: &Type,
+        struct_ty: &StructType,
+        validator: &mut BufferValidator,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (Expression, Expression) {
+        let size = if let Some(no_padding_size) = ns.calculate_struct_non_padded_size(struct_ty) {
+            let padded_size = struct_ty.struct_padded_size(ns);
+            // If the size without padding equals the size with padding,
+            // we can memcpy this struct directly.
+            if padded_size.eq(&no_padding_size) {
+                let size = Expression::NumberLiteral(Codegen, Uint(32), no_padding_size);
+                validator.validate_offset_plus_size(&offset, &size, ns, vartab, cfg);
+                let source_address = Expression::AdvancePointer {
+                    pointer: Box::new(buffer.clone()),
+                    bytes_offset: Box::new(offset),
+                };
+                let allocated_struct = vartab.temp_anonymous(expr_ty);
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Codegen,
+                        res: allocated_struct,
+                        expr: Expression::StructLiteral(Codegen, expr_ty.clone(), vec![]),
+                    },
+                );
+                let struct_var = Expression::Variable(Codegen, expr_ty.clone(), allocated_struct);
+                cfg.add(
+                    vartab,
+                    Instr::MemCopy {
+                        source: source_address,
+                        destination: struct_var.clone(),
+                        bytes: size.clone(),
+                    },
+                );
+                return (struct_var, size);
+            } else {
+                // This struct has a fixed size, but we cannot memcpy it due to
+                // its padding in memory
+                Some(Expression::NumberLiteral(
+                    Codegen,
+                    Uint(32),
+                    no_padding_size,
+                ))
+            }
+        } else {
+            None
+        };
+
+        let struct_tys = struct_ty
+            .definition(ns)
+            .fields
+            .iter()
+            .map(|item| item.ty.clone())
+            .collect::<Vec<Type>>();
+
+        // If it was not possible to validate the struct beforehand, we validate each field
+        // during recursive calls to 'read_from_buffer'
+        let mut struct_validator = validator.create_sub_validator(&struct_tys);
+
+        let qty = struct_ty.definition(ns).fields.len();
+
+        if validator.validation_necessary() {
+            struct_validator.initialize_validation(&offset, ns, vartab, cfg);
+        }
+
+        let (mut read_expr, mut advance) = self.read_from_buffer(
+            buffer,
+            &offset,
+            &struct_tys[0],
+            &mut struct_validator,
+            ns,
+            vartab,
+            cfg,
+        );
+        let mut runtime_size = advance.clone();
+
+        let mut read_items = vec![Expression::Poison; qty];
+        read_items[0] = read_expr;
+        for i in 1..qty {
+            struct_validator.set_argument_number(i);
+            struct_validator.validate_buffer(&offset, ns, vartab, cfg);
+            offset = Expression::Add(
+                Codegen,
+                Uint(32),
+                false,
+                Box::new(offset.clone()),
+                Box::new(advance),
+            );
+            (read_expr, advance) = self.read_from_buffer(
+                buffer,
+                &offset,
+                &struct_tys[i],
+                &mut struct_validator,
+                ns,
+                vartab,
+                cfg,
+            );
+            read_items[i] = read_expr;
+            runtime_size = Expression::Add(
+                Codegen,
+                Uint(32),
+                false,
+                Box::new(runtime_size),
+                Box::new(advance.clone()),
+            );
+        }
+
+        let allocated_struct = vartab.temp_anonymous(expr_ty);
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: allocated_struct,
+                expr: Expression::StructLiteral(Codegen, expr_ty.clone(), read_items),
+            },
+        );
+
+        let struct_var = Expression::Variable(Codegen, expr_ty.clone(), allocated_struct);
+        (struct_var, size.unwrap_or(runtime_size))
+    }
+
     /// Calculate the size of a single codegen::Expression
     fn get_expr_size(
         &mut self,
@@ -585,9 +1213,8 @@ pub(super) trait AbiEncoding {
             }
             Type::ExternalFunction { .. } => {
                 let selector_len: BigInt = ns.target.selector_length().into();
-                let mut address_size = Type::Address(false).memory_size_of(ns);
-                address_size.add_assign(selector_len);
-                Expression::NumberLiteral(Codegen, Uint(32), address_size)
+                let address_size = Type::Address(false).memory_size_of(ns);
+                Expression::NumberLiteral(Codegen, Uint(32), address_size + selector_len)
             }
             Type::Ref(r) => {
                 if let Type::Struct(struct_ty) = &**r {
@@ -614,6 +1241,17 @@ pub(super) trait AbiEncoding {
         }
     }
 
+    fn decode_external_function(
+        &self,
+        buffer: &Expression,
+        offset: &Expression,
+        ty: &Type,
+        validator: &mut BufferValidator,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (Expression, Expression);
+
     /// Calculate the size of an array
     fn calculate_array_size(
         &mut self,
@@ -824,34 +1462,7 @@ pub(super) trait AbiEncoding {
         expr: &Expression,
         _vartab: &mut Vartable,
         _cfg: &mut ControlFlowGraph,
-    ) -> Expression {
-        // When encoding a variable length array, the total size is "length (u32)" + elements
-        let length = Expression::Builtin(
-            Codegen,
-            vec![Uint(32)],
-            Builtin::ArrayLength,
-            vec![expr.clone()],
-        );
-
-        if self.is_packed() {
-            length
-        } else {
-            increment_four(length)
-        }
-    }
-
-    /// Insert decoding routines into the `cfg` for the `Expression`s in `args`.
-    /// Returns a list containing the encoded data.
-    fn abi_decode(
-        &self,
-        loc: &Loc,
-        buffer: &Expression,
-        types: &[Type],
-        ns: &Namespace,
-        vartab: &mut Vartable,
-        cfg: &mut ControlFlowGraph,
-        buffer_size: Option<Expression>,
-    ) -> Vec<Expression>;
+    ) -> Expression;
 
     /// Encoding happens in two steps. First, we look at each argument to calculate its size. If an
     /// argument is a storage variable, we load it and save it to a local variable.
@@ -1107,18 +1718,6 @@ fn array_outer_length(
     Expression::Variable(Codegen, Uint(32), array_length)
 }
 
-/// Increment an expression by some value.
-fn increment_by(expr: Expression, value: Expression) -> Expression {
-    Expression::Add(Codegen, Uint(32), false, expr.into(), value.into())
-}
-
-/// Increment an expression by four. This is useful because we save array sizes as uint32, so we
-/// need to increment the offset by four constantly.
-fn increment_four(expr: Expression) -> Expression {
-    let four = Expression::NumberLiteral(Codegen, Uint(32), 4.into());
-    increment_by(expr, four)
-}
-
 /// Check if we can MemCpy elements of an array to/from a buffer
 fn allow_direct_copy(
     array_ty: &Type,
@@ -1177,31 +1776,6 @@ fn calculate_array_bytes_size(length_var: usize, elem_ty: &Type, ns: &Namespace)
     Expression::Multiply(Codegen, Uint(32), false, var.into(), size.into())
 }
 
-/// Retrieve a dynamic array length from the encoded buffer. It returns the variable number in which
-/// the length has been stored
-fn retrieve_array_length(
-    buffer: &Expression,
-    offset: &Expression,
-    vartab: &mut Vartable,
-    cfg: &mut ControlFlowGraph,
-) -> usize {
-    let array_length = vartab.temp_anonymous(&Uint(32));
-    cfg.add(
-        vartab,
-        Instr::Set {
-            loc: Codegen,
-            res: array_length,
-            expr: Expression::Builtin(
-                Codegen,
-                vec![Uint(32)],
-                Builtin::ReadFromBuffer,
-                vec![buffer.clone(), offset.clone()],
-            ),
-        },
-    );
-    array_length
-}
-
 /// Allocate an array in memory and return its variable number.
 fn allocate_array(
     ty: &Type,

+ 199 - 41
src/codegen/encoding/scale_encoding.rs

@@ -1,13 +1,16 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
-use crate::codegen::encoding::{increment_by, AbiEncoding};
+use crate::codegen::encoding::AbiEncoding;
 use crate::codegen::vartable::Vartable;
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{Namespace, Parameter, Type, Type::Uint};
-use solang_parser::pt::{Loc, Loc::Codegen};
+use crate::sema::ast::StructType;
+use crate::sema::ast::{Namespace, Type, Type::Uint};
+use solang_parser::pt::Loc::Codegen;
 use std::collections::HashMap;
 
+use super::buffer_validator::BufferValidator;
+
 pub(super) struct ScaleEncoding {
     storage_cache: HashMap<usize, Expression>,
     packed_encoder: bool,
@@ -22,6 +25,159 @@ impl ScaleEncoding {
     }
 }
 
+/// Decoding the compact integer at current `offset` inside `buffer`.
+/// Returns the variable number of the decoded integer (32bit) and the width in bytes of the encoded version.
+/// More information can found in the /// [SCALE documentation](https://docs.substrate.io/reference/scale-codec/).
+fn decode_compact(
+    buffer: &Expression,
+    offset: &Expression,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> (usize, Expression) {
+    let decoded_var = vartab.temp_anonymous(&Uint(32));
+    let size_width_var = vartab.temp_anonymous(&Uint(32));
+    vartab.new_dirty_tracker();
+    let read_byte = Expression::Builtin(
+        Codegen,
+        vec![Uint(8)],
+        Builtin::ReadFromBuffer,
+        vec![buffer.clone(), offset.clone()],
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_width_var,
+            expr: Expression::ZeroExt(Codegen, Uint(32), read_byte.into()),
+        },
+    );
+    let size_width = Expression::Variable(Codegen, Uint(32), size_width_var);
+    let two = Expression::NumberLiteral(Codegen, Uint(32), 2.into());
+    let three = Expression::NumberLiteral(Codegen, Uint(32), 3.into());
+    let cond = Expression::BitwiseAnd(Codegen, Uint(32), size_width.clone().into(), three.into());
+    let cases = &[
+        (
+            Expression::NumberLiteral(Codegen, Uint(32), 0.into()),
+            cfg.new_basic_block("case_0".into()),
+        ),
+        (
+            Expression::NumberLiteral(Codegen, Uint(32), 1.into()),
+            cfg.new_basic_block("case_1".into()),
+        ),
+        (
+            Expression::NumberLiteral(Codegen, Uint(32), 2.into()),
+            cfg.new_basic_block("case_2".into()),
+        ),
+    ];
+    let default = cfg.new_basic_block("case_default".into());
+    cfg.add(
+        vartab,
+        Instr::Switch {
+            cond,
+            cases: cases.to_vec(),
+            default,
+        },
+    );
+
+    let done = cfg.new_basic_block("done".into());
+    // We will land in the default block for sizes of 2**30 (1GB) or larger.
+    // Such big sizes are invalid for smart contracts and should never occur anyways.
+    cfg.set_basic_block(default);
+    cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
+
+    cfg.set_basic_block(cases[0].1);
+    let expr = Expression::ShiftRight(
+        Codegen,
+        Uint(32),
+        size_width.clone().into(),
+        two.clone().into(),
+        false,
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: decoded_var,
+            expr,
+        },
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_width_var,
+            expr: Expression::NumberLiteral(Codegen, Uint(32), 1.into()),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
+
+    cfg.set_basic_block(cases[1].1);
+    let read_byte = Expression::Builtin(
+        Codegen,
+        vec![Uint(16)],
+        Builtin::ReadFromBuffer,
+        vec![buffer.clone(), offset.clone()],
+    );
+    let expr = Expression::ShiftRight(
+        Codegen,
+        Uint(32),
+        Expression::ZeroExt(Codegen, Uint(32), read_byte.into()).into(),
+        two.clone().into(),
+        false,
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: decoded_var,
+            expr,
+        },
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_width_var,
+            expr: two.clone(),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
+
+    cfg.set_basic_block(cases[2].1);
+    let read_byte = Expression::Builtin(
+        Codegen,
+        vec![Uint(32)],
+        Builtin::ReadFromBuffer,
+        vec![buffer.clone(), offset.clone()],
+    );
+    let expr = Expression::ShiftRight(Codegen, Uint(32), read_byte.into(), two.into(), false);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: decoded_var,
+            expr,
+        },
+    );
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Codegen,
+            res: size_width_var,
+            expr: Expression::NumberLiteral(Codegen, Uint(32), 4.into()),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: done });
+
+    vartab.set_dirty(decoded_var);
+    vartab.set_dirty(size_width_var);
+
+    cfg.set_basic_block(done);
+    cfg.set_phis(done, vartab.pop_dirty_tracker());
+
+    (decoded_var, size_width)
+}
+
 /// Encode `expr` into `buffer` as a compact integer. More information can found in the
 /// [SCALE documentation](https://docs.substrate.io/reference/scale-codec/).
 fn encode_compact(
@@ -213,49 +369,51 @@ impl AbiEncoding for ScaleEncoding {
         encode_compact(expr, Some(buffer), Some(offset), vartab, cfg)
     }
 
-    fn abi_decode(
+    fn decode_external_function(
         &self,
-        loc: &Loc,
         buffer: &Expression,
-        types: &[Type],
-        _ns: &Namespace,
+        offset: &Expression,
+        ty: &Type,
+        validator: &mut BufferValidator,
+        ns: &Namespace,
         vartab: &mut Vartable,
         cfg: &mut ControlFlowGraph,
-        buffer_size: Option<Expression>,
-    ) -> Vec<Expression> {
-        assert!(!self.packed_encoder);
-        let mut returns: Vec<Expression> = Vec::with_capacity(types.len());
-        let mut var_nos: Vec<usize> = Vec::with_capacity(types.len());
-        let mut decode_params: Vec<Parameter> = Vec::with_capacity(types.len());
-
-        for item in types {
-            let var_no = vartab.temp_anonymous(item);
-            var_nos.push(var_no);
-            returns.push(Expression::Variable(*loc, item.clone(), var_no));
-            decode_params.push(Parameter {
-                loc: Loc::Codegen,
-                id: None,
-                ty: item.clone(),
-                ty_loc: None,
-                indexed: false,
-                readonly: false,
-                recursive: false,
-            });
-        }
-
-        cfg.add(
-            vartab,
-            Instr::AbiDecode {
-                res: var_nos,
-                selector: None,
-                exception_block: None,
-                tys: decode_params,
-                data: buffer.clone(),
-                data_len: buffer_size,
-            },
+    ) -> (Expression, Expression) {
+        let size = Expression::NumberLiteral(Codegen, Uint(32), (ns.address_length + 4).into());
+        validator.validate_offset_plus_size(offset, &size, ns, vartab, cfg);
+        let address = Expression::Builtin(
+            Codegen,
+            vec![Type::Address(false)],
+            Builtin::ReadFromBuffer,
+            vec![buffer.clone(), offset.clone()],
         );
+        let new_offset = offset.clone().add_u32(Expression::NumberLiteral(
+            Codegen,
+            Uint(32),
+            ns.address_length.into(),
+        ));
+        let selector = Expression::Builtin(
+            Codegen,
+            vec![Type::FunctionSelector],
+            Builtin::ReadFromBuffer,
+            vec![buffer.clone(), new_offset],
+        );
+        let ext_func = Expression::StructLiteral(
+            Codegen,
+            Type::Struct(StructType::ExternalFunction),
+            vec![selector, address],
+        );
+        (Expression::Cast(Codegen, ty.clone(), ext_func.into()), size)
+    }
 
-        returns
+    fn retrieve_array_length(
+        &self,
+        buffer: &Expression,
+        offset: &Expression,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> (usize, Expression) {
+        decode_compact(buffer, offset, vartab, cfg)
     }
 
     fn storage_cache_insert(&mut self, arg_no: usize, expr: Expression) {
@@ -282,7 +440,7 @@ impl AbiEncoding for ScaleEncoding {
         if self.is_packed() {
             length
         } else {
-            increment_by(encode_compact(&length, None, None, vartab, cfg), length)
+            encode_compact(&length, None, None, vartab, cfg).add_u32(length)
         }
     }
 

+ 4 - 8
src/codegen/expression.rs

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use super::encoding::abi_encode;
+use super::encoding::{abi_decode, abi_encode};
 use super::storage::{
     array_offset, array_pop, array_push, storage_slots_array_pop, storage_slots_array_push,
 };
@@ -11,7 +11,6 @@ use super::{
 };
 use crate::codegen::array_boundary::handle_array_assign;
 use crate::codegen::constructor::call_constructor;
-use crate::codegen::encoding::create_encoder;
 use crate::codegen::error_msg_with_loc;
 use crate::codegen::unused_variable::should_remove_assignment;
 use crate::codegen::{Builtin, Expression};
@@ -2666,13 +2665,12 @@ pub fn emit_function_call(
 
                 // If the first element of returns is Void, we can discard the returns
                 if !dest_func.returns.is_empty() && returns[0] != Type::Void {
-                    let encoder = create_encoder(ns, false);
                     let tys = dest_func
                         .returns
                         .iter()
                         .map(|e| e.ty.clone())
                         .collect::<Vec<Type>>();
-                    encoder.abi_decode(
+                    abi_decode(
                         loc,
                         &Expression::ReturnData(*loc),
                         &tys,
@@ -2730,8 +2728,7 @@ pub fn emit_function_call(
                 );
 
                 if !func_returns.is_empty() && returns[0] != Type::Void {
-                    let encoder = create_encoder(ns, false);
-                    encoder.abi_decode(
+                    abi_decode(
                         loc,
                         &Expression::ReturnData(*loc),
                         returns,
@@ -2754,8 +2751,7 @@ pub fn emit_function_call(
             args,
         } => {
             let data = expression(&args[0], cfg, caller_contract_no, func, ns, vartab, opt);
-            let encoder = create_encoder(ns, false);
-            encoder.abi_decode(loc, &data, tys, ns, vartab, cfg, None)
+            abi_decode(loc, &data, tys, ns, vartab, cfg, None)
         }
         _ => unreachable!(),
     }

+ 11 - 0
src/codegen/mod.rs

@@ -659,6 +659,17 @@ impl RetrieveType for Expression {
 }
 
 impl Expression {
+    /// Increment an expression by some value.
+    pub(crate) fn add_u32(self, other: Expression) -> Self {
+        Expression::Add(
+            pt::Loc::Codegen,
+            Type::Uint(32),
+            false,
+            self.into(),
+            other.into(),
+        )
+    }
+
     pub(crate) fn cast(&self, to: &Type, ns: &Namespace) -> Expression {
         let from = self.ty();
 

+ 5 - 1
tests/codegen.rs

@@ -134,7 +134,11 @@ fn testcase(path: PathBuf) {
         println!("{stderr}");
         println!("OUTPUT: \n===8<===8<===\n{stdout}===8<===8<===\n");
 
-        panic!("NOT FOUND CHECK: {:?}", checks[current_check]);
+        panic!(
+            "NOT FOUND CHECK: {:?}, {}",
+            checks[current_check],
+            path.display()
+        );
     } else if current_fail < fails.len() {
         println!("STDERR: \n===8<===8<===\n{stderr}===8<===8<===\n");
 

+ 147 - 125
tests/codegen_testcases/solidity/borsh_decoding_complex_types.sol

@@ -7,235 +7,257 @@ contract Testing {
     }
 
     // BEGIN-CHECK: Testing::Testing::function::nonCteArray__bytes
-    function nonCteArray(bytes memory buffer) public pure returns (string[] memory) {
+    function nonCteArray(bytes memory buffer)
+        public
+        pure
+        returns (string[] memory)
+    {
         string[] memory a = abi.decode(buffer, (string[]));
 
         // CHECK: ty:uint32 %temp.10 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: ty:uint32 %temp.12 = uint32 0
-	    // CHECK: branchcond (unsigned uint32 4 <= %temp.10), block1, block2
+        // CHECK: ty:uint32 %temp.12 = uint32 0
+        // CHECK: ty:uint32 %temp.13 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+        // CHECK: branchcond (unsigned uint32 4 <= %temp.10), block1, block2
 
         // CHECK: block1: # inbounds
-	    // CHECK: ty:uint32 %temp.13 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:uint32 %temp.12 = uint32 4
-	    // CHECK: ty:string[] %temp.14 = (alloc string[] len %temp.13)
-	    // CHECK: ty:string[] %temp.11 = %temp.14
-	    // CHECK: ty:uint32 %for_i_0.temp.15 = uint32 0
-	    // CHECK: branch block3
+        // CHECK: ty:uint32 %temp.12 = uint32 4
+        // CHECK: ty:string[] %temp.14 = (alloc string[] len %temp.13)
+        // CHECK: ty:string[] %temp.11 = %temp.14
+        // CHECK: ty:uint32 %for_i_0.temp.15 = uint32 0
+        // CHECK: branch block3
 
         // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block3: # cond
-	    // CHECK: branchcond (unsigned less %for_i_0.temp.15 < (builtin ArrayLength (%temp.11))), block5, block6
+        // CHECK: branchcond (unsigned less %for_i_0.temp.15 < (builtin ArrayLength (%temp.11))), block5, block6
 
         // CHECK: block4: # next
-	    // CHECK: ty:uint32 %for_i_0.temp.15 = (%for_i_0.temp.15 + uint32 1)
-	    // CHECK: branch block3
+        // CHECK: ty:uint32 %for_i_0.temp.15 = (%for_i_0.temp.15 + uint32 1)
+        // CHECK: branch block3
 
         // CHECK: block5: # body
-	    // CHECK: ty:uint32 %1.cse_temp = (%temp.12 + uint32 4)
-	    // CHECK: branchcond (unsigned %1.cse_temp <= %temp.10), block7, block8
+        // CHECK: ty:uint32 %temp.16 = (builtin ReadFromBuffer ((arg #0), %temp.12))
+        // CHECK: ty:uint32 %1.cse_temp = (%temp.12 + uint32 4)
+        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.10), block7, block8
 
         // CHECK: block6: # end_for
-	    // CHECK: ty:uint32 %temp.12 = (%temp.12 - uint32 0)
-	    // CHECK: branchcond (unsigned less (uint32 0 + %temp.12) < %temp.10), block11, block12
-
+        // CHECK: ty:uint32 %temp.12 = (%temp.12 - uint32 0)
+        // CHECK: branchcond (unsigned less (uint32 0 + %temp.12) < %temp.10), block11, block12
         // CHECK: block7: # inbounds
-	    // CHECK: ty:uint32 %temp.16 = (builtin ReadFromBuffer ((arg #0), %temp.12))
-	    // CHECK: ty:uint32 %2.cse_temp = ((%temp.16 + uint32 4) + %temp.12)
-	    // CHECK: branchcond (unsigned %2.cse_temp <= %temp.10), block9, block10
+
+        // CHECK: ty:uint32 %2.cse_temp = (%temp.12 + (%temp.16 + uint32 4))
+        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.10), block9, block10
 
         // CHECK: block8: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block9: # inbounds
-	    // CHECK: ty:string %temp.17 = (alloc string len %temp.16)
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: %1.cse_temp), dest: %temp.17, bytes_len: %temp.16
-	    // CHECK: store (subscript string[] %temp.11[%for_i_0.temp.15]), %temp.17
-	    // CHECK: ty:uint32 %temp.12 = %2.cse_temp
-	    // CHECK: branch block4
+        // CHECK: ty:string %temp.17 = (alloc string len %temp.16)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: %1.cse_temp), dest: %temp.17, bytes_len: %temp.16
+        // CHECK: store (subscript string[] %temp.11[%for_i_0.temp.15]), %temp.17
+        // CHECK: ty:uint32 %temp.12 = %2.cse_temp
+        // CHECK: branch block4
 
         // CHECK: block10: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
-		// CHECK: block11: # not_all_bytes_read
-		// CHECK: assert-failure
-		// CHECK: block12: # buffer_read
-		// CHECK: ty:string[] %a = %temp.11
+        // CHECK: block11: # not_all_bytes_read
+        // CHECK: assert-failure
+
+        // CHECK: block12: # buffer_read
+        // CHECK: ty:string[] %a = %temp.11
 
         return a;
     }
 
     // BEGIN-CHECK: Testing::Testing::function::complexStruct__bytes
-    function complexStruct(bytes memory buffer) public pure returns (NonConstantStruct memory) {
+    function complexStruct(bytes memory buffer)
+        public
+        pure
+        returns (NonConstantStruct memory)
+    {
         NonConstantStruct memory cte = abi.decode(buffer, (NonConstantStruct));
 
-	    // CHECK: ty:uint32 %temp.20 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 8 <= %temp.20), block1, block2
+        // CHECK: block0: # entry
+        // CHECK: ty:bytes %buffer = (arg #0)
+        // CHECK: ty:uint32 %temp.20 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned uint32 8 <= %temp.20), block1, block2
 
         // CHECK: block1: # inbounds
-		// CHECK: ty:uint64 %temp.21 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:uint32 %temp.23 = uint32 8
-	    // CHECK: branchcond (unsigned uint32 12 <= %temp.20), block3, block4
+        // CHECK: ty:uint64 %temp.21 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+        // CHECK: ty:uint32 %temp.23 = uint32 8
+        // CHECK: ty:uint32 %temp.24 = (builtin ReadFromBuffer ((arg #0), uint32 8))
+        // CHECK: branchcond (unsigned uint32 12 <= %temp.20), block3, block4
 
         // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block3: # inbounds
-	    // CHECK: ty:uint32 %temp.24 = (builtin ReadFromBuffer ((arg #0), uint32 8))
-	    // CHECK: ty:uint32 %temp.23 = uint32 12
-	    // CHECK: ty:string[] %temp.25 = (alloc string[] len %temp.24)
-	    // CHECK: ty:string[] %temp.22 = %temp.25
-	    // CHECK: ty:uint32 %for_i_0.temp.26 = uint32 0
-	    // CHECK: branch block5
+        // CHECK: ty:uint32 %temp.23 = uint32 12
+        // CHECK: ty:string[] %temp.25 = (alloc string[] len %temp.24)
+        // CHECK: ty:string[] %temp.22 = %temp.25
+        // CHECK: ty:uint32 %for_i_0.temp.26 = uint32 0
+        // CHECK: branch block5
 
         // CHECK: block4: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block5: # cond
-	    // CHECK: branchcond (unsigned less %for_i_0.temp.26 < (builtin ArrayLength (%temp.22))), block7, block8
+        // CHECK: branchcond (unsigned less %for_i_0.temp.26 < (builtin ArrayLength (%temp.22))), block7, block8
 
         // CHECK: block6: # next
-	    // CHECK: ty:uint32 %for_i_0.temp.26 = (%for_i_0.temp.26 + uint32 1)
-	    // CHECK: branch block5
+        // CHECK: # phis: temp.23,for_i_0.temp.26
+        // CHECK: # reaching: buffer:[0:0],  temp.20:[0:1],  temp.21:[1:0],  temp.24:[1:2],  temp.28:[11:0],  temp.25:[3:1],  temp.22:[3:1],  for_i_0.temp.26:[3:3, 6:0],  temp.27:[7:0],  temp.23:[11:3]
+        // CHECK: ty:uint32 %for_i_0.temp.26 = (%for_i_0.temp.26 + uint32 1)
+        // CHECK: branch block5
 
         // CHECK: block7: # body
-	    // CHECK: ty:uint32 %1.cse_temp = (%temp.23 + uint32 4)
-	    // CHECK: branchcond (unsigned %1.cse_temp <= %temp.20), block9, block10
+        // CHECK: ty:uint32 %temp.27 = (builtin ReadFromBuffer ((arg #0), %temp.23))
+        // CHECK: ty:uint32 %1.cse_temp = (%temp.23 + uint32 4)
+        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.20), block9, block10
 
         // CHECK: block8: # end_for
-    	// CHECK: ty:uint32 %temp.23 = (%temp.23 - uint32 8)
-	    // CHECK: ty:struct Testing.NonConstantStruct %temp.29 = struct { %temp.21, %temp.22 }
-	    // CHECK: branchcond (unsigned less (uint32 0 + (uint32 8 + %temp.23)) < %temp.20), block13, block14
+        // CHECK: ty:uint32 %temp.23 = (%temp.23 - uint32 8)
+        // CHECK: ty:struct Testing.NonConstantStruct %temp.29 = struct { %temp.21, %temp.22 }
+        // CHECK: branchcond (unsigned less (uint32 0 + (uint32 8 + %temp.23)) < %temp.20), block13, block14
 
         // CHECK: block9: # inbounds
-	    // CHECK: ty:uint32 %temp.27 = (builtin ReadFromBuffer ((arg #0), %temp.23))
-	    // CHECK: ty:uint32 %2.cse_temp = ((%temp.27 + uint32 4) + %temp.23)
-	    // CHECK: branchcond (unsigned %2.cse_temp <= %temp.20), block11, block12
-
+        // CHECK: ty:uint32 %2.cse_temp = (%temp.23 + (%temp.27 + uint32 4))
+        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.20), block11, block12
         // CHECK: block10: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block11: # inbounds
-	    // CHECK: ty:string %temp.28 = (alloc string len %temp.27)
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: %1.cse_temp), dest: %temp.28, bytes_len: %temp.27
-	    // CHECK: store (subscript string[] %temp.22[%for_i_0.temp.26]), %temp.28
-	    // CHECK: ty:uint32 %temp.23 = %2.cse_temp
-	    // CHECK: branch block6
+        // CHECK: ty:string %temp.28 = (alloc string len %temp.27)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: %1.cse_temp), dest: %temp.28, bytes_len: %temp.27
+        // CHECK: store (subscript string[] %temp.22[%for_i_0.temp.26]), %temp.28
+        // CHECK: ty:uint32 %temp.23 = %2.cse_temp
+        // CHECK: branch block6
 
         // CHECK: block12: # out_of_bounds
         // CHECK: assert-failure
 
-		// CHECK: block13: # not_all_bytes_read
-		// CHECK: assert-failure
+        // CHECK: block13: # not_all_bytes_read
+        // CHECK: assert-failure
 
-		// CHECK: block14: # buffer_read
-		// CHECK: ty:struct Testing.NonConstantStruct %cte = %temp.29
+        // CHECK: block14: # buffer_read
+        // CHECK: ty:struct Testing.NonConstantStruct %cte = %temp.29
         return cte;
     }
 
     NonConstantStruct[] storage_vec;
+
     // BEGIN-CHECK: Testing::Testing::function::complexArray__bytes
     function complexArray(bytes memory buffer) public {
-        NonConstantStruct[] memory arr = abi.decode(buffer, (NonConstantStruct[]));
+        NonConstantStruct[] memory arr = abi.decode(
+            buffer,
+            (NonConstantStruct[])
+        );
 
-	    // CHECK: ty:uint32 %temp.32 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: ty:uint32 %temp.34 = uint32 0
-	    // CHECK: branchcond (unsigned uint32 4 <= %temp.32), block1, block2
+        // CHECK: ty:bytes %buffer = (arg #0)
+        // CHECK: ty:uint32 %temp.32 = (builtin ArrayLength ((arg #0)))
+        // CHECK: ty:uint32 %temp.34 = uint32 0
+        // CHECK: ty:uint32 %temp.35 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+        // CHECK: branchcond (unsigned uint32 4 <= %temp.32), block1, block2
 
         // CHECK: block1: # inbounds
-    	// CHECK: ty:uint32 %temp.35 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:uint32 %temp.34 = uint32 4
-	    // CHECK: ty:struct Testing.NonConstantStruct[] %temp.36 = (alloc struct Testing.NonConstantStruct[] len %temp.35)
-	    // CHECK: ty:struct Testing.NonConstantStruct[] %temp.33 = %temp.36
-	    // CHECK: ty:uint32 %for_i_0.temp.37 = uint32 0
-	    // CHECK: branch block3
+        // CHECK: ty:uint32 %temp.34 = uint32 4
+        // CHECK: ty:struct Testing.NonConstantStruct[] %temp.36 = (alloc struct Testing.NonConstantStruct[] len %temp.35)
+        // CHECK: ty:struct Testing.NonConstantStruct[] %temp.33 = %temp.36
+        // CHECK: ty:uint32 %for_i_0.temp.37 = uint32 0
+        // CHECK: branch block3
 
         // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block3: # cond
-	    // CHECK: branchcond (unsigned less %for_i_0.temp.37 < (builtin ArrayLength (%temp.33))), block5, block6
+        // CHECK: branchcond (unsigned less %for_i_0.temp.37 < (builtin ArrayLength (%temp.33))), block5, block6
 
         // CHECK: block4: # next
-	    // CHECK: ty:uint32 %for_i_0.temp.37 = (%for_i_0.temp.37 + uint32 1)
-	    // CHECK: branch block3
+        // CHECK: ty:uint32 %for_i_0.temp.37 = (%for_i_0.temp.37 + uint32 1)
+        // CHECK: branch block3
 
         // CHECK: block5: # body
-	    // CHECK: ty:uint32 %1.cse_temp = (%temp.34 + uint32 8)
-	    // CHECK: branchcond (unsigned %1.cse_temp <= %temp.32), block7, block8
+        // CHECK: ty:uint32 %1.cse_temp = (%temp.34 + uint32 8)
+        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.32), block7, block8
 
         // CHECK: block6: # end_for
-	    // CHECK: ty:uint32 %temp.34 = (%temp.34 - uint32 0)
-	    // CHECK: branchcond (unsigned less (uint32 0 + %temp.34) < %temp.32), block19, block20
+        // CHECK: ty:uint32 %temp.34 = (%temp.34 - uint32 0)
+        // CHECK: branchcond (unsigned less (uint32 0 + %temp.34) < %temp.32), block19, block20
 
         // CHECK: block7: # inbounds
-		// CHECK: ty:uint64 %temp.38 = (builtin ReadFromBuffer ((arg #0), %temp.34))
-	    // CHECK: ty:uint32 %temp.40 = %1.cse_temp
-	    // CHECK: ty:uint32 %2.cse_temp = (%temp.40 + uint32 4)
-	    // CHECK: branchcond (unsigned %2.cse_temp <= %temp.32), block9, block10
+        // CHECK: ty:uint64 %temp.38 = (builtin ReadFromBuffer ((arg #0), %temp.34))
+        // CHECK: ty:uint32 %temp.40 = %1.cse_temp
+        // CHECK: ty:uint32 %temp.41 = (builtin ReadFromBuffer ((arg #0), %temp.40))
+        // CHECK: ty:uint32 %2.cse_temp = (%temp.40 + uint32 4)
+        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.32), block9, block10
 
         // CHECK: block8: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block9: # inbounds
-	    // CHECK: ty:uint32 %temp.41 = (builtin ReadFromBuffer ((arg #0), %temp.40))
-	    // CHECK: ty:uint32 %temp.40 = %2.cse_temp
-	    // CHECK: ty:string[] %temp.42 = (alloc string[] len %temp.41)
-	    // CHECK: ty:string[] %temp.39 = %temp.42
-	    // CHECK: ty:uint32 %for_i_0.temp.43 = uint32 0
-	    // CHECK: branch block11
+        // CHECK: ty:uint32 %temp.40 = %2.cse_temp
+        // CHECK: ty:string[] %temp.42 = (alloc string[] len %temp.41)
+        // CHECK: ty:string[] %temp.39 = %temp.42
+        // CHECK: ty:uint32 %for_i_0.temp.43 = uint32 0
+        // CHECK: branch block11
 
         // CHECK: block10: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block11: # cond
-	    // CHECK: branchcond (unsigned less %for_i_0.temp.43 < (builtin ArrayLength (%temp.39))), block13, block14
+        // CHECK: branchcond (unsigned less %for_i_0.temp.43 < (builtin ArrayLength (%temp.39))), block13, block14
 
         // CHECK: block12: # next
-	    // CHECK: ty:uint32 %for_i_0.temp.43 = (%for_i_0.temp.43 + uint32 1)
-	    // CHECK: branch block11
+        // CHECK: ty:uint32 %for_i_0.temp.43 = (%for_i_0.temp.43 + uint32 1)
+        // CHECK: branch block11
 
         // CHECK: block13: # body
-	    // CHECK: ty:uint32 %3.cse_temp = (%temp.40 + uint32 4)
-	    // CHECK: branchcond (unsigned %3.cse_temp <= %temp.32), block15, block16
+        // CHECK: ty:uint32 %temp.44 = (builtin ReadFromBuffer ((arg #0), %temp.40))
+        // CHECK: ty:uint32 %3.cse_temp = (%temp.40 + uint32 4)
+        // CHECK: branchcond (unsigned %3.cse_temp <= %temp.32), block15, block16
 
         // CHECK: block14: # end_for
-	    // CHECK: ty:uint32 %temp.40 = (%temp.40 - (%temp.34 + uint32 8))
-	    // CHECK: ty:struct Testing.NonConstantStruct %temp.46 = struct { %temp.38, %temp.39 }
-		// CHECK: store (subscript struct Testing.NonConstantStruct[] %temp.33[%for_i_0.temp.37]), (load %temp.46)
-	    // CHECK: ty:uint32 %temp.34 = ((uint32 8 + %temp.40) + %temp.34)
+        // CHECK: ty:uint32 %temp.40 = (%temp.40 - (%temp.34 + uint32 8))
+        // CHECK: ty:struct Testing.NonConstantStruct %temp.46 = struct { %temp.38, %temp.39 }
+        // CHECK: store (subscript struct Testing.NonConstantStruct[] %temp.33[%for_i_0.temp.37]), (load %temp.46)
+        // CHECK: ty:uint32 %temp.34 = ((uint32 8 + %temp.40) + %temp.34)
         // CHECK: branch block4
 
         // CHECK: block15: # inbounds
-	    // CHECK: ty:uint32 %temp.44 = (builtin ReadFromBuffer ((arg #0), %temp.40))
-	    // CHECK: ty:uint32 %4.cse_temp = ((%temp.44 + uint32 4) + %temp.40)
-	    // CHECK: branchcond (unsigned %4.cse_temp <= %temp.32), block17, block18
+        // CHECK: ty:uint32 %4.cse_temp = (%temp.40 + (%temp.44 + uint32 4))
+        // CHECK: branchcond (unsigned %4.cse_temp <= %temp.32), block17, block18
 
         // CHECK: block16: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block17: # inbounds
-	    // CHECK: ty:string %temp.45 = (alloc string len %temp.44)
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: %3.cse_temp), dest: %temp.45, bytes_len: %temp.44
-	    // CHECK: store (subscript string[] %temp.39[%for_i_0.temp.43]), %temp.45
-	    // CHECK: ty:uint32 %temp.40 = %4.cse_temp
-	    // CHECK: branch block12
+        // CHECK: ty:string %temp.45 = (alloc string len %temp.44)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: %3.cse_temp), dest: %temp.45, bytes_len: %temp.44
+        // CHECK: store (subscript string[] %temp.39[%for_i_0.temp.43]), %temp.45
+        // CHECK: ty:uint32 %temp.40 = %4.cse_temp
+        // CHECK: branch block12
 
         // CHECK: block18: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
+
+        // CHECK: block19: # not_all_bytes_read
+        // CHECK: assert-failure
 
-		// CHECK: block19: # not_all_bytes_read
-		// CHECK: assert-failure
+        // CHECK: block20: # buffer_read
+        // CHECK: ty:struct Testing.NonConstantStruct[] %arr = %temp.33
+        // CHECK: ty:struct Testing.NonConstantStruct[] storage %temp.47 = %arr
+        // CHECK: store storage slot(uint32 16) ty:struct Testing.NonConstantStruct[] = %temp.47
 
-		// CHECK: block20: # buffer_read
-		// CHECK: ty:struct Testing.NonConstantStruct[] %arr = %temp.33
-		// CHECK: ty:struct Testing.NonConstantStruct[] storage %temp.47 = %arr
-		// CHECK: store storage slot(uint32 16) ty:struct Testing.NonConstantStruct[] = %temp.47
         storage_vec = arr;
     }
 
-    function getItem(uint32 idx) public view returns (NonConstantStruct memory) {
+    function getItem(uint32 idx)
+        public
+        view
+        returns (NonConstantStruct memory)
+    {
         return storage_vec[idx];
     }
-}
+}

+ 85 - 81
tests/codegen_testcases/solidity/borsh_decoding_simple_types.sol

@@ -130,49 +130,49 @@ contract Testing {
     function stringAndBytes(bytes memory buffer) public pure returns (bytes memory, string memory) {
         (bytes memory a, string memory b) = abi.decode(buffer, (bytes, string));
 
-        // CHECK: ty:bytes %buffer = (arg #0)
-	    // CHECK: ty:uint32 %temp.86 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 4 <= %temp.86), block1, block2
+		// CHECK: ty:bytes %buffer = (arg #0)
+		// CHECK: ty:uint32 %temp.86 = (builtin ArrayLength ((arg #0)))
+		// CHECK: ty:uint32 %temp.87 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+		// CHECK: branchcond (unsigned uint32 4 <= %temp.86), block1, block2
 
         // CHECK: block1: # inbounds
-	    // CHECK: ty:uint32 %temp.87 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:uint32 %1.cse_temp = ((%temp.87 + uint32 4) + uint32 0)
-	    // CHECK: branchcond (unsigned %1.cse_temp <= %temp.86), block3, block4
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + (%temp.87 + uint32 4))
+        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.86), block3, block4
 
         // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block3: # inbounds
-	    // CHECK: ty:bytes %temp.88 = (alloc bytes len %temp.87)
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 4), dest: %temp.88, bytes_len: %temp.87
-	    // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 4)
-	    // CHECK: branchcond (unsigned %2.cse_temp <= %temp.86), block5, block6
+        // CHECK: ty:bytes %temp.88 = (alloc bytes len %temp.87)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 4), dest: %temp.88, bytes_len: %temp.87
+        // CHECK: ty:uint32 %temp.89 = (builtin ReadFromBuffer ((arg #0), (uint32 0 + (%temp.87 + uint32 4))))
+        // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 4)
+        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.86), block5, block6
 
         // CHECK: block4: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block5: # inbounds
-	    // CHECK: ty:uint32 %temp.89 = (builtin ReadFromBuffer ((arg #0), (uint32 0 + (%temp.87 + uint32 4))))
-	    // CHECK: ty:uint32 %3.cse_temp = ((%temp.89 + uint32 4) + %1.cse_temp)
-	    // CHECK: branchcond (unsigned %3.cse_temp <= %temp.86), block7, block8
+        // CHECK: ty:uint32 %3.cse_temp = (%1.cse_temp + (%temp.89 + uint32 4))
+        // CHECK: branchcond (unsigned %3.cse_temp <= %temp.86), block7, block8
 
         // CHECK: block6: # out_of_bounds
-    	// CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block7: # inbounds
-	    // CHECK: ty:string %temp.90 = (alloc string len %temp.89)
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: %2.cse_temp), dest: %temp.90, bytes_len: %temp.89
-	    // CHECK: branchcond (unsigned less %3.cse_temp < %temp.86), block9, block10
+        // CHECK: ty:string %temp.90 = (alloc string len %temp.89)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: %2.cse_temp), dest: %temp.90, bytes_len: %temp.89
+        // CHECK: branchcond (unsigned less %3.cse_temp < %temp.86), block9, block10
 
         // CHECK: block8: # out_of_bounds
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block9: # not_all_bytes_read
-	    // CHECK: assert-failure
+        // CHECK: assert-failure
 
         // CHECK: block10: # buffer_read
-	    // CHECK: ty:bytes %a = %temp.88
-	    // CHECK: ty:string %b = %temp.90
+        // CHECK: ty:bytes %a = %temp.88
+        // CHECK: ty:string %b = %temp.90
 
         return (a, b);
     }
@@ -184,21 +184,24 @@ contract Testing {
     // BEGIN-CHECK: Testing::Testing::function::decodeEnum__bytes
     function decodeEnum(bytes memory buffer) public pure returns (WeekDays) {
         WeekDays a = abi.decode(buffer, (WeekDays));
-	    // CHECK: ty:uint32 %temp.94 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 1 <= %temp.94), block1, block2
 
-        // CHECK: block1: # inbounds
-	    // CHECK: ty:enum Testing.WeekDays %temp.95 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: branchcond (unsigned less uint32 1 < %temp.94), block3, block4
+		// CHECK: ty:bytes %buffer = (arg #0)
+		// CHECK: ty:uint32 %temp.94 = (builtin ArrayLength ((arg #0)))
+		// CHECK: branchcond (unsigned uint32 1 <= %temp.94), block1, block2
 
-        // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+		// CHECK: block1: # inbounds
+		// CHECK: ty:enum Testing.WeekDays %temp.95 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+		// CHECK: branchcond (unsigned less uint32 1 < %temp.94), block3, block4
 
-        // CHECK: block3: # not_all_bytes_read
-	    // CHECK: assert-failure
+		// CHECK: block2: # out_of_bounds
+		// CHECK: assert-failure
+
+		// CHECK: block3: # not_all_bytes_read
+		// CHECK: assert-failure
+
+		// CHECK: block4: # buffer_read
+		// CHECK: ty:enum Testing.WeekDays %a = %temp.95
 
-        // CHECK: block4: # buffer_read
-	    // CHECK: ty:enum Testing.WeekDays %a = %temp.95
         return a;
     }
 
@@ -217,27 +220,28 @@ contract Testing {
     function decodeStruct(bytes memory buffer) public pure returns (noPadStruct memory, PaddedStruct memory) {
         (noPadStruct memory a, PaddedStruct memory b) = abi.decode(buffer, (noPadStruct, PaddedStruct));
 
-        // CHECK: ty:uint32 %temp.96 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 57 <= %temp.96), block1, block2
+		// CHECK: ty:uint32 %temp.96 = (builtin ArrayLength ((arg #0)))
+		// CHECK: branchcond (unsigned uint32 57 <= %temp.96), block1, block2
 
-        // CHECK: block1: # inbounds
-	    // CHECK: ty:struct Testing.noPadStruct %temp.97 = struct {  }
-	    // CHECK: memcpy src: %buffer, dest: %temp.97, bytes_len: uint32 8
-	    // CHECK: ty:uint128 %temp.98 = (builtin ReadFromBuffer ((arg #0), uint32 8))
-	    // CHECK: ty:uint8 %temp.99 = (builtin ReadFromBuffer ((arg #0), uint32 24))
-	    // CHECK: ty:bytes32 %temp.100 = (builtin ReadFromBuffer ((arg #0), uint32 25))
-	    // CHECK: ty:struct Testing.PaddedStruct %temp.101 = struct { %temp.98, %temp.99, %temp.100 }
-	    // CHECK: branchcond (unsigned less uint32 57 < %temp.96), block3, block4
+		// CHECK: block1: # inbounds
+		// CHECK: ty:struct Testing.noPadStruct %temp.97 = struct {  }
+		// CHECK: memcpy src: %buffer, dest: %temp.97, bytes_len: uint32 8
+        // CHECK: ty:uint128 %temp.98 = (builtin ReadFromBuffer ((arg #0), uint32 8))
+        // CHECK: ty:uint8 %temp.99 = (builtin ReadFromBuffer ((arg #0), uint32 24))
+        // CHECK: ty:bytes32 %temp.100 = (builtin ReadFromBuffer ((arg #0), uint32 25))
+        // CHECK: ty:struct Testing.PaddedStruct %temp.101 = struct { %temp.98, %temp.99, %temp.100 }
+        // CHECK: branchcond (unsigned less uint32 57 < %temp.96), block3, block4
+		
+		// CHECK: block2: # out_of_bounds
+		// CHECK: assert-failure
 
-        // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+		// CHECK: block3: # not_all_bytes_read
+		// CHECK: assert-failure
 
-        // CHECK: block3: # not_all_bytes_read
-	    // CHECK: assert-failure
+		// CHECK: block4: # buffer_read
+		// CHECK: ty:struct Testing.noPadStruct %a = %temp.97
+		// CHECK: ty:struct Testing.PaddedStruct %b = %temp.101
 
-        // CHECK: block4: # buffer_read
-	    // CHECK: ty:struct Testing.noPadStruct %a = %temp.97
-	    // CHECK: ty:struct Testing.PaddedStruct %b = %temp.101
         return (a, b);
     }
 
@@ -246,42 +250,42 @@ contract Testing {
         (uint32[4] memory a, noPadStruct[2] memory b, noPadStruct[] memory c) =
         abi.decode(buffer, (uint32[4], noPadStruct[2], noPadStruct[]));
 
-        // CHECK: ty:uint32 %temp.102 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 32 <= %temp.102), block1, block2
-
-        // CHECK: block1: # inbounds
-	    // CHECK: ty:uint32[4] %temp.103 =  [  ]
-	    // CHECK: memcpy src: %buffer, dest: %temp.103, bytes_len: uint32 16
-	    // CHECK: ty:struct Testing.noPadStruct[2] %temp.104 =  [  ]
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 16), dest: %temp.104, bytes_len: uint32 16
-	    // CHECK: branchcond (unsigned uint32 36 <= %temp.102), block3, block4
-
-        // CHECK: block2: # out_of_bounds
-	    // CHECK: assert-failure
+		// CHECK: ty:uint32 %temp.102 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned uint32 32 <= %temp.102), block1, block2
+
+		// CHECK: block1: # inbounds
+        // CHECK: ty:uint32[4] %temp.103 =  [  ]
+        // CHECK: memcpy src: %buffer, dest: %temp.103, bytes_len: uint32 16
+        // CHECK: ty:struct Testing.noPadStruct[2] %temp.104 =  [  ]
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 16), dest: %temp.104, bytes_len: uint32 16
+        // CHECK: ty:uint32 %temp.105 = (builtin ReadFromBuffer ((arg #0), uint32 32))
+        // CHECK: branchcond (unsigned uint32 36 <= %temp.102), block3, block4
+		
+		// CHECK: block2: # out_of_bounds
+        // CHECK: assert-failure
 
-        // CHECK: block3: # inbounds
-	    // CHECK: ty:uint32 %temp.105 = (builtin ReadFromBuffer ((arg #0), uint32 32))
-	    // CHECK: ty:struct Testing.noPadStruct[] %temp.106 = (alloc struct Testing.noPadStruct[] len %temp.105)
-	    // CHECK: ty:uint32 %1.cse_temp = (%temp.105 * uint32 8)
-	    // CHECK: branchcond (unsigned (uint32 36 + %1.cse_temp) <= %temp.102), block5, block6
+		// CHECK: block3: # inbounds
+        // CHECK: ty:struct Testing.noPadStruct[] %temp.106 = (alloc struct Testing.noPadStruct[] len %temp.105)
+        // CHECK: ty:uint32 %1.cse_temp = (%temp.105 * uint32 8)
+        // CHECK: branchcond (unsigned (uint32 36 + %1.cse_temp) <= %temp.102), block5, block6
 
-        // CHECK: block4: # out_of_bounds
-	    // CHECK: assert-failure
+		// CHECK: block4: # out_of_bounds
+        // CHECK: assert-failure
 
-        // CHECK: block5: # inbounds
-	    // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 36), dest: %temp.106, bytes_len: %1.cse_temp
-	    // CHECK: branchcond (unsigned less (uint32 32 + (%1.cse_temp + uint32 4)) < %temp.102), block7, block8
+		// CHECK: block5: # inbounds
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 36), dest: %temp.106, bytes_len: %1.cse_temp
+        // CHECK: branchcond (unsigned less (uint32 32 + (%1.cse_temp + uint32 4)) < %temp.102), block7, block8
 
-        // CHECK: block6: # out_of_bounds
-	    // CHECK: assert-failure
+		// CHECK: block6: # out_of_bounds
+        // CHECK: assert-failure
 
-        // CHECK: block7: # not_all_bytes_read
-	    // CHECK: assert-failure
+		// CHECK: block7: # not_all_bytes_read
+        // CHECK: assert-failure
 
-        // CHECK: block8: # buffer_read
-	    // CHECK: ty:uint32[4] %a = %temp.103
-	    // CHECK: ty:struct Testing.noPadStruct[2] %b = %temp.104
-	    // CHECK: ty:struct Testing.noPadStruct[] %c = %temp.106
+		// CHECK: block8: # buffer_read
+        // CHECK: ty:uint32[4] %a = %temp.103
+        // CHECK: ty:struct Testing.noPadStruct[2] %b = %temp.104
+        // CHECK: ty:struct Testing.noPadStruct[] %c = %temp.106
 
         return (a, b, c);
     }

+ 3 - 3
tests/codegen_testcases/solidity/encode_decode_double_dynamic_array.sol

@@ -89,10 +89,10 @@ contract Testing {
 
 	    // CHECK: ty:uint32 %temp.14 = (builtin ArrayLength ((arg #0)))
 	    // CHECK: ty:uint32 %temp.16 = uint32 0
+	    // CHECK: ty:uint32 %temp.17 = (builtin ReadFromBuffer ((arg #0), uint32 0))
 	    // CHECK: branchcond (unsigned uint32 4 <= %temp.14), block1, block2
-
+        
         // CHECK: block1: # inbounds
-	    // CHECK: ty:uint32 %temp.17 = (builtin ReadFromBuffer ((arg #0), uint32 0))
 	    // CHECK: ty:uint32 %temp.16 = uint32 4
 	    // CHECK: ty:uint16[][] %temp.18 = (alloc uint16[][] len %temp.17)
 	    // CHECK: ty:uint16[][] %temp.15 = %temp.18
@@ -110,6 +110,7 @@ contract Testing {
         // CHECK: branch block3
 
         // CHECK: block5: # body
+	    // CHECK: ty:uint32 %temp.20 = (builtin ReadFromBuffer ((arg #0), %temp.16))
 	    // CHECK: ty:uint32 %1.cse_temp = (%temp.16 + uint32 4)
 	    // CHECK: branchcond (unsigned %1.cse_temp <= %temp.14), block7, block8
 
@@ -118,7 +119,6 @@ contract Testing {
 	    // CHECK: branchcond (unsigned less (uint32 0 + %temp.16) < %temp.14), block15, block16
 
         // CHECK: block7: # inbounds
-	    // CHECK: ty:uint32 %temp.20 = (builtin ReadFromBuffer ((arg #0), %temp.16))
 	    // CHECK: ty:uint32 %temp.16 = %1.cse_temp
 	    // CHECK: ty:uint16[] %temp.21 = (alloc uint16[] len %temp.20)
 	    // CHECK: store (subscript uint16[][] %temp.15[%for_i_1.temp.19]), %temp.21

+ 130 - 23
tests/codegen_testcases/solidity/scale.sol

@@ -5,8 +5,8 @@ contract ExternalFunctions {
 
     // BEGIN-CHECK: ExternalFunctions::ExternalFunctions::function::to_storage
     function to_storage() public {
-        // CHECK: ty:function(int32) external returns (uint64) storage %temp.3 = function(int32) external returns (uint64)(function(int32) external returns (uint64)(struct { hex"42761137", (builtin GetAddress ()) }))
-        // CHECK: store storage slot(uint256 0) ty:function(int32) external returns (uint64) = %temp.3
+        // CHECK: ty:function(int32) external returns (uint64) storage %temp.4 = function(int32) external returns (uint64)(function(int32) external returns (uint64)(struct { hex"42761137", (builtin GetAddress ()) }))
+        // CHECK: store storage slot(uint256 0) ty:function(int32) external returns (uint64) = %temp.4
         func = this.foo;
     }
 
@@ -16,16 +16,40 @@ contract ExternalFunctions {
 
     function bar(function(int32) external returns (uint64) f) public {
         assert(f(102) == 0xabbaabba);
+
+        // CHECK: ty:function(int32) external returns (uint64) %f = (arg #0)
+        // CHECK: ty:bytes %abi_encoded.temp.5 = (alloc bytes len uint32 8)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.5 offset:uint32 0 value:(load (struct (arg #0) field 0))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.5 offset:uint32 4 value:int32 102
+        // CHECK: _ = external call::regular address:(load (struct (arg #0) field 1)) payload:%abi_encoded.temp.5 value:uint128 0 gas:uint64 0 accounts: seeds:
+        // CHECK: ty:uint32 %temp.6 = (builtin ArrayLength ((external call return data)))
+        // CHECK: branchcond (unsigned uint32 8 <= %temp.6), block3, block4
+
+        // CHECK: block1: # noassert
+        // CHECK: return
+
+        // CHECK: block2: # doassert
+        // CHECK: assert-failure
+
+        // CHECK: block3: # inbounds
+        // CHECK: ty:uint64 %temp.7 = (builtin ReadFromBuffer ((external call return data), uint32 0))
+        // CHECK: branchcond (unsigned less uint32 8 < %temp.6), block5, block6
+
+        // CHECK: block4: # out_of_bounds
+        // CHECK: assert-failure
+
+        // CHECK: block5: # not_all_bytes_read
+        // CHECK: assert-failure
     }
 
     // BEGIN-CHECK: ExternalFunctions::ExternalFunctions::function::storage_callback
     function storage_callback() public {
-        // CHECK: %temp.6 = load storage slot(uint256 0) ty:function(int32) external returns (uint64)
-        // CHECK: ty:bytes %abi_encoded.temp.7 = (alloc bytes len uint32 40)
-        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 0 value:hex"f503f5fe"
-        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 4 value:(load (struct function(int32) external returns (uint64)(%temp.6) field 1))
-        // CHECK: writebuffer buffer:%abi_encoded.temp.7 offset:uint32 36 value:(load (struct function(int32) external returns (uint64)(%temp.6) field 0))
-        // CHECK: _ = external call::regular address:(builtin GetAddress ()) payload:%abi_encoded.temp.7 value:uint128 0 gas:uint64 0 accounts: seeds:
+        // CHECK: %temp.8 = load storage slot(uint256 0) ty:function(int32) external returns (uint64)
+        // CHECK: ty:bytes %abi_encoded.temp.9 = (alloc bytes len uint32 40)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:hex"f503f5fe"
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 4 value:(load (struct function(int32) external returns (uint64)(%temp.8) field 1))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 36 value:(load (struct function(int32) external returns (uint64)(%temp.8) field 0))
+        // CHECK: _ = external call::regular address:(builtin GetAddress ()) payload:%abi_encoded.temp.9 value:uint128 0 gas:uint64 0 accounts: seeds:
         this.bar(func);
     }
 }
@@ -33,25 +57,108 @@ contract ExternalFunctions {
 contract CompactEncoding {
     // BEGIN-CHECK: CompactEncoding::CompactEncoding::function::vector_length
     function vector_length(string memory s) public {
+        bytes memory enc = abi.encode(s);
+        abi.decode(enc, (string));
+
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 1073741823), block6, block7
+
         // CHECK: block1: # small
-        // CHECK: ty:uint32 %temp.8 = uint32 1
+        // CHECK: ty:uint32 %temp.10 = uint32 1
+        // CHECK: branch block5
+
         // CHECK: block2: # medium
-        // CHECK: ty:uint32 %temp.8 = uint32 2
+        // CHECK: ty:uint32 %temp.10 = uint32 2
+        // CHECK: branch block5
+
+        // CHECK: block3: # medium_or_big
+        // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 16383), block4, block2
+
         // CHECK: block4: # big
-        // CHECK: ty:uint32 %temp.8 = uint32 4
-        // CHECK: ty:bytes %abi_encoded.temp.9 = (alloc bytes len (%temp.8 + (builtin ArrayLength ((arg #0)))))
-        // CHECK: ty:uint32 %temp.10 = (builtin ArrayLength ((arg #0)))
-        // CHECK: branchcond (unsigned more %temp.10 > uint32 1073741823), block13, block14
+        // CHECK: ty:uint32 %temp.10 = uint32 4
+        // CHECK: branch block5
+
+        // CHECK: block5: # done
+        // CHECK: ty:bytes %abi_encoded.temp.11 = (alloc bytes len (%temp.10 + (builtin ArrayLength ((arg #0)))))
+        // CHECK: ty:uint32 %temp.12 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned more %temp.12 > uint32 1073741823), block13, block14
+
+        // CHECK: block6: # fail
+        // CHECK: assert-failure
+
+        // CHECK: block7: # prepare
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 63), block3, block1
-        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:uint8((%temp.10 * uint32 4))
-        // CHECK: ty:uint32 %temp.11 = uint32 1
-        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:uint16(((%temp.10 * uint32 4) | uint32 1))
-        // CHECK: ty:uint32 %temp.11 = uint32 2
-        // CHECK: writebuffer buffer:%abi_encoded.temp.9 offset:uint32 0 value:((%temp.10 * uint32 4) | uint32 2)
-        // CHECK: ty:uint32 %temp.11 = uint32 4
-        // CHECK: memcpy src: (arg #0), dest: (advance ptr: %abi_encoded.temp.9, by: (uint32 0 + %temp.11)), bytes_len: %temp.10
-        // CHECK: branchcond (unsigned more %temp.10 > uint32 63), block10, block8
-        abi.encode(s);
+
+        // CHECK: block8: # small
+        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:uint8((%temp.12 * uint32 4))
+        // CHECK: ty:uint32 %temp.13 = uint32 1
+        // CHECK: branch block12
+
+        // CHECK: block9: # medium
+        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:uint16(((%temp.12 * uint32 4) | uint32 1))
+        // CHECK: ty:uint32 %temp.13 = uint32 2
+        // CHECK: branch block12
+
+        // CHECK: block10: # medium_or_big
+        // CHECK: branchcond (unsigned more %temp.12 > uint32 16383), block11, block9
+
+        // CHECK: block11: # big
+        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:((%temp.12 * uint32 4) | uint32 2)
+        // CHECK: ty:uint32 %temp.13 = uint32 4
+        // CHECK: branch block12
+
+        // CHECK: block12: # done
+        // CHECK: memcpy src: (arg #0), dest: (advance ptr: %abi_encoded.temp.11, by: (uint32 0 + %temp.13)), bytes_len: %temp.12
+        // CHECK: ty:bytes %enc = %abi_encoded.temp.11
+        // CHECK: ty:uint32 %temp.14 = (builtin ArrayLength (%enc))
+        // CHECK: ty:uint32 %temp.16 = (zext uint32 (builtin ReadFromBuffer (%enc, uint32 0)))
+        // CHECK: switch (%temp.16 & uint32 3):
+        // CHECK:         case uint32 0: goto block #15
+        // CHECK:         case uint32 1: goto block #16
+        // CHECK:         case uint32 2: goto block #17
+        // CHECK:         default: goto block #18
+
+        // CHECK: block13: # fail
+        // CHECK: assert-failure
+
+        // CHECK: block14: # prepare
+        // CHECK: branchcond (unsigned more %temp.12 > uint32 63), block10, block8
+
+        // CHECK: block15: # case_0
+        // CHECK: ty:uint32 %temp.15 = (%temp.16 >> uint32 2)
+        // CHECK: ty:uint32 %temp.16 = uint32 1
+        // CHECK: branch block19
+
+        // CHECK: block16: # case_1
+        // CHECK: ty:uint32 %temp.15 = ((zext uint32 (builtin ReadFromBuffer (%enc, uint32 0))) >> uint32 2)
+        // CHECK: ty:uint32 %temp.16 = uint32 2
+        // CHECK: branch block19
+
+        // CHECK: block17: # case_2
+        // CHECK: ty:uint32 %temp.15 = ((builtin ReadFromBuffer (%enc, uint32 0)) >> uint32 2)
+        // CHECK: ty:uint32 %temp.16 = uint32 4
+        // CHECK: branch block19
+
+        // CHECK: block18: # case_default
+        // CHECK: assert-failure
+
+        // CHECK: block19: # done
+        // CHECK: branchcond (unsigned (uint32 0 + %temp.16) <= %temp.14), block20, block21
+
+        // CHECK: block20: # inbounds
+        // CHECK: branchcond (unsigned (uint32 0 + (%temp.15 + %temp.16)) <= %temp.14), block22, block23
+
+        // CHECK: block21: # out_of_bounds
+        // CHECK: assert-failure
+
+        // CHECK: block22: # inbounds
+        // CHECK: ty:string %temp.17 = (alloc string len %temp.15)
+        // CHECK: memcpy src: (advance ptr: %enc, by: (uint32 0 + %temp.16)), dest: %temp.17, bytes_len: %temp.15
+        // CHECK: branchcond (unsigned less (uint32 0 + (%temp.15 + %temp.16)) < %temp.14), block24, block25
+
+        // CHECK: block23: # out_of_bounds
+        // CHECK: assert-failure
+
+        // CHECK: block24: # not_all_bytes_read
+        // CHECK: assert-failure
     }
 }

+ 4 - 4
tests/codegen_testcases/solidity/unused_variable_elimination.sol

@@ -77,13 +77,13 @@ contract c3 {
         c2 ct = new c2();
 
         return 3;
-// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.79, buffer len: uint32 4)
+// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.80, buffer len: uint32 4)
     }
 
 // BEGIN-CHECK: c3::function::test7
     function test7() public returns (int32) {
         c2 ct = new c2();
-// constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.81, buffer len: uint32 4)
+// constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.82, buffer len: uint32 4)
         address ad = address(ct);
         (bool p, ) = ad.call(hex'ba');
 // CHECK: external call::regular address:%ad payload:(alloc bytes uint32 1 hex"ba") value:uint128 0 gas:uint64 0
@@ -136,7 +136,7 @@ return 3;
         int f = 4;
 
         int c = 32 +4 *(f = it1+it2);
-// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.88 + %temp.89)))))
+// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.89 + %temp.90)))))
 // NOT-CHECK: ty:int256 %f = (%temp.10 + %temp.11)
         return c;
     }
@@ -177,7 +177,7 @@ return 3;
     function test14() public returns (int) {
         int[] storage ptrArr = testArr;
 
-// CHECK: store storage slot(%temp.106) ty:int256 storage = int256 3
+// CHECK: store storage slot(%temp.109) ty:int256 storage = int256 3
         ptrArr.push(3);
 
         return ptrArr[0];

+ 68 - 0
tests/substrate_tests/arrays.rs

@@ -1512,3 +1512,71 @@ fn fixed_bytes() {
         assert_eq!(runtime.vm.output[..], [i]);
     }
 }
+
+#[test]
+fn abi_decode_dynamic_array2() {
+    let mut runtime = build_solidity(
+        r#"
+        contract c {
+            function decode() pure public {
+                bytes enc = hex"2c000000000300000006000000090000000c0000000f0000001200000015000000180000001b0000001e000000";
+                int32[] bar = abi.decode(enc, (int32[]));
+                assert(bar.length == 11);
+
+                for (int32 i = 0; i <11; i++) {
+                    assert(bar[uint32(i)] == i * 3);
+                }
+            }
+
+            function decode_empty() pure public {
+                bytes enc = hex"00";
+                int32[] bar = abi.decode(enc, (int32[]));
+                assert(bar.length == 0);
+            }
+        }
+        "#,
+    );
+
+    runtime.function("decode", vec![]);
+    runtime.function("decode_empty", vec![]);
+}
+
+#[test]
+fn abi_decode_dynamic_array3() {
+    let mut runtime = build_solidity(
+        r#"
+        contract Arr {
+            function decode() pure public {
+                bytes enc = hex"14140001020304140102030405140203040506140304050607140405060708";
+                uint8[][] bar = abi.decode(enc, (uint8[][]));
+                assert(bar.length == 5);
+        
+                for (uint8 i = 0; i <5; i++) {
+                        for (uint8 j = 0; j <5; j++) {
+                        assert(bar[uint32(i)][uint32(j)] == i + j);
+                                }
+                }
+            }
+        
+            function decode_empty() pure public {
+                bytes enc = hex"00";
+                uint8[][] bar = abi.decode(enc, (uint8[][]));
+                assert(bar.length == 0);
+            }
+        }
+        "#,
+    );
+
+    // The array in the first function was generated like this:
+    // let mut matrix = vec![];
+    // for i in 0..5 {
+    //     matrix.push(vec![]);
+    //     for j in 0..5 {
+    //         matrix[i].push((i + j) as u8);
+    //     }
+    // }
+
+    runtime.function("decode", vec![]);
+
+    runtime.function("decode_empty", vec![]);
+}

+ 46 - 0
tests/substrate_tests/function_types.rs

@@ -330,3 +330,49 @@ fn ext() {
     runtime.function("test1", Vec::new());
     runtime.function("test2", Vec::new());
 }
+
+/// Test external function decoding
+#[test]
+fn encode_decode_ext_func() {
+    let mut runtime = build_solidity(
+        r##"
+        contract A {
+            @selector([0,0,0,0])
+            function a() public pure returns (uint8) {
+                return 127;
+            }
+
+            function it() public view returns (address) {
+                return address(this);
+            }
+        }
+        
+        contract B {
+            function encode_decode_call(address a) public returns (uint8) {
+                bytes4 selector = hex"00000000";
+    	        bytes enc = abi.encode(a, selector);
+                function() external returns (uint8) dec2 = abi.decode(enc, (function() external returns (uint8)));
+                return dec2();
+            }
+
+            function decode_call(function() external returns(uint8) func) public returns (uint8) {
+                bytes enc = abi.encode(func);
+                print("{}  ".format(enc));
+                function() external returns (uint8) dec2 = abi.decode(enc, (function() external returns (uint8)));
+                return dec2();
+            }
+        }
+        "##,
+    );
+
+    runtime.function("it", vec![]);
+    let mut address_of_a = runtime.vm.output.clone();
+
+    runtime.set_program(1);
+    runtime.function("encode_decode_call", address_of_a.clone());
+    assert_eq!(runtime.vm.output, 127u8.encode());
+
+    address_of_a.extend([0, 0, 0, 0].iter());
+    runtime.function("decode_call", address_of_a);
+    assert_eq!(runtime.vm.output, 127u8.encode());
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 18 - 0
tests/substrate_tests/strings.rs


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác