use crate::{
    backend::riscv::from_ir::{
        function::FunctionCompileContext, register_assign::RegisterAssign, HasSize,
    },
    ir::{self, quantity::Quantity},
    utility::data_type::Type,
};
pub fn emit_code(set_field: &ir::statement::SetField, ctx: &mut FunctionCompileContext) -> String {
    let ir::statement::SetField {
        target,
        source,
        origin_root,
        field_chain,
        final_type,
    } = set_field;
    let mut code = String::new();
    let value_to_set = match source {
        Quantity::RegisterName(local) => ctx.local_assign.get(local).unwrap().clone(),
        Quantity::NumberLiteral(n) => {
            code.push_str(&format!("    li t1, {n}\n"));
            RegisterAssign::Register("t1".to_string())
        }
        Quantity::GlobalVariableName(_) => todo!(),
    };
    let value_to_be_setted = ctx.local_assign.get(origin_root).unwrap();
    let result_register = ctx.local_assign.get(target).unwrap();
    let mut current_offset = 0;
    let root_type_bytes = (field_chain[0].0.size(ctx.parent_context) + 7) / 8;
    for (field_parent_type, field_index) in field_chain {
        if let Type::StructRef(struct_name) = field_parent_type {
            let parent_type = ctx
                .parent_context
                .struct_definitions
                .get(struct_name)
                .unwrap();
            for calculating_field_index in 0..*field_index {
                current_offset +=
                    parent_type.fields[calculating_field_index].size(ctx.parent_context);
            }
        }
    }
    let current_offset_bytes = (current_offset + 7) / 8;
    let final_type_bytes = (final_type.size(ctx.parent_context) + 7) / 8;
    code.push_str(
        match (result_register, value_to_be_setted, value_to_set) {
            (RegisterAssign::Register(result), _, RegisterAssign::Register(to_set)) => {
                format!("    mv {result}, {to_set}\n")
            }
            (RegisterAssign::Register(result), _, RegisterAssign::StackValue(to_set)) => {
                format!("    lw {result}, {to_set}(sp)\n")
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::Register(value_to_set),
            ) => {
                let mut result_code = String::new();
                for i in 0..result.len() {
                    if i == current_offset_bytes / 4 {
                        result_code.push_str(&format!("    mv {}, {}\n", result[i], value_to_set));
                    } else {
                        result_code
                            .push_str(&format!("    mv {}, {}\n", result[i], to_be_setted[i]));
                    }
                }
                result_code
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::MultipleRegisters(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    result_code.push_str(&format!("    mv {}, {}\n", result[i], to_be_setted[i]));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    result_code.push_str(&format!(
                        "    mv {}, {}\n",
                        result[i],
                        value_to_set[i - current_offset_bytes / 4]
                    ));
                    i += 1;
                }
                while i < result.len() {
                    result_code.push_str(&format!("    mv {}, {}\n", result[i], to_be_setted[i]));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::StackValue(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    result_code.push_str(&format!("    mv {}, {}\n", result[i], to_be_setted[i]));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = value_to_set + (i - current_offset_bytes / 4) * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                while i < result.len() {
                    result_code.push_str(&format!("    mv {}, {}\n", result[i], to_be_setted[i]));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::Register(value_to_set),
            ) => {
                let mut result_code = String::new();
                for (i, result_register) in result.iter().enumerate() {
                    if i == current_offset_bytes / 4 {
                        result_code
                            .push_str(&format!("    mv {result_register}, {value_to_set}\n"));
                    } else {
                        let offset = to_be_setted + i * 4;
                        result_code.push_str(&format!("    lw {result_register}, {offset}(sp)\n"));
                    }
                }
                result_code
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::MultipleRegisters(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = to_be_setted + i * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let index = i - current_offset_bytes / 4;
                    result_code
                        .push_str(&format!("    mv {}, {}\n", result[i], value_to_set[index]));
                    i += 1;
                }
                while i < result.len() {
                    let offset = to_be_setted + i * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::MultipleRegisters(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::StackValue(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = to_be_setted + i * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = value_to_set + (i - current_offset_bytes / 4) * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                while i < result.len() {
                    let offset = to_be_setted + i * 4;
                    result_code.push_str(&format!("    lw {}, {}(sp)\n", result[i], offset));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::Register(_to_be_setted),
                RegisterAssign::Register(value_to_set),
            ) => format!("    sw {value_to_set}, {result}(sp)\n"),
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::Register(_to_be_setted),
                RegisterAssign::StackValue(value_to_set),
            ) => format!("    lw t0, {value_to_set}(sp)\n    sw t0, {result}(sp)\n"),
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::Register(value_to_set),
            ) => {
                let mut result_code = String::new();
                for (i, to_be_setted_register) in to_be_setted.iter().enumerate() {
                    let offset = result + i * 4;
                    if i == current_offset_bytes / 4 {
                        result_code.push_str(&format!("    sw {value_to_set}, {offset}(sp)\n"));
                    } else {
                        result_code
                            .push_str(&format!("    sw {to_be_setted_register}, {offset}(sp)\n"));
                    }
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::MultipleRegisters(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = result + i * 4;
                    result_code.push_str(&format!("    sw {}, {}(sp)\n", to_be_setted[i], offset));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = result + i * 4;
                    let index = i - current_offset_bytes / 4;
                    result_code
                        .push_str(&format!("    sw {}, {}(sp)\n", value_to_set[index], offset));
                    i += 1;
                }
                while i < to_be_setted.len() {
                    let offset = result + i * 4;
                    result_code.push_str(&format!("    sw {}, {}(sp)\n", to_be_setted[i], offset));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::MultipleRegisters(to_be_setted),
                RegisterAssign::StackValue(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    sw {}, {}(sp)\n",
                        to_be_setted[i],
                        result + offset
                    ));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = i * 4;
                    let value_to_set_offset = (i - current_offset_bytes / 4) * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        value_to_set + value_to_set_offset,
                        result + offset
                    ));
                    i += 1;
                }
                while i < to_be_setted.len() {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    sw {}, {}(sp)\n",
                        to_be_setted[i],
                        result + offset
                    ));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::Register(value_to_set),
            ) => {
                let mut result_code = String::new();
                for i in 0..current_offset_bytes / 4 {
                    let offset = i * 4;
                    if i == current_offset_bytes / 4 {
                        result_code.push_str(&format!(
                            "    sw {}, {}(sp)\n",
                            value_to_set,
                            result + offset
                        ));
                    } else {
                        result_code.push_str(&format!(
                            "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                            to_be_setted + offset,
                            result + offset
                        ));
                    }
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::MultipleRegisters(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        to_be_setted + offset,
                        result + offset
                    ));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = i * 4;
                    let index = i - current_offset_bytes / 4;
                    result_code.push_str(&format!(
                        "    sw {}, {}(sp)\n",
                        value_to_set[index],
                        result + offset
                    ));
                    i += 1;
                }
                while i < root_type_bytes / 4 {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        to_be_setted + offset,
                        result + offset
                    ));
                    i += 1;
                }
                result_code
            }
            (
                RegisterAssign::StackValue(result),
                RegisterAssign::StackValue(to_be_setted),
                RegisterAssign::StackValue(value_to_set),
            ) => {
                let mut result_code = String::new();
                let mut i = 0;
                while i < current_offset_bytes / 4 {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        to_be_setted + offset,
                        result + offset
                    ));
                    i += 1;
                }
                while i < current_offset_bytes / 4 + final_type_bytes / 4 {
                    let offset = i * 4;
                    let value_to_set_offset = (i - current_offset_bytes / 4) * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        value_to_set + value_to_set_offset,
                        result + offset
                    ));
                    i += 1;
                }
                while i < root_type_bytes / 4 {
                    let offset = i * 4;
                    result_code.push_str(&format!(
                        "    lw t0, {}(sp)\n    sw t0, {}(sp)\n",
                        to_be_setted + offset,
                        result + offset
                    ));
                    i += 1;
                }
                result_code
            }
            (_, RegisterAssign::StackRef(_), _) => todo!(),
            (_, _, RegisterAssign::StackRef(_)) => todo!(),
            (_, RegisterAssign::Register(_), RegisterAssign::MultipleRegisters(_)) => {
                unreachable!()
            }
            (RegisterAssign::MultipleRegisters(_), RegisterAssign::Register(_), _) => {
                unreachable!()
            }
            (RegisterAssign::Register(_), RegisterAssign::MultipleRegisters(_), _) => {
                unreachable!()
            }
            (RegisterAssign::Register(_), _, RegisterAssign::MultipleRegisters(_)) => {
                unreachable!()
            }
            (RegisterAssign::StackRef(_), _, _) => unreachable!(),
        }
        .as_str(),
    );
    code
}
#[cfg(test)]
mod tests {
    #![allow(clippy::borrow_interior_mutable_const)]
    use crate::{backend::riscv::from_ir::Context, ir::RegisterName, utility::data_type};
    use super::*;
    use std::collections::HashMap;
    #[test]
    fn emit_code_reg_whatever_reg() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S0".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone()],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::Register("t2".to_string()),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::Register("t3".to_string()),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: 42.into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S0".to_string()), 0)],
            final_type: data_type::I32.clone(),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(code, "    li t1, 42\n    mv t2, t1\n");
    }
    #[test]
    fn emit_code_reg_whatever_mem() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S0".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone()],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::Register("t2".to_string()),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::Register("t3".to_string()),
        );
        ctx.local_assign.insert(
            RegisterName("c".to_string()),
            RegisterAssign::StackValue(16),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: RegisterName("c".to_string()).into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S0".to_string()), 0)],
            final_type: data_type::I32.clone(),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(code, "    lw t2, 16(sp)\n");
    }
    #[test]
    fn emit_code_multi_multi_reg() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S1".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone(), data_type::I32.clone()],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::MultipleRegisters(vec!["t2".to_string(), "t3".to_string()]),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::MultipleRegisters(vec!["t4".to_string(), "t5".to_string()]),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: 42.into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S1".to_string()), 0)],
            final_type: data_type::I32.clone(),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(code, "    li t1, 42\n    mv t2, t1\n    mv t3, t5\n");
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: 42.into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S1".to_string()), 1)],
            final_type: data_type::I32.clone(),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(code, "    li t1, 42\n    mv t2, t4\n    mv t3, t1\n");
    }
    #[test]
    fn emit_code_multi_multi_multi() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S1".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone(), data_type::I32.clone()],
            },
        );
        ctx.struct_definitions.insert(
            "S2".to_string(),
            ir::TypeDefinition {
                name: "S2".to_string(),
                fields: vec![
                    data_type::I32.clone(),
                    Type::StructRef("S1".to_string()),
                    data_type::I32.clone(),
                ],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::MultipleRegisters(vec![
                "a0".to_string(),
                "a1".to_string(),
                "a2".to_string(),
                "a3".to_string(),
            ]),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::MultipleRegisters(vec!["t2".to_string(), "t3".to_string()]),
        );
        ctx.local_assign.insert(
            RegisterName("c".to_string()),
            RegisterAssign::MultipleRegisters(vec![
                "a4".to_string(),
                "a5".to_string(),
                "a6".to_string(),
                "a7".to_string(),
            ]),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("c".to_string()),
            source: RegisterName("b".to_string()).into(),
            origin_root: RegisterName("a".to_string()),
            field_chain: vec![(Type::StructRef("S2".to_string()), 1)],
            final_type: Type::StructRef("S1".to_string()),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(
            code,
            "    mv a4, a0\n    mv a5, t2\n    mv a6, t3\n    mv a7, a3\n"
        );
    }
    #[test]
    fn emit_code_multi_multi_mem() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S1".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone(), data_type::I32.clone()],
            },
        );
        ctx.struct_definitions.insert(
            "S2".to_string(),
            ir::TypeDefinition {
                name: "S2".to_string(),
                fields: vec![
                    data_type::I32.clone(),
                    Type::StructRef("S1".to_string()),
                    data_type::I32.clone(),
                ],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::MultipleRegisters(vec![
                "a0".to_string(),
                "a1".to_string(),
                "a2".to_string(),
                "a3".to_string(),
            ]),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::StackValue(16),
        );
        ctx.local_assign.insert(
            RegisterName("c".to_string()),
            RegisterAssign::MultipleRegisters(vec![
                "a4".to_string(),
                "a5".to_string(),
                "a6".to_string(),
                "a7".to_string(),
            ]),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("c".to_string()),
            source: RegisterName("b".to_string()).into(),
            origin_root: RegisterName("a".to_string()),
            field_chain: vec![(Type::StructRef("S2".to_string()), 1)],
            final_type: Type::StructRef("S1".to_string()),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(
            code,
            "    mv a4, a0\n    lw a5, 16(sp)\n    lw a6, 20(sp)\n    mv a7, a3\n"
        );
    }
    #[test]
    fn emit_code_multi_mem_reg() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S1".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone(), data_type::I32.clone()],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::MultipleRegisters(vec!["a0".to_string(), "a1".to_string()]),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::StackValue(16),
        );
        ctx.local_assign.insert(
            RegisterName("c".to_string()),
            RegisterAssign::Register("t2".to_string()),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: RegisterName("c".to_string()).into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S1".to_string()), 1)],
            final_type: data_type::I32.clone(),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(code, "    lw a0, 16(sp)\n    mv a1, t2\n");
    }
    #[test]
    fn emit_code_multi_mem_multi() {
        let mut ctx = Context {
            struct_definitions: HashMap::new(),
        };
        ctx.struct_definitions.insert(
            "S1".to_string(),
            ir::TypeDefinition {
                name: "S1".to_string(),
                fields: vec![data_type::I32.clone(), data_type::I32.clone()],
            },
        );
        ctx.struct_definitions.insert(
            "S2".to_string(),
            ir::TypeDefinition {
                name: "S2".to_string(),
                fields: vec![
                    data_type::I32.clone(),
                    Type::StructRef("S1".to_string()),
                    data_type::I32.clone(),
                ],
            },
        );
        let mut ctx = FunctionCompileContext {
            parent_context: &mut ctx,
            local_assign: HashMap::new(),
            cleanup_label: None,
            phi_constant_assign: HashMap::new(),
        };
        ctx.local_assign.insert(
            RegisterName("a".to_string()),
            RegisterAssign::MultipleRegisters(vec![
                "a0".to_string(),
                "a1".to_string(),
                "a2".to_string(),
                "a3".to_string(),
            ]),
        );
        ctx.local_assign.insert(
            RegisterName("b".to_string()),
            RegisterAssign::StackValue(16),
        );
        ctx.local_assign.insert(
            RegisterName("c".to_string()),
            RegisterAssign::MultipleRegisters(vec!["t2".to_string(), "t3".to_string()]),
        );
        let set_field = ir::statement::SetField {
            target: RegisterName("a".to_string()),
            source: RegisterName("c".to_string()).into(),
            origin_root: RegisterName("b".to_string()),
            field_chain: vec![(Type::StructRef("S2".to_string()), 1)],
            final_type: Type::StructRef("S1".to_string()),
        };
        let code = emit_code(&set_field, &mut ctx);
        assert_eq!(
            code,
            "    lw a0, 16(sp)\n    mv a1, t2\n    mv a2, t3\n    lw a3, 28(sp)\n"
        );
        }
}