diff --git a/Cargo.lock b/Cargo.lock index 5622c9407ec..8bb0cb6b9e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2945,6 +2945,7 @@ dependencies = [ "nanoid", "petgraph", "prettydiff 0.5.1", + "regex", "serde", "sha2", "smallvec", diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index b32aea8d12d..abd72f7e1d9 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -24,6 +24,7 @@ pest = { version = "3.0.4", package = "fuel-pest" } pest_derive = { version = "3.0.4", package = "fuel-pest_derive" } petgraph = "0.5" prettydiff = "0.5" +regex = "1" serde = { version = "1.0", features = ["derive"] } sha2 = "0.9" smallvec = "1.7" diff --git a/sway-core/src/asm_generation/from_ir.rs b/sway-core/src/asm_generation/from_ir.rs index 3dc963e3d7b..7e07475dd78 100644 --- a/sway-core/src/asm_generation/from_ir.rs +++ b/sway-core/src/asm_generation/from_ir.rs @@ -22,6 +22,7 @@ use crate::{ }; use sway_ir::*; +use sway_types::span::Span; use either::Either; @@ -197,13 +198,6 @@ pub(super) enum Storage { } impl<'ir> AsmBuilder<'ir> { - fn empty_span() -> crate::span::Span { - crate::span::Span { - span: pest::Span::new(" ".into(), 0, 0).unwrap(), - path: None, - } - } - fn new(data_section: DataSection, reg_seqr: RegisterSequencer, context: &'ir Context) -> Self { AsmBuilder { data_section, @@ -218,6 +212,19 @@ impl<'ir> AsmBuilder<'ir> { } } + // This is here temporarily for in the case when the IR can't absolutely provide a valid span, + // until we can improve ASM block parsing and verification mostly. It's where it's needed the + // most, for returning failure errors. If we move ASM verification to the parser and semantic + // analysis then ASM block conversion shouldn't/can't fail and we won't need to provide a + // guaranteed to be available span. + fn empty_span() -> Span { + let msg = "unknown source location"; + Span { + span: pest::Span::new(std::sync::Arc::from(msg), 0, msg.len()).unwrap(), + path: None, + } + } + fn add_locals(&mut self, function: Function) { // If they're immutable and have a constant initialiser then they go in the data section. // Otherwise they go in runtime allocated space, either a register or on the stack. @@ -291,9 +298,12 @@ impl<'ir> AsmBuilder<'ir> { VirtualRegister::Constant(ConstantRegister::StackPointer), "save locals base register", )); - let mut alloc_op = Op::unowned_stack_allocate_memory( - VirtualImmediate24::new(stack_base * 8, Self::empty_span()).unwrap(), - ); + if stack_base * 8 > crate::asm_generation::compiler_constants::TWENTY_FOUR_BITS { + todo!("Enormous stack usage for locals."); + } + let mut alloc_op = Op::unowned_stack_allocate_memory(VirtualImmediate24 { + value: (stack_base * 8) as u32, + }); alloc_op.comment = format!("allocate {} bytes for all locals", stack_base * 8); self.bytecode.push(alloc_op); self.stack_base_reg = Some(base_reg); @@ -303,19 +313,18 @@ impl<'ir> AsmBuilder<'ir> { fn add_block_label(&mut self, block: Block) { if &block.get_label(self.context) != "entry" { let label = self.block_to_label(&block); - self.bytecode - .push(Op::jump_label(label, Self::empty_span())) + self.bytecode.push(Op::unowned_jump_label(label)) } } fn add_label(&mut self) -> Label { let label = self.reg_seqr.get_label(); - self.bytecode - .push(Op::jump_label(label.clone(), Self::empty_span())); + self.bytecode.push(Op::unowned_jump_label(label.clone())); label } fn finalize(self) -> CompileResult<(DataSection, Vec, RegisterSequencer)> { + // XXX Assuming no warnings... ok( (self.data_section, self.bytecode, self.reg_seqr), Vec::new(), @@ -325,9 +334,10 @@ impl<'ir> AsmBuilder<'ir> { fn compile_function(&mut self, function: Function) -> CompileResult<()> { // Compile instructions. + self.add_locals(function); + let mut warnings = Vec::new(); let mut errors = Vec::new(); - self.add_locals(function); for block in function.block_iter(self.context) { self.add_block_label(block); for instr_val in block.instruction_iter(self.context) { @@ -345,7 +355,7 @@ impl<'ir> AsmBuilder<'ir> { fn compile_instruction(&mut self, block: &Block, instr_val: &Value) -> CompileResult<()> { let mut warnings = Vec::new(); let mut errors = Vec::new(); - if let ValueContent::Instruction(instruction) = &self.context.values[instr_val.0] { + if let ValueDatum::Instruction(instruction) = &self.context.values[instr_val.0].value { match instruction { Instruction::AsmBlock(asm, args) => { check!( @@ -359,7 +369,9 @@ impl<'ir> AsmBuilder<'ir> { Instruction::Call(..) => { errors.push(CompileError::Internal( "Calls are not yet supported.", - Self::empty_span(), + instr_val + .get_span(self.context) + .unwrap_or_else(Self::empty_span), )); return err(warnings, errors); } @@ -393,13 +405,17 @@ impl<'ir> AsmBuilder<'ir> { } => self.compile_insert_value(instr_val, aggregate, ty, value, indices), Instruction::Load(ptr) => self.compile_load(instr_val, ptr), Instruction::Phi(_) => (), // Managing the phi value is done in br and cbr compilation. - Instruction::Ret(ret_val, ty) => self.compile_ret(ret_val, ty), - Instruction::Store { ptr, stored_val } => self.compile_store(ptr, stored_val), + Instruction::Ret(ret_val, ty) => self.compile_ret(instr_val, ret_val, ty), + Instruction::Store { ptr, stored_val } => { + self.compile_store(instr_val, ptr, stored_val) + } } } else { errors.push(CompileError::Internal( "Value not an instruction.", - Self::empty_span(), + instr_val + .get_span(self.context) + .unwrap_or_else(Self::empty_span), )); } ok((), warnings, errors) @@ -424,7 +440,7 @@ impl<'ir> AsmBuilder<'ir> { assert_or_warn!( ConstantRegister::parse_register_name(name.as_str()).is_none(), warnings, - Self::empty_span(), + name.span().clone(), Warning::ShadowingReservedRegister { reg_name: name.clone() } @@ -451,7 +467,7 @@ impl<'ir> AsmBuilder<'ir> { .map(|reg_name| -> Result<_, CompileError> { realize_register(reg_name.as_str()).ok_or_else(|| { CompileError::UnknownRegister { - span: Self::empty_span(), + span: reg_name.span().clone(), initialized_registers: inline_reg_map .iter() .map(|(name, _)| *name) @@ -470,12 +486,35 @@ impl<'ir> AsmBuilder<'ir> { .collect::>(); // Parse the actual op and registers. + let op_span = match op.span_md_idx { + None => { + // XXX This sucks. We have two options: not needing a span to parse the opcode + // (which is used for the error) or force a span from the IR somehow, maybe by + // using a .ir file? OK, we have a third and best option: do the parsing of + // asm blocks in the parser itself, so we can verify them all the way back then + // and not have to worry about them being malformed all the way down here in + // codegen. + Self::empty_span() + } + Some(span_md_idx) => match span_md_idx.to_span(self.context) { + Ok(span) => span, + Err(msg) => { + errors.push(CompileError::InternalOwned( + msg, + instr_val + .get_span(self.context) + .unwrap_or_else(Self::empty_span), + )); + return err(warnings, errors); + } + }, + }; let opcode = check!( Op::parse_opcode( &op.name, &replaced_registers, &op.immediate, - Self::empty_span(), // Whole op span. + op_span.clone(), ), return err(warnings, errors), warnings, @@ -485,7 +524,7 @@ impl<'ir> AsmBuilder<'ir> { inline_ops.push(Op { opcode: either::Either::Left(opcode), comment: "asm block".into(), - owning_span: None, + owning_span: Some(op_span), }); } @@ -497,22 +536,22 @@ impl<'ir> AsmBuilder<'ir> { Some(reg) => reg, None => { errors.push(CompileError::UnknownRegister { - span: Self::empty_span(), initialized_registers: inline_reg_map .iter() .map(|(name, _)| name.to_string()) .collect::>() .join("\n"), + span: ret_reg_name.span().clone(), }); return err(warnings, errors); } }; let instr_reg = self.reg_seqr.next(); - inline_ops.push(Op::unowned_register_move_comment( - instr_reg.clone(), - ret_reg, - "return value from inline asm", - )); + inline_ops.push(Op { + opcode: Either::Left(VirtualOp::MOVE(instr_reg.clone(), ret_reg)), + comment: "return value from inline asm".into(), + owning_span: instr_val.get_span(self.context), + }); self.reg_map.insert(*instr_val, instr_reg); } @@ -556,7 +595,7 @@ impl<'ir> AsmBuilder<'ir> { let local_reg = self.value_to_register(&local_val); let phi_reg = self.value_to_register(&to_block.get_phi(self.context)); self.bytecode - .push(Op::register_move(phi_reg, local_reg, Self::empty_span())); + .push(Op::unowned_register_move(phi_reg, local_reg)); } } @@ -586,13 +625,13 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 8 }, )), comment: "extract_element relative offset".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); let elem_offs_reg = self.reg_seqr.next(); self.bytecode.push(Op { opcode: Either::Left(VirtualOp::ADD(elem_offs_reg.clone(), base_reg, index_reg)), comment: "extract_element absolute offset".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); self.bytecode.push(Op { opcode: Either::Left(VirtualOp::LW( @@ -601,19 +640,38 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 0 }, )), comment: "extract_element".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); } else { // Value too big for a register, so we return the memory offset. - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::MULI( - instr_reg.clone(), - index_reg, - VirtualImmediate12::new(elem_size, Self::empty_span()).unwrap(), - )), - comment: "extract_element relative offset".into(), - owning_span: None, - }); + if elem_size > crate::asm_generation::compiler_constants::TWELVE_BITS { + let size_data_id = self + .data_section + .insert_data_value(&Literal::U64(elem_size)); + let size_reg = self.reg_seqr.next(); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LWDataId(size_reg.clone(), size_data_id)), + owning_span: instr_val.get_span(self.context), + comment: "loading element size for relative offset".into(), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MUL(instr_reg.clone(), index_reg, size_reg)), + comment: "extract_element relative offset".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MULI( + instr_reg.clone(), + index_reg, + VirtualImmediate12 { + value: elem_size as u16, + }, + )), + comment: "extract_element relative offset".into(), + owning_span: instr_val.get_span(self.context), + }); + } self.bytecode.push(Op { opcode: Either::Left(VirtualOp::ADD( instr_reg.clone(), @@ -621,7 +679,7 @@ impl<'ir> AsmBuilder<'ir> { instr_reg.clone(), )), comment: "extract_element absolute offset".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); } @@ -641,33 +699,89 @@ impl<'ir> AsmBuilder<'ir> { let instr_reg = self.reg_seqr.next(); if value_size <= 8 { - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::LW( - instr_reg.clone(), - base_reg, - VirtualImmediate12::new(extract_offset, Self::empty_span()).unwrap(), - )), - comment: format!( - "extract_value @ {}", - indices - .iter() - .map(|idx| format!("{}", idx)) - .collect::>() - .join(",") - ), - owning_span: None, - }); + if extract_offset > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offset_reg = self.reg_seqr.next(); + self.number_to_reg( + extract_offset, + &offset_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::ADD( + offset_reg.clone(), + base_reg.clone(), + base_reg, + )), + comment: "add array base to offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LW( + instr_reg.clone(), + offset_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: format!( + "extract_value @ {}", + indices + .iter() + .map(|idx| format!("{}", idx)) + .collect::>() + .join(",") + ), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LW( + instr_reg.clone(), + base_reg, + VirtualImmediate12 { + value: extract_offset as u16, + }, + )), + comment: format!( + "extract_value @ {}", + indices + .iter() + .map(|idx| format!("{}", idx)) + .collect::>() + .join(",") + ), + owning_span: instr_val.get_span(self.context), + }); + } } else { // Value too big for a register, so we return the memory offset. - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - instr_reg.clone(), - base_reg, - VirtualImmediate12::new(extract_offset * 8, Self::empty_span()).unwrap(), - )), - comment: "extract address".into(), - owning_span: None, - }); + if extract_offset * 8 > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offset_reg = self.reg_seqr.next(); + self.number_to_reg( + extract_offset * 8, + &offset_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADD( + instr_reg.clone(), + base_reg, + offset_reg, + )), + comment: "extract address".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + instr_reg.clone(), + base_reg, + VirtualImmediate12 { + value: (extract_offset * 8) as u16, + }, + )), + comment: "extract address".into(), + owning_span: instr_val.get_span(self.context), + }); + } } self.reg_map.insert(*instr_val, instr_reg); @@ -677,25 +791,32 @@ impl<'ir> AsmBuilder<'ir> { // `get_ptr` is like a `load` except the value isn't dereferenced. match self.ptr_map.get(ptr) { None => unimplemented!("BUG? Uninitialised pointer."), - Some(storage) => match storage { + Some(storage) => match storage.clone() { Storage::Data(_data_id) => { // Not sure if we'll ever need this. unimplemented!("TODO get_ptr() into the data section."); } Storage::Register(var_reg) => { - self.reg_map.insert(*instr_val, var_reg.clone()); + self.reg_map.insert(*instr_val, var_reg); } Storage::Stack(word_offs) => { + let word_offs = word_offs * 8; let instr_reg = self.reg_seqr.next(); - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - instr_reg.clone(), - self.stack_base_reg.as_ref().unwrap().clone(), - VirtualImmediate12::new(*word_offs * 8, Self::empty_span()).unwrap(), - )), - comment: "get_ptr".into(), - owning_span: None, - }); + if word_offs > crate::asm_generation::compiler_constants::TWELVE_BITS { + self.number_to_reg(word_offs, &instr_reg, instr_val.get_span(self.context)); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + instr_reg.clone(), + self.stack_base_reg.as_ref().unwrap().clone(), + VirtualImmediate12 { + value: (word_offs) as u16, + }, + )), + comment: "get_ptr".into(), + owning_span: instr_val.get_span(self.context), + }); + } self.reg_map.insert(*instr_val, instr_reg); } }, @@ -726,7 +847,7 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 8 }, )), comment: "insert_element relative offset".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); let elem_offs_reg = self.reg_seqr.next(); self.bytecode.push(Op { @@ -736,7 +857,7 @@ impl<'ir> AsmBuilder<'ir> { index_reg, )), comment: "insert_element absolute offset".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); self.bytecode.push(Op { opcode: Either::Left(VirtualOp::SW( @@ -745,38 +866,46 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 0 }, )), comment: "insert_element".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); } else { // Element size is larger than 8; we switch to bytewise offsets and sizes and use MCP. - let elem_index_offs_reg = self.reg_seqr.next(); - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::MULI( - elem_index_offs_reg.clone(), - index_reg, - VirtualImmediate12::new(elem_size, Self::empty_span()).unwrap(), - )), - comment: "insert_element relative offset".into(), - owning_span: None, - }); - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::ADD( - elem_index_offs_reg.clone(), - base_reg.clone(), - elem_index_offs_reg.clone(), - )), - comment: "insert_element absolute offset".into(), - owning_span: None, - }); - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::MCPI( - elem_index_offs_reg, - insert_reg, - VirtualImmediate12::new(elem_size, Self::empty_span()).unwrap(), - )), - comment: "insert_element store value".into(), - owning_span: None, - }); + if elem_size > crate::asm_generation::compiler_constants::TWELVE_BITS { + todo!("array element size bigger than 4k") + } else { + let elem_index_offs_reg = self.reg_seqr.next(); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MULI( + elem_index_offs_reg.clone(), + index_reg, + VirtualImmediate12 { + value: elem_size as u16, + }, + )), + comment: "insert_element relative offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::ADD( + elem_index_offs_reg.clone(), + base_reg.clone(), + elem_index_offs_reg.clone(), + )), + comment: "insert_element absolute offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MCPI( + elem_index_offs_reg, + insert_reg, + VirtualImmediate12 { + value: elem_size as u16, + }, + )), + comment: "insert_element store value".into(), + owning_span: instr_val.get_span(self.context), + }); + } } // We set the 'instruction' register to the base register, so that cascading inserts will @@ -804,35 +933,82 @@ impl<'ir> AsmBuilder<'ir> { .collect::>() .join(","); if value_size <= 8 { - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::SW( - base_reg.clone(), - insert_reg, - VirtualImmediate12::new(insert_offs, Self::empty_span()).unwrap(), - )), - comment: format!("insert_value @ {}", indices_str), - owning_span: None, - }); + if insert_offs > crate::asm_generation::compiler_constants::TWELVE_BITS { + let insert_offs_reg = self.reg_seqr.next(); + self.number_to_reg( + insert_offs, + &insert_offs_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::ADD( + base_reg.clone(), + base_reg.clone(), + insert_offs_reg, + )), + comment: "insert_value absolute offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + base_reg.clone(), + insert_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: format!("insert_value @ {}", indices_str), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + base_reg.clone(), + insert_reg, + VirtualImmediate12 { + value: insert_offs as u16, + }, + )), + comment: format!("insert_value @ {}", indices_str), + owning_span: instr_val.get_span(self.context), + }); + } } else { let offs_reg = self.reg_seqr.next(); - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - offs_reg.clone(), - base_reg.clone(), - VirtualImmediate12::new(insert_offs * 8, Self::empty_span()).unwrap(), - )), - comment: format!("get struct field(s) {} offset", indices_str), - owning_span: None, - }); - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::MCPI( - offs_reg, - insert_reg, - VirtualImmediate12::new(value_size, Self::empty_span()).unwrap(), - )), - comment: "store struct field value".into(), - owning_span: None, - }); + if insert_offs * 8 > crate::asm_generation::compiler_constants::TWELVE_BITS { + self.number_to_reg(insert_offs * 8, &offs_reg, instr_val.get_span(self.context)); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + offs_reg.clone(), + base_reg.clone(), + VirtualImmediate12 { + value: (insert_offs * 8) as u16, + }, + )), + comment: format!("get struct field(s) {} offset", indices_str), + owning_span: instr_val.get_span(self.context), + }); + } + if value_size > crate::asm_generation::compiler_constants::TWELVE_BITS { + let size_reg = self.reg_seqr.next(); + self.number_to_reg(value_size, &size_reg, instr_val.get_span(self.context)); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MCP(offs_reg, insert_reg, size_reg)), + comment: "store struct field value".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MCPI( + offs_reg, + insert_reg, + VirtualImmediate12 { + value: value_size as u16, + }, + )), + comment: "store struct field value".into(), + owning_span: instr_val.get_span(self.context), + }); + } } // We set the 'instruction' register to the base register, so that cascading inserts will @@ -846,46 +1022,92 @@ impl<'ir> AsmBuilder<'ir> { let instr_reg = self.reg_seqr.next(); match self.ptr_map.get(ptr) { None => unimplemented!("BUG? Uninitialised pointer."), - Some(storage) => match storage { + Some(storage) => match storage.clone() { Storage::Data(data_id) => { - self.bytecode.push(Op::unowned_load_data_comment( - instr_reg.clone(), - data_id.clone(), - "load constant", - )); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LWDataId(instr_reg.clone(), data_id)), + comment: "load constant".into(), + owning_span: instr_val.get_span(self.context), + }); } Storage::Register(var_reg) => { - self.bytecode.push(Op::register_move( - instr_reg.clone(), - var_reg.clone(), - Self::empty_span(), - )); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MOVE(instr_reg.clone(), var_reg)), + comment: String::new(), + owning_span: instr_val.get_span(self.context), + }); } Storage::Stack(word_offs) => { + let base_reg = self.stack_base_reg.as_ref().unwrap().clone(); // XXX Need to check for zero sized types? if load_size_in_words == 1 { // Value can fit in a register, so we load the value. - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::LW( - instr_reg.clone(), - self.stack_base_reg.as_ref().unwrap().clone(), - VirtualImmediate12::new(*word_offs, Self::empty_span()).unwrap(), - )), - comment: "load value".into(), - owning_span: None, - }); + if word_offs > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offs_reg = self.reg_seqr.next(); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::ADD( + base_reg.clone(), + base_reg, + offs_reg.clone(), + )), + comment: "insert_value absolute offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LW( + instr_reg.clone(), + offs_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "load value".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::LW( + instr_reg.clone(), + base_reg, + VirtualImmediate12 { + value: word_offs as u16, + }, + )), + comment: "load value".into(), + owning_span: instr_val.get_span(self.context), + }); + } } else { // Value too big for a register, so we return the memory offset. This is // what LW to the data section does, via LWDataId. - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - instr_reg.clone(), - self.stack_base_reg.as_ref().unwrap().clone(), - VirtualImmediate12::new(word_offs * 8, Self::empty_span()).unwrap(), - )), - comment: "load address".into(), - owning_span: None, - }); + let word_offs = word_offs * 8; + if word_offs > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offs_reg = self.reg_seqr.next(); + self.number_to_reg( + word_offs, + &offs_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADD( + instr_reg.clone(), + base_reg, + offs_reg, + )), + comment: "load address".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + instr_reg.clone(), + base_reg, + VirtualImmediate12 { + value: word_offs as u16, + }, + )), + comment: "load address".into(), + owning_span: instr_val.get_span(self.context), + }); + } } } }, @@ -893,8 +1115,7 @@ impl<'ir> AsmBuilder<'ir> { self.reg_map.insert(*instr_val, instr_reg); } - // XXX This is copied from ret_or_retd_value() above, streamlined for IR types. - fn compile_ret(&mut self, ret_val: &Value, ret_type: &Type) { + fn compile_ret(&mut self, instr_val: &Value, ret_val: &Value, ret_type: &Type) { if ret_type == &Type::Unit { // Unit returns should always be zero, although because they can be omitted from // functions, the register is sometimes uninitialized. Manually return zero in this @@ -903,7 +1124,7 @@ impl<'ir> AsmBuilder<'ir> { opcode: Either::Left(VirtualOp::RET(VirtualRegister::Constant( ConstantRegister::Zero, ))), - owning_span: None, + owning_span: instr_val.get_span(self.context), comment: "returning unit as zero".into(), }); } else { @@ -912,7 +1133,7 @@ impl<'ir> AsmBuilder<'ir> { if size_in_bytes <= 8 { self.bytecode.push(Op { - owning_span: None, + owning_span: instr_val.get_span(self.context), opcode: Either::Left(VirtualOp::RET(ret_reg)), comment: "".into(), }); @@ -927,11 +1148,11 @@ impl<'ir> AsmBuilder<'ir> { self.bytecode.push(Op { opcode: Either::Left(VirtualOp::LWDataId(size_reg.clone(), size_data_id)), - owning_span: None, + owning_span: instr_val.get_span(self.context), comment: "loading size for RETD".into(), }); self.bytecode.push(Op { - owning_span: None, + owning_span: instr_val.get_span(self.context), opcode: Either::Left(VirtualOp::RETD(ret_reg, size_reg)), comment: "".into(), }); @@ -939,7 +1160,7 @@ impl<'ir> AsmBuilder<'ir> { } } - fn compile_store(&mut self, ptr: &Pointer, stored_val: &Value) { + fn compile_store(&mut self, instr_val: &Value, ptr: &Pointer, stored_val: &Value) { let stored_reg = self.value_to_register(stored_val); let is_struct_ptr = ptr.is_struct_ptr(self.context); match self.ptr_map.get(ptr) { @@ -947,11 +1168,11 @@ impl<'ir> AsmBuilder<'ir> { Some(storage) => match storage { Storage::Data(_) => unreachable!("BUG! Trying to store to the data section."), Storage::Register(reg) => { - self.bytecode.push(Op::register_move( - reg.clone(), - stored_reg, - Self::empty_span(), - )); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MOVE(reg.clone(), stored_reg)), + comment: String::new(), + owning_span: instr_val.get_span(self.context), + }); } Storage::Stack(word_offs) => { let word_offs = *word_offs; @@ -962,6 +1183,8 @@ impl<'ir> AsmBuilder<'ir> { // We can have empty sized types which we can ignore. 0 => (), 1 => { + let base_reg = self.stack_base_reg.as_ref().unwrap().clone(); + // A single word can be stored with SW. let stored_reg = if !is_struct_ptr { // stored_reg is a value. @@ -976,47 +1199,116 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 0 }, )), comment: "load for store".into(), - owning_span: None, + owning_span: instr_val.get_span(self.context), }); tmp_reg }; - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::SW( - self.stack_base_reg.as_ref().unwrap().clone(), - stored_reg, - VirtualImmediate12::new(word_offs, Self::empty_span()).unwrap(), - )), - comment: "store value".into(), - owning_span: None, - }); + if word_offs > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offs_reg = self.reg_seqr.next(); + self.number_to_reg( + word_offs, + &offs_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::ADD( + base_reg.clone(), + base_reg, + offs_reg.clone(), + )), + comment: "store absolute offset".into(), + owning_span: instr_val.get_span(self.context), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + offs_reg, + stored_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "store value".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + base_reg, + stored_reg, + VirtualImmediate12 { + value: word_offs as u16, + }, + )), + comment: "store value".into(), + owning_span: instr_val.get_span(self.context), + }); + } } _ => { + let base_reg = self.stack_base_reg.as_ref().unwrap().clone(); + // Bigger than 1 word needs a MCPI. XXX Or MCP if it's huge. - let dest_reg = self.reg_seqr.next(); - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - dest_reg.clone(), - self.stack_base_reg.as_ref().unwrap().clone(), - VirtualImmediate12::new(word_offs * 8, Self::empty_span()) - .unwrap(), - )), - comment: "get store offset".into(), - owning_span: None, - }); + let dest_offs_reg = self.reg_seqr.next(); + if word_offs * 8 + > crate::asm_generation::compiler_constants::TWELVE_BITS + { + self.number_to_reg( + word_offs * 8, + &dest_offs_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADD( + dest_offs_reg.clone(), + base_reg, + dest_offs_reg.clone(), + )), + comment: "get store offset".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + dest_offs_reg.clone(), + base_reg, + VirtualImmediate12 { + value: (word_offs * 8) as u16, + }, + )), + comment: "get store offset".into(), + owning_span: instr_val.get_span(self.context), + }); + } - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::MCPI( - dest_reg, - stored_reg, - VirtualImmediate12::new( - store_size_in_words * 8, - Self::empty_span(), - ) - .unwrap(), - )), - comment: "store value".into(), - owning_span: None, - }); + if store_size_in_words * 8 + > crate::asm_generation::compiler_constants::TWELVE_BITS + { + let size_reg = self.reg_seqr.next(); + self.number_to_reg( + store_size_in_words * 8, + &size_reg, + instr_val.get_span(self.context), + ); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MCP( + dest_offs_reg, + stored_reg, + size_reg, + )), + comment: "store value".into(), + owning_span: instr_val.get_span(self.context), + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::MCPI( + dest_offs_reg, + stored_reg, + VirtualImmediate12 { + value: (store_size_in_words * 8) as u16, + }, + )), + comment: "store value".into(), + owning_span: instr_val.get_span(self.context), + }); + } } } } @@ -1028,9 +1320,9 @@ impl<'ir> AsmBuilder<'ir> { match self.reg_map.get(value) { Some(reg) => reg.clone(), None => { - match &self.context.values[value.0] { + match &self.context.values[value.0].value { // Handle constants. - ValueContent::Constant(constant) => { + ValueDatum::Constant(constant) => { match &constant.value { ConstantValue::Struct(_) | ConstantValue::Array(_) => { // A constant struct or array. We still allocate space for it on @@ -1041,6 +1333,11 @@ impl<'ir> AsmBuilder<'ir> { let total_size = size_bytes_round_up_to_word_alignment!( self.constant_size_in_bytes(constant) ); + if total_size + > crate::asm_generation::compiler_constants::TWENTY_FOUR_BITS + { + todo!("Enormous stack usage for locals."); + } let start_reg = self.reg_seqr.next(); @@ -1053,10 +1350,10 @@ impl<'ir> AsmBuilder<'ir> { "save register for temporary stack value", )); - let mut alloc_op = Op::unowned_stack_allocate_memory( - VirtualImmediate24::new(total_size, Self::empty_span()) - .unwrap(), - ); + let mut alloc_op = + Op::unowned_stack_allocate_memory(VirtualImmediate24 { + value: total_size as u32, + }); alloc_op.comment = format!( "allocate {} bytes for temporary {}", total_size, @@ -1069,7 +1366,12 @@ impl<'ir> AsmBuilder<'ir> { self.bytecode.push(alloc_op); // Fill in the fields. - self.initialise_constant_memory(constant, &start_reg, 0); + self.initialise_constant_memory( + constant, + &start_reg, + 0, + value.get_span(self.context), + ); } // Return the start ptr. @@ -1094,7 +1396,7 @@ impl<'ir> AsmBuilder<'ir> { data_id, )), comment: "literal instantiation".into(), - owning_span: None, + owning_span: value.get_span(self.context), }); // Insert the value into the map. @@ -1117,6 +1419,45 @@ impl<'ir> AsmBuilder<'ir> { } } + fn number_to_reg(&mut self, offset: u64, offset_reg: &VirtualRegister, span: Option) { + if offset > crate::asm_generation::compiler_constants::TWENTY_FOUR_BITS { + todo!("Absolutely giant arrays."); + } + + // Use bitwise ORs and SHIFTs to crate a 24 bit value in a register. + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ORI( + offset_reg.clone(), + VirtualRegister::Constant(ConstantRegister::Zero), + VirtualImmediate12 { + value: (offset >> 12) as u16, + }, + )), + comment: "get extract offset high bits".into(), + owning_span: span.clone(), + }); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::SLLI( + offset_reg.clone(), + offset_reg.clone(), + VirtualImmediate12 { value: 12 }, + )), + comment: "shift extract offset high bits".into(), + owning_span: span.clone(), + }); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ORI( + offset_reg.clone(), + offset_reg.clone(), + VirtualImmediate12 { + value: (offset & 0xfff) as u16, + }, + )), + comment: "get extract offset low bits".into(), + owning_span: span, + }); + } + fn constant_size_in_bytes(&mut self, constant: &Constant) -> u64 { match &constant.value { ConstantValue::Undef => self.ir_type_size_in_bytes(&constant.ty), @@ -1143,6 +1484,7 @@ impl<'ir> AsmBuilder<'ir> { constant: &Constant, start_reg: &VirtualRegister, offs_in_words: u64, + span: Option, ) -> u64 { match &constant.value { ConstantValue::Undef => { @@ -1163,22 +1505,37 @@ impl<'ir> AsmBuilder<'ir> { self.bytecode.push(Op { opcode: either::Either::Left(VirtualOp::LWDataId(init_reg.clone(), data_id)), comment: "literal instantiation for aggregate field".into(), - owning_span: None, + owning_span: span.clone(), }); // Write the initialiser to memory. Most Literals are 1 word, B256 is 32 bytes and // needs to use a MCP instruction. - if let Literal::B256(_) = lit { + if matches!(lit, Literal::B256(_)) { let offs_reg = self.reg_seqr.next(); - self.bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::ADDI( - offs_reg.clone(), - start_reg.clone(), - VirtualImmediate12::new(offs_in_words * 8, Self::empty_span()).unwrap(), - )), - comment: "calculate byte offset to aggregate field".into(), - owning_span: None, - }); + if offs_in_words * 8 > crate::asm_generation::compiler_constants::TWELVE_BITS { + self.number_to_reg(offs_in_words * 8, &offs_reg, span.clone()); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADD( + offs_reg.clone(), + start_reg.clone(), + offs_reg.clone(), + )), + comment: "calculate byte offset to aggregate field".into(), + owning_span: span.clone(), + }); + } else { + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADDI( + offs_reg.clone(), + start_reg.clone(), + VirtualImmediate12 { + value: (offs_in_words * 8) as u16, + }, + )), + comment: "calculate byte offset to aggregate field".into(), + owning_span: span.clone(), + }); + } self.bytecode.push(Op { opcode: Either::Left(VirtualOp::MCPI( offs_reg, @@ -1186,20 +1543,45 @@ impl<'ir> AsmBuilder<'ir> { VirtualImmediate12 { value: 32 }, )), comment: "initialise aggregate field".into(), - owning_span: None, + owning_span: span, }); 4 // 32 bytes is 4 words. } else { - self.bytecode.push(Op { - opcode: Either::Left(VirtualOp::SW( - start_reg.clone(), - init_reg, - VirtualImmediate12::new(offs_in_words, Self::empty_span()).unwrap(), - )), - comment: "initialise aggregate field".into(), - owning_span: None, - }); + if offs_in_words > crate::asm_generation::compiler_constants::TWELVE_BITS { + let offs_reg = self.reg_seqr.next(); + self.number_to_reg(offs_in_words, &offs_reg, span.clone()); + self.bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::ADD( + start_reg.clone(), + start_reg.clone(), + offs_reg.clone(), + )), + comment: "calculate byte offset to aggregate field".into(), + owning_span: span.clone(), + }); + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + start_reg.clone(), + init_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "initialise aggregate field".into(), + owning_span: span, + }); + } else { + self.bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + start_reg.clone(), + init_reg, + VirtualImmediate12 { + value: offs_in_words as u16, + }, + )), + comment: "initialise aggregate field".into(), + owning_span: span, + }); + } 1 } @@ -1211,7 +1593,7 @@ impl<'ir> AsmBuilder<'ir> { self.bytecode.push(Op { opcode: Either::Left(VirtualOp::NOOP), comment: "strings aren't implemented!".into(), - owning_span: None, + owning_span: span, }); 0 @@ -1220,7 +1602,8 @@ impl<'ir> AsmBuilder<'ir> { ConstantValue::Array(items) | ConstantValue::Struct(items) => { let mut cur_offs = offs_in_words; for item in items { - let item_size = self.initialise_constant_memory(item, start_reg, cur_offs); + let item_size = + self.initialise_constant_memory(item, start_reg, cur_offs, span.clone()); cur_offs += item_size; } cur_offs diff --git a/sway-core/src/optimize.rs b/sway-core/src/optimize.rs index 46f6bbfe50b..8fb80d02ff2 100644 --- a/sway-core/src/optimize.rs +++ b/sway-core/src/optimize.rs @@ -135,7 +135,8 @@ fn compile_constant_expression( const_expr: &TypedExpression, ) -> Result { if let TypedExpressionVariant::Literal(literal) = &const_expr.expression { - Ok(convert_literal_to_value(context, literal)) + let span_md_idx = MetadataIndex::from_span(context, &const_expr.span); + Ok(convert_literal_to_value(context, literal, span_md_idx)) } else { Err("Unsupported constant expression type.".into()) } @@ -308,9 +309,9 @@ fn compile_function( .iter() .map(|param| { convert_resolved_typeid(context, ¶m.r#type, ¶m.type_span) - .map(|ty| (param.name.as_str().into(), ty)) + .map(|ty| (param.name.as_str().into(), ty, param.name.span().clone())) }) - .collect::, String>>()?; + .collect::, String>>()?; compile_fn_with_args(context, module, ast_fn_decl, args, None) } @@ -322,7 +323,7 @@ fn compile_fn_with_args( context: &mut Context, module: Module, ast_fn_decl: TypedFunctionDeclaration, - args: Vec<(String, Type)>, + args: Vec<(String, Type, Span)>, selector: Option<[u8; 4]>, ) -> Result<(), String> { let TypedFunctionDeclaration { @@ -334,6 +335,10 @@ fn compile_fn_with_args( .. } = ast_fn_decl; + let args = args + .into_iter() + .map(|(name, ty, span)| (name, ty, MetadataIndex::from_span(context, &span))) + .collect(); let ret_type = convert_resolved_typeid(context, &return_type, &return_type_span)?; let func = Function::new( context, @@ -348,7 +353,10 @@ fn compile_fn_with_args( let mut compiler = FnCompiler::new(context, module, func); let ret_val = compiler.compile_code_block(context, body)?; - compiler.current_block.ins(context).ret(ret_val, ret_type); + compiler + .current_block + .ins(context) + .ret(ret_val, ret_type, None); Ok(()) } @@ -370,9 +378,9 @@ fn compile_impl( } else { convert_resolved_typeid(context, ¶m.r#type, ¶m.type_span) } - .map(|ty| (param.name.as_str().into(), ty)) + .map(|ty| (param.name.as_str().into(), ty, param.name.span().clone())) }) - .collect::, String>>()?; + .collect::, String>>()?; compile_fn_with_args(context, module, method, args, None)?; } @@ -396,9 +404,9 @@ fn compile_abi_method( .iter() .map(|param| { convert_resolved_typeid(context, ¶m.r#type, ¶m.type_span) - .map(|ty| (param.name.as_str().into(), ty)) + .map(|ty| (param.name.as_str().into(), ty, param.name.span().clone())) }) - .collect::, String>>()?; + .collect::, String>>()?; compile_fn_with_args(context, module, ast_fn_decl, args, Some(selector)) } @@ -438,34 +446,37 @@ impl FnCompiler { .contents .into_iter() .map(|ast_node| { + let span_md_idx = MetadataIndex::from_span(context, &ast_node.span); match ast_node.content { TypedAstNodeContent::ReturnStatement(trs) => { self.compile_return_statement(context, trs.expr) } TypedAstNodeContent::Declaration(td) => match td { TypedDeclaration::VariableDeclaration(tvd) => { - self.compile_var_decl(context, tvd) + self.compile_var_decl(context, tvd, span_md_idx) } TypedDeclaration::ConstantDeclaration(tcd) => { - self.compile_const_decl(context, tcd) + self.compile_const_decl(context, tcd, span_md_idx) } TypedDeclaration::FunctionDeclaration(_) => Err("func decl".into()), TypedDeclaration::TraitDeclaration(_) => Err("trait decl".into()), TypedDeclaration::StructDeclaration(_) => Err("struct decl".into()), TypedDeclaration::EnumDeclaration(ted) => { + let span_md_idx = MetadataIndex::from_span(context, &ted.span); compile_enum_decl(context, ted).map(|_| ())?; - Ok(Constant::get_unit(context)) + Ok(Constant::get_unit(context, span_md_idx)) } TypedDeclaration::Reassignment(tr) => { - self.compile_reassignment(context, tr) + self.compile_reassignment(context, tr, span_md_idx) } - TypedDeclaration::ImplTrait { .. } => { + TypedDeclaration::ImplTrait { span, .. } => { // XXX What if I ignore the trait implementation??? Potentially since // we currently inline everything and below we 'recreate' the functions // lazily as they are called, nothing needs to be done here. BUT! // This is obviously not really correct, and eventually we want to // compile and then call these properly. - Ok(Constant::get_unit(context)) + let span_md_idx = MetadataIndex::from_span(context, &span); + Ok(Constant::get_unit(context, span_md_idx)) } TypedDeclaration::AbiDeclaration(_) => Err("abi decl".into()), TypedDeclaration::GenericTypeForFunctionScope { .. } => { @@ -480,14 +491,16 @@ impl FnCompiler { TypedAstNodeContent::ImplicitReturnExpression(te) => { self.compile_expression(context, te) } - TypedAstNodeContent::WhileLoop(twl) => self.compile_while_loop(context, twl), + TypedAstNodeContent::WhileLoop(twl) => { + self.compile_while_loop(context, twl, span_md_idx) + } TypedAstNodeContent::SideEffect => Err("code block side effect".into()), } }) .collect::, String>>() .map(|vals| vals.last().cloned()) .transpose() - .unwrap_or_else(|| Ok(Constant::get_unit(context))) + .unwrap_or_else(|| Ok(Constant::get_unit(context, None))) } // --------------------------------------------------------------------------------------------- @@ -497,8 +510,9 @@ impl FnCompiler { context: &mut Context, ast_expr: TypedExpression, ) -> Result { + let span_md_idx = MetadataIndex::from_span(context, &ast_expr.span); match ast_expr.expression { - TypedExpressionVariant::Literal(l) => Ok(convert_literal_to_value(context, &l)), + TypedExpressionVariant::Literal(l) => Ok(convert_literal_to_value(context, &l, span_md_idx)), TypedExpressionVariant::FunctionApplication { name, arguments, @@ -509,23 +523,24 @@ impl FnCompiler { name.suffix.as_str(), arguments, Some(function_body), + span_md_idx, ), - TypedExpressionVariant::LazyOperator { op, lhs, rhs, .. } => { - self.compile_lazy_op(context, op, *lhs, *rhs) + TypedExpressionVariant::LazyOperator { op, lhs, rhs } => { + self.compile_lazy_op(context, op, *lhs, *rhs, span_md_idx) } TypedExpressionVariant::VariableExpression { name } => { - self.compile_var_expr(context, name.as_str()) + self.compile_var_expr(context, name.as_str(), span_md_idx) } TypedExpressionVariant::Array { contents } => { - self.compile_array_expr(context, contents) + self.compile_array_expr(context, contents, span_md_idx) } TypedExpressionVariant::ArrayIndex { prefix, index } => { - self.compile_array_index(context, *prefix, *index) + self.compile_array_index(context, *prefix, *index, span_md_idx) } TypedExpressionVariant::StructExpression { struct_name, fields, - } => self.compile_struct_expr(context, struct_name.as_str(), fields), + } => self.compile_struct_expr(context, struct_name.as_str(), fields, span_md_idx), TypedExpressionVariant::CodeBlock(cb) => self.compile_code_block(context, cb), TypedExpressionVariant::FunctionParameter => Err("expr func param".into()), TypedExpressionVariant::IfExp { @@ -537,19 +552,27 @@ impl FnCompiler { registers, body, returns, - .. - } => self.compile_asm_expr(context, registers, body, returns), + whole_block_span, + } => { + let span_md_idx = MetadataIndex::from_span(context, &whole_block_span); + self.compile_asm_expr(context, registers, body, returns, span_md_idx) + } TypedExpressionVariant::StructFieldAccess { prefix, field_to_access, resolved_type_of_parent, + field_to_access_span, .. - } => self.compile_struct_field_expr( - context, - *prefix, - field_to_access, - resolved_type_of_parent, - ), + } => { + let span_md_idx = MetadataIndex::from_span(context, &field_to_access_span); + self.compile_struct_field_expr( + context, + *prefix, + field_to_access, + resolved_type_of_parent, + span_md_idx, + ) + } TypedExpressionVariant::EnumInstantiation { enum_decl, tag, @@ -564,7 +587,7 @@ impl FnCompiler { } => Err("enum arg access".into()), TypedExpressionVariant::Tuple { fields - } => self.compile_tuple_expr(context, fields), + } => self.compile_tuple_expr(context, fields, span_md_idx), TypedExpressionVariant::TupleElemAccess { prefix, elem_to_access_num: idx, @@ -572,7 +595,10 @@ impl FnCompiler { resolved_type_of_parent: tuple_type, } => self.compile_tuple_elem_expr( context, *prefix, tuple_type, idx, span), // XXX IGNORE FOR NOW? - TypedExpressionVariant::AbiCast { .. } => Ok(Constant::get_unit(context)), + TypedExpressionVariant::AbiCast { span, .. } => { + let span_md_idx = MetadataIndex::from_span(context, &span); + Ok(Constant::get_unit(context, span_md_idx)) + } } } @@ -583,15 +609,18 @@ impl FnCompiler { context: &mut Context, ast_expr: TypedExpression, ) -> Result { + let span_md_idx = MetadataIndex::from_span(context, &ast_expr.span); let ret_value = self.compile_expression(context, ast_expr)?; match ret_value.get_type(context) { None => Err("Unable to determine type for return statement expression.".into()), Some(ret_ty) => { - self.current_block.ins(context).ret(ret_value, ret_ty); + self.current_block + .ins(context) + .ret(ret_value, ret_ty, span_md_idx); // RET is a terminator so we must create a new block here. If anything is added to // it then it'll almost certainly be dead code. self.current_block = self.function.create_block(context, None); - Ok(Constant::get_unit(context)) + Ok(Constant::get_unit(context, span_md_idx)) } } } @@ -604,6 +633,7 @@ impl FnCompiler { ast_op: LazyOp, ast_lhs: TypedExpression, ast_rhs: TypedExpression, + span_md_idx: Option, ) -> Result { // Short-circuit: if LHS is true for AND we still must eval the RHS block; for OR we can // skip the RHS block, and vice-versa. @@ -612,19 +642,27 @@ impl FnCompiler { let final_block = self.function.create_block(context, None); let cond_builder = self.current_block.ins(context); match ast_op { - LazyOp::And => { - cond_builder.conditional_branch(lhs_val, rhs_block, final_block, Some(lhs_val)) - } - LazyOp::Or => { - cond_builder.conditional_branch(lhs_val, final_block, rhs_block, Some(lhs_val)) - } + LazyOp::And => cond_builder.conditional_branch( + lhs_val, + rhs_block, + final_block, + Some(lhs_val), + span_md_idx, + ), + LazyOp::Or => cond_builder.conditional_branch( + lhs_val, + final_block, + rhs_block, + Some(lhs_val), + span_md_idx, + ), }; self.current_block = rhs_block; let rhs_val = self.compile_expression(context, ast_rhs)?; self.current_block .ins(context) - .branch(final_block, Some(rhs_val)); + .branch(final_block, Some(rhs_val), span_md_idx); self.current_block = final_block; Ok(final_block.get_phi(context)) @@ -638,6 +676,7 @@ impl FnCompiler { ast_name: &str, ast_args: Vec<(Ident, TypedExpression)>, callee_body: Option, + span_md_idx: Option, ) -> Result { // XXX OK, now, the old compiler inlines everything very lazily. Function calls include // the body of the callee (i.e., the callee_body arg above) and so codegen just pulled it @@ -668,7 +707,10 @@ impl FnCompiler { .into_iter() .map(|(_, expr)| self.compile_expression(context, expr)) .collect::, String>>()?; - Ok(self.current_block.ins(context).call(callee, &args)) + Ok(self + .current_block + .ins(context) + .call(callee, &args, span_md_idx)) } None if callee_body.is_none() => Err(format!("function not found: {}", ast_name)), @@ -732,7 +774,7 @@ impl FnCompiler { compile_function(context, self.module, callee_fn_decl)?; // Then recursively create a call to it. - self.compile_fn_call(context, &callee_name, ast_args, None) + self.compile_fn_call(context, &callee_name, ast_args, None, span_md_idx) } } } @@ -763,6 +805,7 @@ impl FnCompiler { ) -> Result { // Compile the condition expression in the entry block. Then save the current block so we // can jump to the true and false blocks after we've created them. + let cond_span_md_idx = MetadataIndex::from_span(context, &ast_condition.span); let cond_value = self.compile_expression(context, ast_condition)?; let entry_block = self.current_block; @@ -787,7 +830,7 @@ impl FnCompiler { let false_block_begin = self.function.create_block(context, None); self.current_block = false_block_begin; let false_value = match ast_else { - None => Constant::get_unit(context), + None => Constant::get_unit(context, None), Some(expr) => self.compile_expression(context, *expr)?, }; let false_block_end = self.current_block; @@ -797,15 +840,16 @@ impl FnCompiler { true_block_begin, false_block_begin, None, + cond_span_md_idx, ); let merge_block = self.function.create_block(context, None); true_block_end .ins(context) - .branch(merge_block, Some(true_value)); + .branch(merge_block, Some(true_value), None); false_block_end .ins(context) - .branch(merge_block, Some(false_value)); + .branch(merge_block, Some(false_value), None); self.current_block = merge_block; Ok(merge_block.get_phi(context)) @@ -817,6 +861,7 @@ impl FnCompiler { &mut self, context: &mut Context, ast_while_loop: TypedWhileLoop, + span_md_idx: Option, ) -> Result { // We're dancing around a bit here to make the blocks sit in the right order. Ideally we // have the cond block, followed by the body block which may contain other blocks, and the @@ -824,7 +869,9 @@ impl FnCompiler { // Jump to the while cond block. let cond_block = self.function.create_block(context, Some("while".into())); - self.current_block.ins(context).branch(cond_block, None); + self.current_block + .ins(context) + .branch(cond_block, None, None); // Fill in the body block now, jump unconditionally to the cond block at its end. let body_block = self @@ -832,7 +879,9 @@ impl FnCompiler { .create_block(context, Some("while_body".into())); self.current_block = body_block; self.compile_code_block(context, ast_while_loop.body)?; - self.current_block.ins(context).branch(cond_block, None); + self.current_block + .ins(context) + .branch(cond_block, None, None); // Create the final block after we're finished with the body. let final_block = self @@ -847,15 +896,21 @@ impl FnCompiler { body_block, final_block, None, + None, ); self.current_block = final_block; - Ok(Constant::get_unit(context)) + Ok(Constant::get_unit(context, span_md_idx)) } // --------------------------------------------------------------------------------------------- - fn compile_var_expr(&mut self, context: &mut Context, name: &str) -> Result { + fn compile_var_expr( + &mut self, + context: &mut Context, + name: &str, + span_md_idx: Option, + ) -> Result { // We need to check the symbol map first, in case locals are shadowing the args, other // locals or even constants. if let Some(ptr) = self @@ -864,9 +919,9 @@ impl FnCompiler { .and_then(|local_name| self.function.get_local_ptr(context, local_name)) { Ok(if ptr.is_struct_ptr(context) { - self.current_block.ins(context).get_ptr(ptr) + self.current_block.ins(context).get_ptr(ptr, span_md_idx) } else { - self.current_block.ins(context).load(ptr) + self.current_block.ins(context).load(ptr, span_md_idx) }) } else if let Some(val) = self.function.get_arg(context, name) { Ok(val) @@ -883,6 +938,7 @@ impl FnCompiler { &mut self, context: &mut Context, ast_var_decl: TypedVariableDeclaration, + span_md_idx: Option, ) -> Result { let TypedVariableDeclaration { name, @@ -917,7 +973,9 @@ impl FnCompiler { None, )?; - self.current_block.ins(context).store(ptr, init_val); + self.current_block + .ins(context) + .store(ptr, init_val, span_md_idx); Ok(init_val) } @@ -927,6 +985,7 @@ impl FnCompiler { &mut self, context: &mut Context, ast_const_decl: TypedConstantDeclaration, + span_md_idx: Option, ) -> Result { // This is local to the function, so we add it to the locals, rather than the module // globals like other const decls. @@ -949,7 +1008,7 @@ impl FnCompiler { // decls vs const decls, for now they're essentially the same...) self.symbol_map.insert(name.clone(), name); - Ok(Constant::get_unit(context)) + Ok(Constant::get_unit(context, span_md_idx)) } else { Err("Unsupported constant declaration type.".into()) } @@ -961,6 +1020,7 @@ impl FnCompiler { &mut self, context: &mut Context, ast_reassignment: TypedReassignment, + span_md_idx: Option, ) -> Result { let name = ast_reassignment.lhs[0].name.as_str(); let ptr_val = self @@ -972,7 +1032,9 @@ impl FnCompiler { if ast_reassignment.lhs.len() == 1 { // A non-aggregate; use a `store`. - self.current_block.ins(context).store(ptr_val, reassign_val); + self.current_block + .ins(context) + .store(ptr_val, reassign_val, span_md_idx); } else { // An aggregate. Iterate over the field names from the left hand side and collect // field indices. @@ -1018,10 +1080,17 @@ impl FnCompiler { } }; - let get_ptr_val = self.current_block.ins(context).get_ptr(ptr_val); - self.current_block + let get_ptr_val = self + .current_block .ins(context) - .insert_value(get_ptr_val, ty, reassign_val, field_idcs); + .get_ptr(ptr_val, span_md_idx); + self.current_block.ins(context).insert_value( + get_ptr_val, + ty, + reassign_val, + field_idcs, + span_md_idx, + ); } // This shouldn't really return a value, it doesn't make sense to return the `store` or @@ -1035,6 +1104,7 @@ impl FnCompiler { &mut self, context: &mut Context, contents: Vec, + span_md_idx: Option, ) -> Result { if contents.is_empty() { return Err("Unable to create zero sized static arrays.".into()); @@ -1045,7 +1115,7 @@ impl FnCompiler { let aggregate = Aggregate::new_array(context, elem_type, contents.len() as u64); // Compile each element and insert it immediately. - let array_value = Constant::get_undef(context, Type::Array(aggregate)); + let array_value = Constant::get_undef(context, Type::Array(aggregate), span_md_idx); contents .into_iter() .enumerate() @@ -1054,7 +1124,7 @@ impl FnCompiler { match array_value { Err(_) => array_value, Ok(array_value) => { - let index_val = Constant::get_uint(context, 64, idx as u64); + let index_val = Constant::get_uint(context, 64, idx as u64, span_md_idx); self.compile_expression(context, elem_expr) .map(|elem_value| { self.current_block.ins(context).insert_element( @@ -1062,6 +1132,7 @@ impl FnCompiler { aggregate, elem_value, index_val, + span_md_idx, ) }) } @@ -1076,10 +1147,11 @@ impl FnCompiler { context: &mut Context, array_expr: TypedExpression, index_expr: TypedExpression, + span_md_idx: Option, ) -> Result { let array_val = self.compile_expression(context, array_expr)?; - let aggregate = match &context.values[array_val.0] { - ValueContent::Instruction(instruction) => { + let aggregate = match &context.values[array_val.0].value { + ValueDatum::Instruction(instruction) => { instruction.get_aggregate(context).ok_or_else(|| { format!( "Unsupported instruction as array value for index expression. {:?}", @@ -1087,7 +1159,7 @@ impl FnCompiler { ) }) } - ValueContent::Argument(Type::Array(aggregate)) => Ok(*aggregate), + ValueDatum::Argument(Type::Array(aggregate)) => Ok(*aggregate), otherwise => Err(format!( "Unsupported array value for index expression: {:?}", otherwise @@ -1110,10 +1182,12 @@ impl FnCompiler { let index_val = self.compile_expression(context, index_expr)?; - Ok(self - .current_block - .ins(context) - .extract_element(array_val, aggregate, index_val)) + Ok(self.current_block.ins(context).extract_element( + array_val, + aggregate, + index_val, + span_md_idx, + )) } // --------------------------------------------------------------------------------------------- @@ -1123,6 +1197,7 @@ impl FnCompiler { context: &mut Context, struct_name: &str, fields: Vec, + span_md_idx: Option, ) -> Result { let aggregate = context .get_aggregate_by_name(struct_name) @@ -1146,7 +1221,7 @@ impl FnCompiler { .collect::, String>>()?; // Start with a constant empty struct and then fill in the values. - let agg_value = Constant::get_undef(context, Type::Struct(aggregate)); + let agg_value = Constant::get_undef(context, Type::Struct(aggregate), span_md_idx); Ok(inserted_values_indices.into_iter().fold( agg_value, |agg_value, (insert_val, insert_idx)| { @@ -1155,6 +1230,7 @@ impl FnCompiler { aggregate, insert_val, vec![insert_idx], + span_md_idx, ) }, )) @@ -1168,10 +1244,11 @@ impl FnCompiler { ast_struct_expr: TypedExpression, ast_field: OwnedTypedStructField, _ast_parent_type: TypeId, + span_md_idx: Option, ) -> Result { let struct_val = self.compile_expression(context, ast_struct_expr)?; - let aggregate = match &context.values[struct_val.0] { - ValueContent::Instruction(instruction) => { + let aggregate = match &context.values[struct_val.0].value { + ValueDatum::Instruction(instruction) => { instruction.get_aggregate(context).ok_or_else(|| { format!( "Unsupported instruction as struct value for field expression. {:?}", @@ -1179,7 +1256,7 @@ impl FnCompiler { ) }) } - ValueContent::Argument(Type::Struct(aggregate)) => Ok(*aggregate), + ValueDatum::Argument(Type::Struct(aggregate)) => Ok(*aggregate), otherwise => Err(format!( "Unsupported struct value for field expression: {:?}", otherwise @@ -1190,10 +1267,12 @@ impl FnCompiler { .get_aggregate_index(&aggregate, &ast_field.name) .ok_or_else(|| format!("Unknown field name {} in struct ???", ast_field.name))?; - Ok(self - .current_block - .ins(context) - .extract_value(struct_val, aggregate, vec![field_idx])) + Ok(self.current_block.ins(context).extract_value( + struct_val, + aggregate, + vec![field_idx], + span_md_idx, + )) } // --------------------------------------------------------------------------------------------- @@ -1212,18 +1291,22 @@ impl FnCompiler { // the name, and if not add a new aggregate... OTOH the naming seems a little fragile and // we could potentially use the wrong aggregate with the same name, different module... // dunno. + let span_md_idx = MetadataIndex::from_span(context, &enum_decl.span); let aggregate = match context.get_aggregate_by_name(enum_decl.name.as_str()) { Some(agg) => Ok(agg), None => compile_enum_decl(context, enum_decl), }?; - let tag_value = Constant::get_uint(context, 64, tag as u64); + let tag_value = Constant::get_uint(context, 64, tag as u64, span_md_idx); // Start with the undef and insert the tag. - let agg_value = Constant::get_undef(context, Type::Struct(aggregate)); - let agg_value = - self.current_block - .ins(context) - .insert_value(agg_value, aggregate, tag_value, vec![0]); + let agg_value = Constant::get_undef(context, Type::Struct(aggregate), span_md_idx); + let agg_value = self.current_block.ins(context).insert_value( + agg_value, + aggregate, + tag_value, + vec![0], + span_md_idx, + ); Ok(match contents { None => agg_value, @@ -1235,6 +1318,7 @@ impl FnCompiler { aggregate, contents_value, vec![1], + span_md_idx, ) } }) @@ -1246,11 +1330,12 @@ impl FnCompiler { &mut self, context: &mut Context, fields: Vec, + span_md_idx: Option, ) -> Result { if fields.is_empty() { // This is a Unit. We're still debating whether Unit should just be an empty tuple in // the IR or not... it is a special case for now. - Ok(Constant::get_unit(context)) + Ok(Constant::get_unit(context, span_md_idx)) } else { let (init_values, init_types): (Vec, Vec) = fields .into_iter() @@ -1267,7 +1352,7 @@ impl FnCompiler { .unzip(); let aggregate = Aggregate::new_struct(context, None, init_types); - let agg_value = Constant::get_undef(context, Type::Struct(aggregate)); + let agg_value = Constant::get_undef(context, Type::Struct(aggregate), span_md_idx); Ok(init_values.into_iter().enumerate().fold( agg_value, @@ -1277,6 +1362,7 @@ impl FnCompiler { aggregate, insert_val, vec![insert_idx as u64], + span_md_idx, ) }, )) @@ -1295,10 +1381,12 @@ impl FnCompiler { ) -> Result { let tuple_value = self.compile_expression(context, tuple)?; if let Type::Struct(aggregate) = convert_resolved_typeid(context, &tuple_type, &span)? { + let span_md_idx = MetadataIndex::from_span(context, &span); Ok(self.current_block.ins(context).extract_value( tuple_value, aggregate, vec![idx as u64], + span_md_idx, )) } else { Err("Invalid (non-aggregate?) tuple type for TupleElemAccess?".into()) @@ -1313,6 +1401,7 @@ impl FnCompiler { registers: Vec, body: Vec, returns: Option<(AsmRegister, Span)>, + whole_block_span_md_idx: Option, ) -> Result { let registers = registers .into_iter() @@ -1339,11 +1428,12 @@ impl FnCompiler { op_name, op_args, immediate, - .. + span, }| AsmInstruction { name: op_name, args: op_args, immediate, + span_md_idx: MetadataIndex::from_span(context, &span), }, ) .collect(); @@ -1353,25 +1443,31 @@ impl FnCompiler { path: None, }) }); - Ok(self - .current_block - .ins(context) - .asm_block(registers, body, returns)) + Ok(self.current_block.ins(context).asm_block( + registers, + body, + returns, + whole_block_span_md_idx, + )) } } // ------------------------------------------------------------------------------------------------- -fn convert_literal_to_value(context: &mut Context, ast_literal: &Literal) -> Value { +fn convert_literal_to_value( + context: &mut Context, + ast_literal: &Literal, + span_id_idx: Option, +) -> Value { match ast_literal { - Literal::U8(n) | Literal::Byte(n) => Constant::get_uint(context, 8, *n as u64), - Literal::U16(n) => Constant::get_uint(context, 16, *n as u64), - Literal::U32(n) => Constant::get_uint(context, 32, *n as u64), - Literal::U64(n) => Constant::get_uint(context, 64, *n), - Literal::Numeric(n) => Constant::get_uint(context, 64, *n), - Literal::String(s) => Constant::get_string(context, s.as_str().to_owned()), - Literal::Boolean(b) => Constant::get_bool(context, *b), - Literal::B256(bs) => Constant::get_b256(context, *bs), + Literal::U8(n) | Literal::Byte(n) => Constant::get_uint(context, 8, *n as u64, span_id_idx), + Literal::U16(n) => Constant::get_uint(context, 16, *n as u64, span_id_idx), + Literal::U32(n) => Constant::get_uint(context, 32, *n as u64, span_id_idx), + Literal::U64(n) => Constant::get_uint(context, 64, *n, span_id_idx), + Literal::Numeric(n) => Constant::get_uint(context, 64, *n, span_id_idx), + Literal::String(s) => Constant::get_string(context, s.as_str().to_owned(), span_id_idx), + Literal::Boolean(b) => Constant::get_bool(context, *b, span_id_idx), + Literal::B256(bs) => Constant::get_b256(context, *bs, span_id_idx), } } @@ -1519,19 +1615,27 @@ mod tests { } } - fn test_sway_to_ir(mut path: PathBuf) { - let input_bytes = std::fs::read(&path).unwrap(); + fn test_sway_to_ir(sw_path: PathBuf) { + let input_bytes = std::fs::read(&sw_path).unwrap(); let input = String::from_utf8_lossy(&input_bytes); - path.set_extension("ir"); + let mut ir_path = sw_path.clone(); + ir_path.set_extension("ir"); - let expected_bytes = std::fs::read(&path).unwrap(); + let expected_bytes = std::fs::read(&ir_path).unwrap(); let expected = String::from_utf8_lossy(&expected_bytes); - let typed_ast = parse_to_typed_ast(&input); + let typed_ast = parse_to_typed_ast(sw_path, &input); let ir = super::compile_ast(typed_ast).unwrap(); let output = sway_ir::printer::to_string(&ir); + // Use a tricky regex to replace the local path in the metadata with something generic. It + // should convert, e.g., + // `!0 = filepath "/usr/home/me/sway/sway-core/tests/sway_to_ir/foo.sw"` + // to `!0 = filepath "/path/to/foo.sw"` + let path_converter = regex::Regex::new(r#"(!\d = filepath ")(?:[^/]*/)*(.+)"#).unwrap(); + let output = path_converter.replace_all(output.as_str(), "$1/path/to/$2"); + if output != expected { println!("{}", prettydiff::diff_lines(&expected, &output)); } @@ -1568,6 +1672,14 @@ mod tests { let input_bytes = std::fs::read(&path).unwrap(); let input = String::from_utf8_lossy(&input_bytes); + // Use another tricky regex to inject the proper metadata filepath back, so we can create + // spans in the parser. NOTE, if/when we refactor spans to not have the source string and + // just the path these tests should pass without needing this conversion. + let mut true_path = path.clone(); + true_path.set_extension("sw"); + let path_converter = regex::Regex::new(r#"(!\d = filepath )(?:.+)"#).unwrap(); + let input = path_converter.replace_all(&input, format!("$1\"{}\"", true_path.display())); + let parsed_ctx = match sway_ir::parser::parse(&input) { Ok(p) => p, Err(e) => { @@ -1584,23 +1696,16 @@ mod tests { // ------------------------------------------------------------------------------------------------- - fn parse_to_typed_ast(input: &str) -> TypedParseTree { + fn parse_to_typed_ast(path: PathBuf, input: &str) -> TypedParseTree { let mut parsed = SwayParser::parse(Rule::program, std::sync::Arc::from(input)).expect("parse_tree"); - let mut warnings = vec![]; - let mut errors = vec![]; - let parse_tree = crate::parse_root_from_pairs(parsed.next().unwrap().into_inner(), None) - .unwrap(&mut warnings, &mut errors); + let dir_of_code = std::sync::Arc::new(path.parent().unwrap().into()); + let file_name = std::sync::Arc::new(path); - let mut dead_code_graph = ControlFlowGraph { - graph: Graph::new(), - entry_points: vec![], - namespace: Default::default(), - }; let build_config = crate::build_config::BuildConfig { - file_name: std::sync::Arc::new("test.sw".into()), - dir_of_code: std::sync::Arc::new("tests".into()), + file_name, + dir_of_code, manifest_path: std::sync::Arc::new(".".into()), use_ir: false, print_intermediate_asm: false, @@ -1608,6 +1713,18 @@ mod tests { print_ir: false, generated_names: std::sync::Arc::new(std::sync::Mutex::new(vec![])), }; + + let mut warnings = vec![]; + let mut errors = vec![]; + let parse_tree = + crate::parse_root_from_pairs(parsed.next().unwrap().into_inner(), Some(&build_config)) + .unwrap(&mut warnings, &mut errors); + + let mut dead_code_graph = ControlFlowGraph { + graph: Graph::new(), + entry_points: vec![], + namespace: Default::default(), + }; TypedParseTree::type_check( parse_tree.tree, crate::create_module(), diff --git a/sway-core/tests/sway_to_ir/array_simple.ir b/sway-core/tests/sway_to_ir/array_simple.ir index 913417977e6..84ff0265775 100644 --- a/sway-core/tests/sway_to_ir/array_simple.ir +++ b/sway-core/tests/sway_to_ir/array_simple.ir @@ -3,20 +3,30 @@ script script { local ptr [bool; 3] a entry: - v0 = const [bool; 3] [bool undef, bool undef, bool undef] - v1 = const bool false - v2 = const u64 0 - v3 = insert_element v0, [bool; 3], v1, v2 - v4 = const bool true - v5 = const u64 1 - v6 = insert_element v3, [bool; 3], v4, v5 - v7 = const bool false - v8 = const u64 2 - v9 = insert_element v6, [bool; 3], v7, v8 - store v9, ptr [bool; 3] a - v10 = load ptr [bool; 3] a - v11 = const u64 1 - v12 = extract_element v10, [bool; 3], v11 + v0 = const [bool; 3] [bool undef, bool undef, bool undef], !1 + v1 = const bool false, !2 + v2 = const u64 0, !1 + v3 = insert_element v0, [bool; 3], v1, v2, !1 + v4 = const bool true, !3 + v5 = const u64 1, !1 + v6 = insert_element v3, [bool; 3], v4, v5, !1 + v7 = const bool false, !4 + v8 = const u64 2, !1 + v9 = insert_element v6, [bool; 3], v7, v8, !1 + store v9, ptr [bool; 3] a, !5 + v10 = load ptr [bool; 3] a, !6 + v11 = const u64 1, !7 + v12 = extract_element v10, [bool; 3], v11, !8 ret bool v12 } } + +!0 = filepath "/path/to/array_simple.sw" +!1 = span !0 42 60 +!2 = span !0 42 47 +!3 = span !0 49 53 +!4 = span !0 55 60 +!5 = span !0 33 62 +!6 = span !0 67 68 +!7 = span !0 69 70 +!8 = span !0 67 72 diff --git a/sway-core/tests/sway_to_ir/asm_block.ir b/sway-core/tests/sway_to_ir/asm_block.ir index 372ef5fee00..9374b5d8973 100644 --- a/sway-core/tests/sway_to_ir/asm_block.ir +++ b/sway-core/tests/sway_to_ir/asm_block.ir @@ -1,16 +1,21 @@ script script { fn get_global_gas() -> u64 { entry: - v0 = asm() -> ggas { + v0 = asm() -> ggas, !1 { } ret u64 v0 } fn main() -> u64 { entry: - v0 = asm(r1) -> r1 { - bhei r1 + v0 = asm(r1) -> r1, !2 { + bhei r1, !3 } ret u64 v0 } } + +!0 = filepath "/path/to/asm_block.sw" +!1 = span !0 139 165 +!2 = span !0 192 240 +!3 = span !0 210 218 diff --git a/sway-core/tests/sway_to_ir/b256_immeds.ir b/sway-core/tests/sway_to_ir/b256_immeds.ir index 743bef99934..9d29aca5c9d 100644 --- a/sway-core/tests/sway_to_ir/b256_immeds.ir +++ b/sway-core/tests/sway_to_ir/b256_immeds.ir @@ -1,9 +1,9 @@ script script { - fn cmp(a: b256, b: b256) -> bool { + fn cmp(a !1: b256, b !2: b256) -> bool { entry: - v0 = asm(lhs: a, rhs: b, sz, res) -> res { - addi sz zero i32 - meq res lhs rhs sz + v0 = asm(lhs: a, rhs: b, sz, res) -> res, !3 { + addi sz zero i32, !4 + meq res lhs rhs sz, !5 } ret bool v0 } @@ -12,11 +12,23 @@ script script { local ptr b256 a entry: - v0 = const b256 0x0202020202020202020202020202020202020202020202020202020202020202 - store v0, ptr b256 a - v1 = load ptr b256 a - v2 = const b256 0x0303030303030303030303030303030303030303030303030303030303030303 - v3 = call cmp(v1, v2) + v0 = const b256 0x0202020202020202020202020202020202020202020202020202020202020202, !6 + store v0, ptr b256 a, !7 + v1 = load ptr b256 a, !8 + v2 = const b256 0x0303030303030303030303030303030303030303030303030303030303030303, !9 + v3 = call cmp(v1, v2), !10 ret bool v3 } } + +!0 = filepath "/path/to/b256_immeds.sw" +!1 = span !0 198 199 +!2 = span !0 207 208 +!3 = span !0 230 338 +!4 = span !0 269 286 +!5 = span !0 295 314 +!6 = span !0 41 107 +!7 = span !0 33 108 +!8 = span !0 117 118 +!9 = span !0 120 186 +!10 = span !0 191 340 diff --git a/sway-core/tests/sway_to_ir/enum.ir b/sway-core/tests/sway_to_ir/enum.ir index a156655be97..4b5162363d2 100644 --- a/sway-core/tests/sway_to_ir/enum.ir +++ b/sway-core/tests/sway_to_ir/enum.ir @@ -1,7 +1,7 @@ script script { - fn eat(meal: { u64, { () | () | u64 } }) -> bool { + fn eat(meal !1: { u64, { () | () | u64 } }) -> bool { entry: - v0 = const bool false + v0 = const bool false, !2 ret bool v0 } @@ -9,18 +9,29 @@ script script { local ptr { u64, { () | () | u64 } } lunch entry: - v0 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef } - v1 = const u64 1 - v2 = insert_value v0, { u64, { () | () | u64 } }, v1, 0 - store v2, ptr { u64, { () | () | u64 } } lunch - v3 = get_ptr ptr { u64, { () | () | u64 } } lunch - v4 = call eat(v3) - v5 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef } - v6 = const u64 2 - v7 = insert_value v5, { u64, { () | () | u64 } }, v6, 0 - v8 = const u64 3 - v9 = insert_value v7, { u64, { () | () | u64 } }, v8, 1 - v10 = call eat(v9) + v0 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef }, !3 + v1 = const u64 1, !3 + v2 = insert_value v0, { u64, { () | () | u64 } }, v1, 0, !3 + store v2, ptr { u64, { () | () | u64 } } lunch, !4 + v3 = get_ptr ptr { u64, { () | () | u64 } } lunch, !5 + v4 = call eat(v3), !6 + v5 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef }, !7 + v6 = const u64 2, !7 + v7 = insert_value v5, { u64, { () | () | u64 } }, v6, 0, !7 + v8 = const u64 3, !8 + v9 = insert_value v7, { u64, { () | () | u64 } }, v8, 1, !7 + v10 = call eat(v9), !9 ret () v10 } } + +!0 = filepath "/path/to/enum.sw" +!1 = span !0 169 173 +!2 = span !0 196 201 +!3 = span !0 9 71 +!4 = span !0 89 115 +!5 = span !0 124 129 +!6 = span !0 162 203 +!7 = span !0 9 71 +!8 = span !0 154 155 +!9 = span !0 162 203 diff --git a/sway-core/tests/sway_to_ir/enum_enum.ir b/sway-core/tests/sway_to_ir/enum_enum.ir index 417fd986220..450078867fc 100644 --- a/sway-core/tests/sway_to_ir/enum_enum.ir +++ b/sway-core/tests/sway_to_ir/enum_enum.ir @@ -1,13 +1,17 @@ script script { fn main() -> () { entry: - v0 = const { u64, { () | { u64, { () | bool | () } } | () } } { u64 undef, { () | { u64, { () | bool | () } } | () } undef } - v1 = const u64 1 - v2 = insert_value v0, { u64, { () | { u64, { () | bool | () } } | () } }, v1, 0 - v3 = const { u64, { () | bool | () } } { u64 undef, { () | bool | () } undef } - v4 = const u64 0 - v5 = insert_value v3, { u64, { () | bool | () } }, v4, 0 - v6 = insert_value v2, { u64, { () | { u64, { () | bool | () } } | () } }, v5, 1 + v0 = const { u64, { () | { u64, { () | bool | () } } | () } } { u64 undef, { () | { u64, { () | bool | () } } | () } undef }, !1 + v1 = const u64 1, !1 + v2 = insert_value v0, { u64, { () | { u64, { () | bool | () } } | () } }, v1, 0, !1 + v3 = const { u64, { () | bool | () } } { u64 undef, { () | bool | () } undef }, !2 + v4 = const u64 0, !2 + v5 = insert_value v3, { u64, { () | bool | () } }, v4, 0, !2 + v6 = insert_value v2, { u64, { () | { u64, { () | bool | () } } | () } }, v5, 1, !1 ret () v6 } } + +!0 = filepath "/path/to/enum_enum.sw" +!1 = span !0 9 55 +!2 = span !0 57 104 diff --git a/sway-core/tests/sway_to_ir/enum_struct.ir b/sway-core/tests/sway_to_ir/enum_struct.ir index a0936c321f1..ea9141d0b5a 100644 --- a/sway-core/tests/sway_to_ir/enum_struct.ir +++ b/sway-core/tests/sway_to_ir/enum_struct.ir @@ -1,17 +1,24 @@ script script { fn main() -> () { entry: - v0 = const { u64, { () | { b256, bool, u64 } | () } } { u64 undef, { () | { b256, bool, u64 } | () } undef } - v1 = const u64 1 - v2 = insert_value v0, { u64, { () | { b256, bool, u64 } | () } }, v1, 0 - v3 = const { b256, bool, u64 } { b256 undef, bool undef, u64 undef } - v4 = const b256 0x0001010101010101000101010101010100010101010101010001010101010101 - v5 = insert_value v3, { b256, bool, u64 }, v4, 0 - v6 = const bool true - v7 = insert_value v5, { b256, bool, u64 }, v6, 1 - v8 = const u64 53 - v9 = insert_value v7, { b256, bool, u64 }, v8, 2 - v10 = insert_value v2, { u64, { () | { b256, bool, u64 } | () } }, v9, 1 + v0 = const { u64, { () | { b256, bool, u64 } | () } } { u64 undef, { () | { b256, bool, u64 } | () } undef }, !1 + v1 = const u64 1, !1 + v2 = insert_value v0, { u64, { () | { b256, bool, u64 } | () } }, v1, 0, !1 + v3 = const { b256, bool, u64 } { b256 undef, bool undef, u64 undef }, !2 + v4 = const b256 0x0001010101010101000101010101010100010101010101010001010101010101, !3 + v5 = insert_value v3, { b256, bool, u64 }, v4, 0, !2 + v6 = const bool true, !4 + v7 = insert_value v5, { b256, bool, u64 }, v6, 1, !2 + v8 = const u64 53, !5 + v9 = insert_value v7, { b256, bool, u64 }, v8, 2, !2 + v10 = insert_value v2, { u64, { () | { b256, bool, u64 } | () } }, v9, 1, !1 ret () v10 } } + +!0 = filepath "/path/to/enum_struct.sw" +!1 = span !0 9 55 +!2 = span !0 134 256 +!3 = span !0 151 217 +!4 = span !0 230 234 +!5 = span !0 247 249 diff --git a/sway-core/tests/sway_to_ir/fn_call.ir b/sway-core/tests/sway_to_ir/fn_call.ir index 0968a1ae641..2984a2c32cf 100644 --- a/sway-core/tests/sway_to_ir/fn_call.ir +++ b/sway-core/tests/sway_to_ir/fn_call.ir @@ -1,15 +1,22 @@ script script { - fn a(x: u64) -> u64 { + fn a(x !1: u64) -> u64 { entry: ret u64 x } fn main() -> u64 { entry: - v0 = const u64 0 - v1 = call a(v0) - v2 = const u64 1 - v3 = call a(v2) + v0 = const u64 0, !2 + v1 = call a(v0), !3 + v2 = const u64 1, !4 + v3 = call a(v2), !5 ret u64 v3 } } + +!0 = filepath "/path/to/fn_call.sw" +!1 = span !0 14 15 +!2 = span !0 65 66 +!3 = span !0 9 38 +!4 = span !0 75 76 +!5 = span !0 9 38 diff --git a/sway-core/tests/sway_to_ir/if_expr.ir b/sway-core/tests/sway_to_ir/if_expr.ir index 5f5bbdc9b53..c40c6b7ee30 100644 --- a/sway-core/tests/sway_to_ir/if_expr.ir +++ b/sway-core/tests/sway_to_ir/if_expr.ir @@ -1,15 +1,15 @@ script script { fn main() -> u64 { entry: - v0 = const bool false - cbr v0, block0, block1 + v0 = const bool false, !1 + cbr v0, block0, block1, !2 block0: - v1 = const u64 1000000 + v1 = const u64 1000000, !3 br block2 block1: - v2 = const u64 42 + v2 = const u64 42, !4 br block2 block2: @@ -17,3 +17,9 @@ script script { ret u64 v3 } } + +!0 = filepath "/path/to/if_expr.sw" +!1 = span !0 35 40 +!2 = span !0 35 40 +!3 = span !0 51 60 +!4 = span !0 82 84 diff --git a/sway-core/tests/sway_to_ir/impl_ret_int.ir b/sway-core/tests/sway_to_ir/impl_ret_int.ir index e138d80f3a9..1d88dd221c8 100644 --- a/sway-core/tests/sway_to_ir/impl_ret_int.ir +++ b/sway-core/tests/sway_to_ir/impl_ret_int.ir @@ -1,7 +1,10 @@ script script { fn main() -> u64 { entry: - v0 = const u64 42 + v0 = const u64 42, !1 ret u64 v0 } } + +!0 = filepath "/path/to/impl_ret_int.sw" +!1 = span !0 32 34 diff --git a/sway-core/tests/sway_to_ir/lazy_binops.ir b/sway-core/tests/sway_to_ir/lazy_binops.ir index 326fc418aad..ebd5f87b653 100644 --- a/sway-core/tests/sway_to_ir/lazy_binops.ir +++ b/sway-core/tests/sway_to_ir/lazy_binops.ir @@ -1,26 +1,33 @@ script script { fn main() -> bool { entry: - v0 = const bool false - v0 = const bool false - cbr v0, block0, block1 + v0 = const bool false, !1 + v0 = const bool false, !1 + cbr v0, block0, block1, !2 block0: v1 = phi(entry: v0) - v2 = const bool true - br block1 + v2 = const bool true, !3 + br block1, !2 block1: v3 = phi(entry: v0, block0: v2) - cbr v3, block3, block2 + cbr v3, block3, block2, !4 block2: v4 = phi(block1: v3) - v5 = const bool true - br block3 + v5 = const bool true, !5 + br block3, !4 block3: v6 = phi(block1: v3, block2: v5) ret bool v6 } } + +!0 = filepath "/path/to/lazy_binops.sw" +!1 = span !0 34 39 +!2 = span !0 34 47 +!3 = span !0 43 47 +!4 = span !0 34 56 +!5 = span !0 52 56 diff --git a/sway-core/tests/sway_to_ir/let_reassign_while_loop.ir b/sway-core/tests/sway_to_ir/let_reassign_while_loop.ir index f9f5fac4470..5ca25674ca1 100644 --- a/sway-core/tests/sway_to_ir/let_reassign_while_loop.ir +++ b/sway-core/tests/sway_to_ir/let_reassign_while_loop.ir @@ -3,30 +3,40 @@ script script { local mut ptr bool a entry: - v0 = const bool true - store v0, mut ptr bool a + v0 = const bool true, !1 + store v0, mut ptr bool a, !2 br while while: - v1 = load mut ptr bool a + v1 = load mut ptr bool a, !3 cbr v1, while_body, end_while while_body: - v2 = load mut ptr bool a - cbr v2, block0, block1 + v2 = load mut ptr bool a, !4 + cbr v2, block0, block1, !5 block0: v3 = phi(while_body: v2) - v4 = const bool false - br block1 + v4 = const bool false, !6 + br block1, !5 block1: v5 = phi(while_body: v2, block0: v4) - store v5, mut ptr bool a + store v5, mut ptr bool a, !7 br while end_while: - v6 = load mut ptr bool a + v6 = load mut ptr bool a, !8 ret bool v6 } } + +!0 = filepath "/path/to/let_reassign_while_loop.sw" +!1 = span !0 45 49 +!2 = span !0 33 50 +!3 = span !0 61 62 +!4 = span !0 77 78 +!5 = span !0 77 87 +!6 = span !0 82 87 +!7 = span !0 73 88 +!8 = span !0 99 100 diff --git a/sway-core/tests/sway_to_ir/mutable_struct.ir b/sway-core/tests/sway_to_ir/mutable_struct.ir index e9831830842..cd508892c43 100644 --- a/sway-core/tests/sway_to_ir/mutable_struct.ir +++ b/sway-core/tests/sway_to_ir/mutable_struct.ir @@ -3,17 +3,27 @@ script script { local mut ptr { u64, u64 } record entry: - v0 = const { u64, u64 } { u64 undef, u64 undef } - v1 = const u64 40 - v2 = insert_value v0, { u64, u64 }, v1, 0 - v3 = const u64 2 - v4 = insert_value v2, { u64, u64 }, v3, 1 - store v4, mut ptr { u64, u64 } record - v5 = get_ptr mut ptr { u64, u64 } record - v6 = const u64 50 - v7 = insert_value v5, { u64, u64 }, v6, 0 - v8 = get_ptr mut ptr { u64, u64 } record - v9 = extract_value v8, { u64, u64 }, 1 + v0 = const { u64, u64 } { u64 undef, u64 undef }, !1 + v1 = const u64 40, !2 + v2 = insert_value v0, { u64, u64 }, v1, 0, !1 + v3 = const u64 2, !3 + v4 = insert_value v2, { u64, u64 }, v3, 1, !1 + store v4, mut ptr { u64, u64 } record, !4 + v5 = get_ptr mut ptr { u64, u64 } record, !5 + v6 = const u64 50, !6 + v7 = insert_value v5, { u64, u64 }, v6, 0, !5 + v8 = get_ptr mut ptr { u64, u64 } record, !7 + v9 = extract_value v8, { u64, u64 }, 1, !8 ret u64 v9 } } + +!0 = filepath "/path/to/mutable_struct.sw" +!1 = span !0 49 92 +!2 = span !0 69 71 +!3 = span !0 84 85 +!4 = span !0 32 93 +!5 = span !0 98 112 +!6 = span !0 109 111 +!7 = span !0 117 123 +!8 = span !0 124 125 diff --git a/sway-core/tests/sway_to_ir/shadowed_locals.ir b/sway-core/tests/sway_to_ir/shadowed_locals.ir index 3c7acdf3bcc..da31edbb076 100644 --- a/sway-core/tests/sway_to_ir/shadowed_locals.ir +++ b/sway-core/tests/sway_to_ir/shadowed_locals.ir @@ -5,28 +5,42 @@ script script { local ptr { u64 } a__ entry: - v0 = const bool true - store v0, ptr bool a - v1 = load ptr bool a - cbr v1, block0, block1 + v0 = const bool true, !1 + store v0, ptr bool a, !2 + v1 = load ptr bool a, !3 + cbr v1, block0, block1, !4 block0: - v2 = const u64 12 + v2 = const u64 12, !5 br block2 block1: - v3 = const u64 21 + v3 = const u64 21, !6 br block2 block2: v4 = phi(block0: v2, block1: v3) - store v4, ptr u64 a_ - v5 = load ptr u64 a_ - v6 = const { u64 } { u64 undef } - v7 = insert_value v6, { u64 }, v5, 0 - store v7, ptr { u64 } a__ - v8 = get_ptr ptr { u64 } a__ - v9 = extract_value v8, { u64 }, 0 + store v4, ptr u64 a_, !7 + v5 = load ptr u64 a_, !8 + v6 = const { u64 } { u64 undef }, !9 + v7 = insert_value v6, { u64 }, v5, 0, !9 + store v7, ptr { u64 } a__, !10 + v8 = get_ptr ptr { u64 } a__, !11 + v9 = extract_value v8, { u64 }, 0, !12 ret u64 v9 } } + +!0 = filepath "/path/to/shadowed_locals.sw" +!1 = span !0 66 70 +!2 = span !0 58 71 +!3 = span !0 87 88 +!4 = span !0 87 88 +!5 = span !0 91 93 +!6 = span !0 103 105 +!7 = span !0 76 108 +!8 = span !0 128 129 +!9 = span !0 121 131 +!10 = span !0 113 132 +!11 = span !0 137 138 +!12 = span !0 139 140 diff --git a/sway-core/tests/sway_to_ir/shadowed_struct_init.ir b/sway-core/tests/sway_to_ir/shadowed_struct_init.ir index c1905dafcd5..1180b31be75 100644 --- a/sway-core/tests/sway_to_ir/shadowed_struct_init.ir +++ b/sway-core/tests/sway_to_ir/shadowed_struct_init.ir @@ -1,26 +1,40 @@ script script { - fn new(a: bool, b: bool) -> { bool, bool } { + fn new(a !1: bool, b !2: bool) -> { bool, bool } { local ptr bool a_ local ptr bool b_ entry: - v0 = const bool false - store v0, ptr bool a_ - v1 = const bool true - store v1, ptr bool b_ - v2 = load ptr bool a_ - v3 = load ptr bool b_ - v4 = const { bool, bool } { bool undef, bool undef } - v5 = insert_value v4, { bool, bool }, v2, 0 - v6 = insert_value v5, { bool, bool }, v3, 1 + v0 = const bool false, !3 + store v0, ptr bool a_, !4 + v1 = const bool true, !5 + store v1, ptr bool b_, !6 + v2 = load ptr bool a_, !7 + v3 = load ptr bool b_, !8 + v4 = const { bool, bool } { bool undef, bool undef }, !9 + v5 = insert_value v4, { bool, bool }, v2, 0, !9 + v6 = insert_value v5, { bool, bool }, v3, 1, !9 ret { bool, bool } v6 } fn main() -> () { entry: - v0 = const bool true - v1 = const bool false - v2 = call new(v0, v1) + v0 = const bool true, !10 + v1 = const bool false, !11 + v2 = call new(v0, v1), !12 ret () v2 } } + +!0 = filepath "/path/to/shadowed_struct_init.sw" +!1 = span !0 56 57 +!2 = span !0 65 66 +!3 = span !0 93 98 +!4 = span !0 85 99 +!5 = span !0 112 116 +!6 = span !0 104 117 +!7 = span !0 137 138 +!8 = span !0 217 218 +!9 = span !0 122 225 +!10 = span !0 249 253 +!11 = span !0 255 260 +!12 = span !0 49 227 diff --git a/sway-core/tests/sway_to_ir/struct.ir b/sway-core/tests/sway_to_ir/struct.ir index 87b0b2372a1..9b9ab0c79b4 100644 --- a/sway-core/tests/sway_to_ir/struct.ir +++ b/sway-core/tests/sway_to_ir/struct.ir @@ -3,14 +3,22 @@ script script { local ptr { u64, u64 } record entry: - v0 = const { u64, u64 } { u64 undef, u64 undef } - v1 = const u64 40 - v2 = insert_value v0, { u64, u64 }, v1, 0 - v3 = const u64 2 - v4 = insert_value v2, { u64, u64 }, v3, 1 - store v4, ptr { u64, u64 } record - v5 = get_ptr ptr { u64, u64 } record - v6 = extract_value v5, { u64, u64 }, 0 + v0 = const { u64, u64 } { u64 undef, u64 undef }, !1 + v1 = const u64 40, !2 + v2 = insert_value v0, { u64, u64 }, v1, 0, !1 + v3 = const u64 2, !3 + v4 = insert_value v2, { u64, u64 }, v3, 1, !1 + store v4, ptr { u64, u64 } record, !4 + v5 = get_ptr ptr { u64, u64 } record, !5 + v6 = extract_value v5, { u64, u64 }, 0, !6 ret u64 v6 } } + +!0 = filepath "/path/to/struct.sw" +!1 = span !0 45 88 +!2 = span !0 65 67 +!3 = span !0 80 81 +!4 = span !0 32 89 +!5 = span !0 94 100 +!6 = span !0 101 102 diff --git a/sway-core/tests/sway_to_ir/struct_enum.ir b/sway-core/tests/sway_to_ir/struct_enum.ir index bc65cdc0f89..8a9232dbece 100644 --- a/sway-core/tests/sway_to_ir/struct_enum.ir +++ b/sway-core/tests/sway_to_ir/struct_enum.ir @@ -3,16 +3,24 @@ script script { local ptr { bool, { u64, { () | () | u64 } } } record entry: - v0 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef } - v1 = const u64 0 - v2 = insert_value v0, { u64, { () | () | u64 } }, v1, 0 - v3 = const { bool, { u64, { () | () | u64 } } } { bool undef, { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef } } - v4 = const bool false - v5 = insert_value v3, { bool, { u64, { () | () | u64 } } }, v4, 0 - v6 = insert_value v5, { bool, { u64, { () | () | u64 } } }, v2, 1 - store v6, ptr { bool, { u64, { () | () | u64 } } } record - v7 = get_ptr ptr { bool, { u64, { () | () | u64 } } } record - v8 = extract_value v7, { bool, { u64, { () | () | u64 } } }, 0 + v0 = const { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef }, !1 + v1 = const u64 0, !1 + v2 = insert_value v0, { u64, { () | () | u64 } }, v1, 0, !1 + v3 = const { bool, { u64, { () | () | u64 } } } { bool undef, { u64, { () | () | u64 } } { u64 undef, { () | () | u64 } undef } }, !2 + v4 = const bool false, !3 + v5 = insert_value v3, { bool, { u64, { () | () | u64 } } }, v4, 0, !2 + v6 = insert_value v5, { bool, { u64, { () | () | u64 } } }, v2, 1, !2 + store v6, ptr { bool, { u64, { () | () | u64 } } } record, !4 + v7 = get_ptr ptr { bool, { u64, { () | () | u64 } } } record, !5 + v8 = extract_value v7, { bool, { u64, { () | () | u64 } } }, 0, !6 ret bool v8 } } + +!0 = filepath "/path/to/struct_enum.sw" +!1 = span !0 167 229 +!2 = span !0 46 103 +!3 = span !0 66 71 +!4 = span !0 33 104 +!5 = span !0 109 115 +!6 = span !0 116 117 diff --git a/sway-core/tests/sway_to_ir/struct_struct.ir b/sway-core/tests/sway_to_ir/struct_struct.ir index 539db657aa5..62ae32b9d02 100644 --- a/sway-core/tests/sway_to_ir/struct_struct.ir +++ b/sway-core/tests/sway_to_ir/struct_struct.ir @@ -3,19 +3,30 @@ script script { local ptr { b256, { bool, u64 } } record entry: - v0 = const { bool, u64 } { bool undef, u64 undef } - v1 = const bool true - v2 = insert_value v0, { bool, u64 }, v1, 0 - v3 = const u64 76 - v4 = insert_value v2, { bool, u64 }, v3, 1 - v5 = const { b256, { bool, u64 } } { b256 undef, { bool, u64 } { bool undef, u64 undef } } - v6 = const b256 0x0102030405060708010203040506070801020304050607080102030405060708 - v7 = insert_value v5, { b256, { bool, u64 } }, v6, 0 - v8 = insert_value v7, { b256, { bool, u64 } }, v4, 1 - store v8, ptr { b256, { bool, u64 } } record - v9 = get_ptr ptr { b256, { bool, u64 } } record - v10 = extract_value v9, { b256, { bool, u64 } }, 1 - v11 = extract_value v10, { bool, u64 }, 1 + v0 = const { bool, u64 } { bool undef, u64 undef }, !1 + v1 = const bool true, !2 + v2 = insert_value v0, { bool, u64 }, v1, 0, !1 + v3 = const u64 76, !3 + v4 = insert_value v2, { bool, u64 }, v3, 1, !1 + v5 = const { b256, { bool, u64 } } { b256 undef, { bool, u64 } { bool undef, u64 undef } }, !4 + v6 = const b256 0x0102030405060708010203040506070801020304050607080102030405060708, !5 + v7 = insert_value v5, { b256, { bool, u64 } }, v6, 0, !4 + v8 = insert_value v7, { b256, { bool, u64 } }, v4, 1, !4 + store v8, ptr { b256, { bool, u64 } } record, !6 + v9 = get_ptr ptr { b256, { bool, u64 } } record, !7 + v10 = extract_value v9, { b256, { bool, u64 } }, 1, !8 + v11 = extract_value v10, { bool, u64 }, 1, !9 ret u64 v11 } } + +!0 = filepath "/path/to/struct_struct.sw" +!1 = span !0 144 201 +!2 = span !0 167 171 +!3 = span !0 188 190 +!4 = span !0 45 207 +!5 = span !0 65 131 +!6 = span !0 32 208 +!7 = span !0 213 219 +!8 = span !0 220 221 +!9 = span !0 222 223 diff --git a/sway-core/tests/sway_to_ir/trait.ir b/sway-core/tests/sway_to_ir/trait.ir index 722cabb3693..fe41f080009 100644 --- a/sway-core/tests/sway_to_ir/trait.ir +++ b/sway-core/tests/sway_to_ir/trait.ir @@ -1,19 +1,19 @@ script script { - fn pred(self: { bool }) -> bool { + fn pred(self !1: { bool }) -> bool { entry: - v0 = extract_value self, { bool }, 0 + v0 = extract_value self, { bool }, 0, !2 ret bool v0 } - fn pred_or(self: { bool }, other: { bool }) -> bool { + fn pred_or(self !3: { bool }, other !4: { bool }) -> bool { entry: - v0 = call pred(self) - cbr v0, block1, block0 + v0 = call pred(self), !5 + cbr v0, block1, block0, !6 block0: v1 = phi(entry: v0) - v2 = call pred(other) - br block1 + v2 = call pred(other), !7 + br block1, !6 block1: v3 = phi(entry: v0, block0: v2) @@ -25,17 +25,35 @@ script script { local ptr { bool } foo entry: - v0 = const { bool } { bool undef } - v1 = const bool true - v2 = insert_value v0, { bool }, v1, 0 - store v2, ptr { bool } foo - v3 = const { bool } { bool undef } - v4 = const bool false - v5 = insert_value v3, { bool }, v4, 0 - store v5, ptr { bool } bar - v6 = get_ptr ptr { bool } foo - v7 = get_ptr ptr { bool } bar - v8 = call pred_or(v6, v7) + v0 = const { bool } { bool undef }, !8 + v1 = const bool true, !9 + v2 = insert_value v0, { bool }, v1, 0, !8 + store v2, ptr { bool } foo, !10 + v3 = const { bool } { bool undef }, !11 + v4 = const bool false, !12 + v5 = insert_value v3, { bool }, v4, 0, !11 + store v5, ptr { bool } bar, !13 + v6 = get_ptr ptr { bool } foo, !14 + v7 = get_ptr ptr { bool } bar, !15 + v8 = call pred_or(v6, v7), !16 ret bool v8 } } + +!0 = filepath "/path/to/trait.sw" +!1 = span !0 203 207 +!2 = span !0 232 233 +!3 = span !0 68 72 +!4 = span !0 74 79 +!5 = span !0 105 116 +!6 = span !0 105 132 +!7 = span !0 120 132 +!8 = span !0 277 304 +!9 = span !0 294 298 +!10 = span !0 267 305 +!11 = span !0 320 348 +!12 = span !0 337 342 +!13 = span !0 310 349 +!14 = span !0 354 357 +!15 = span !0 366 369 +!16 = span !0 354 370 diff --git a/sway-ir/src/asm.rs b/sway-ir/src/asm.rs index 195e69c5215..43d4172dc0b 100644 --- a/sway-ir/src/asm.rs +++ b/sway-ir/src/asm.rs @@ -14,9 +14,10 @@ //! } //! ``` -use crate::{context::Context, irtype::Type, value::Value}; use sway_types::ident::Ident; +use crate::{context::Context, irtype::Type, metadata::MetadataIndex, value::Value}; + /// A wrapper around an [ECS](https://github.com/fitzgen/generational-arena) handle into the /// [`Context`]. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -41,6 +42,7 @@ pub struct AsmInstruction { pub name: Ident, pub args: Vec, pub immediate: Option, + pub span_md_idx: Option, } impl AsmBlock { diff --git a/sway-ir/src/block.rs b/sway-ir/src/block.rs index bb9a79bb62f..cbd242c4cf6 100644 --- a/sway-ir/src/block.rs +++ b/sway-ir/src/block.rs @@ -14,7 +14,7 @@ use crate::{ context::Context, function::Function, instruction::{Instruction, InstructionInserter, InstructionIterator}, - value::{Value, ValueContent}, + value::{Value, ValueDatum}, }; /// A wrapper around an [ECS](https://github.com/fitzgen/generational-arena) handle into the @@ -39,7 +39,7 @@ impl Block { /// is optional and is used only when printing the IR. pub fn new(context: &mut Context, function: Function, label: Option) -> Block { let label = function.get_unique_label(context, label); - let phi = Value::new_instruction(context, Instruction::Phi(Vec::new())); + let phi = Value::new_instruction(context, Instruction::Phi(Vec::new()), None); let content = BlockContent { label, function, @@ -75,8 +75,8 @@ impl Block { /// use `phi_value`. pub fn add_phi(&self, context: &mut Context, from_block: Block, phi_value: Value) { let phi_val = self.get_phi(context); - match &mut context.values[phi_val.0] { - ValueContent::Instruction(Instruction::Phi(list)) => { + match &mut context.values[phi_val.0].value { + ValueDatum::Instruction(Instruction::Phi(list)) => { list.push((from_block, phi_value)); } _ => unreachable!("First value in block instructions is not a phi."), @@ -88,7 +88,7 @@ impl Block { /// Returns `None` if `from_block` isn't found. pub fn get_phi_val_coming_from(&self, context: &Context, from_block: &Block) -> Option { let phi_val = self.get_phi(context); - if let ValueContent::Instruction(Instruction::Phi(pairs)) = &context.values[phi_val.0] { + if let ValueDatum::Instruction(Instruction::Phi(pairs)) = &context.values[phi_val.0].value { pairs.iter().find_map(|(block, value)| { if block == from_block { Some(*value) @@ -111,8 +111,8 @@ impl Block { new_source: Block, ) { let phi_val = self.get_phi(context); - if let ValueContent::Instruction(Instruction::Phi(ref mut pairs)) = - &mut context.values[phi_val.0] + if let ValueDatum::Instruction(Instruction::Phi(ref mut pairs)) = + &mut context.values[phi_val.0].value { for (block, _) in pairs { if *block == old_source { @@ -130,7 +130,7 @@ impl Block { pub fn get_term_inst<'a>(&self, context: &'a Context) -> Option<&'a Instruction> { context.blocks[self.0].instructions.last().and_then(|val| { // It's guaranteed to be an instruction value. - if let ValueContent::Instruction(term_inst) = &context.values[val.0] { + if let ValueDatum::Instruction(term_inst) = &context.values[val.0].value { Some(term_inst) } else { None diff --git a/sway-ir/src/constant.rs b/sway-ir/src/constant.rs index 3ad2e30e7f8..cb98f0b89bd 100644 --- a/sway-ir/src/constant.rs +++ b/sway-ir/src/constant.rs @@ -3,6 +3,7 @@ use crate::{ context::Context, irtype::{Aggregate, Type}, + metadata::MetadataIndex, value::Value, }; @@ -99,32 +100,53 @@ impl Constant { } } - pub fn get_undef(context: &mut Context, ty: Type) -> Value { - Value::new_constant(context, Constant::new_undef(context, ty)) + pub fn get_undef(context: &mut Context, ty: Type, span_md_idx: Option) -> Value { + Value::new_constant(context, Constant::new_undef(context, ty), span_md_idx) } - pub fn get_unit(context: &mut Context) -> Value { - Value::new_constant(context, Constant::new_unit()) + pub fn get_unit(context: &mut Context, span_md_idx: Option) -> Value { + Value::new_constant(context, Constant::new_unit(), span_md_idx) } - pub fn get_bool(context: &mut Context, value: bool) -> Value { - Value::new_constant(context, Constant::new_bool(value)) + pub fn get_bool( + context: &mut Context, + value: bool, + span_md_idx: Option, + ) -> Value { + Value::new_constant(context, Constant::new_bool(value), span_md_idx) } - pub fn get_uint(context: &mut Context, nbits: u8, value: u64) -> Value { - Value::new_constant(context, Constant::new_uint(nbits, value)) + pub fn get_uint( + context: &mut Context, + nbits: u8, + value: u64, + span_md_idx: Option, + ) -> Value { + Value::new_constant(context, Constant::new_uint(nbits, value), span_md_idx) } - pub fn get_b256(context: &mut Context, value: [u8; 32]) -> Value { - Value::new_constant(context, Constant::new_b256(value)) + pub fn get_b256( + context: &mut Context, + value: [u8; 32], + span_md_idx: Option, + ) -> Value { + Value::new_constant(context, Constant::new_b256(value), span_md_idx) } - pub fn get_string(context: &mut Context, value: String) -> Value { - Value::new_constant(context, Constant::new_string(value)) + pub fn get_string( + context: &mut Context, + value: String, + span_md_idx: Option, + ) -> Value { + Value::new_constant(context, Constant::new_string(value), span_md_idx) } /// `value` must be created as an array constant first, using [`Constant::new_array()`]. - pub fn get_array(context: &mut Context, value: Constant) -> Value { + pub fn get_array( + context: &mut Context, + value: Constant, + span_md_idx: Option, + ) -> Value { assert!(matches!( value, Constant { @@ -132,11 +154,15 @@ impl Constant { .. } )); - Value::new_constant(context, value) + Value::new_constant(context, value, span_md_idx) } /// `value` must be created as a struct constant first, using [`Constant::new_struct()`]. - pub fn get_struct(context: &mut Context, value: Constant) -> Value { + pub fn get_struct( + context: &mut Context, + value: Constant, + span_md_idx: Option, + ) -> Value { assert!(matches!( value, Constant { @@ -144,6 +170,6 @@ impl Constant { .. } )); - Value::new_constant(context, value) + Value::new_constant(context, value, span_md_idx) } } diff --git a/sway-ir/src/context.rs b/sway-ir/src/context.rs index 57500d6f355..31acddfb9fd 100644 --- a/sway-ir/src/context.rs +++ b/sway-ir/src/context.rs @@ -15,6 +15,7 @@ use crate::{ block::BlockContent, function::FunctionContent, irtype::{AbiInstanceContent, Aggregate, AggregateContent}, + metadata::Metadatum, module::ModuleContent, module::ModuleIterator, pointer::PointerContent, @@ -36,6 +37,8 @@ pub struct Context { pub abi_instances: Arena, pub asm_blocks: Arena, + pub metadata: Arena, + pub(super) aggregate_names: HashMap, aggregate_symbols: HashMap>, diff --git a/sway-ir/src/function.rs b/sway-ir/src/function.rs index 3dbe4ea1727..d47e140fc02 100644 --- a/sway-ir/src/function.rs +++ b/sway-ir/src/function.rs @@ -13,6 +13,7 @@ use crate::{ constant::Constant, context::Context, irtype::Type, + metadata::MetadataIndex, module::Module, pointer::{Pointer, PointerContent}, value::Value, @@ -49,14 +50,14 @@ impl Function { context: &mut Context, module: Module, name: String, - args: Vec<(String, Type)>, + args: Vec<(String, Type, Option)>, return_type: Type, selector: Option<[u8; 4]>, is_public: bool, ) -> Function { let arguments = args .into_iter() - .map(|(name, ty)| (name, Value::new_argument(context, ty))) + .map(|(name, ty, span_md_idx)| (name, Value::new_argument(context, ty, span_md_idx))) .collect(); let content = FunctionContent { name, diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index 03f677ad82a..00a883841de 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -8,16 +8,18 @@ //! this should be addressed in the future, perhaps by using compiler intrinsic calls instead of //! the ASM blocks where possible. +use sway_types::ident::Ident; + use crate::{ asm::{AsmArg, AsmBlock, AsmInstruction}, block::Block, context::Context, function::Function, irtype::{Aggregate, Type}, + metadata::MetadataIndex, pointer::Pointer, value::Value, }; -use sway_types::ident::Ident; #[derive(Debug, Clone)] pub enum Instruction { @@ -239,6 +241,7 @@ impl<'a> InstructionInserter<'a> { args: Vec, body: Vec, return_name: Option, + span_md_idx: Option, ) -> Value { let asm = AsmBlock::new( self.context, @@ -246,17 +249,29 @@ impl<'a> InstructionInserter<'a> { body, return_name, ); - self.asm_block_from_asm(asm, args) + self.asm_block_from_asm(asm, args, span_md_idx) } - pub fn asm_block_from_asm(self, asm: AsmBlock, args: Vec) -> Value { - let asm_val = Value::new_instruction(self.context, Instruction::AsmBlock(asm, args)); + pub fn asm_block_from_asm( + self, + asm: AsmBlock, + args: Vec, + span_md_idx: Option, + ) -> Value { + let asm_val = + Value::new_instruction(self.context, Instruction::AsmBlock(asm, args), span_md_idx); self.context.blocks[self.block.0].instructions.push(asm_val); asm_val } - pub fn branch(self, to_block: Block, phi_value: Option) -> Value { - let br_val = Value::new_instruction(self.context, Instruction::Branch(to_block)); + pub fn branch( + self, + to_block: Block, + phi_value: Option, + span_md_idx: Option, + ) -> Value { + let br_val = + Value::new_instruction(self.context, Instruction::Branch(to_block), span_md_idx); phi_value .into_iter() .for_each(|pv| to_block.add_phi(self.context, self.block, pv)); @@ -264,9 +279,17 @@ impl<'a> InstructionInserter<'a> { br_val } - pub fn call(self, function: Function, args: &[Value]) -> Value { - let call_val = - Value::new_instruction(self.context, Instruction::Call(function, args.to_vec())); + pub fn call( + self, + function: Function, + args: &[Value], + span_md_idx: Option, + ) -> Value { + let call_val = Value::new_instruction( + self.context, + Instruction::Call(function, args.to_vec()), + span_md_idx, + ); self.context.blocks[self.block.0] .instructions .push(call_val); @@ -279,6 +302,7 @@ impl<'a> InstructionInserter<'a> { true_block: Block, false_block: Block, phi_value: Option, + span_md_idx: Option, ) -> Value { let cbr_val = Value::new_instruction( self.context, @@ -287,6 +311,7 @@ impl<'a> InstructionInserter<'a> { true_block, false_block, }, + span_md_idx, ); phi_value.into_iter().for_each(|pv| { true_block.add_phi(self.context, self.block, pv); @@ -296,7 +321,13 @@ impl<'a> InstructionInserter<'a> { cbr_val } - pub fn extract_element(self, array: Value, ty: Aggregate, index_val: Value) -> Value { + pub fn extract_element( + self, + array: Value, + ty: Aggregate, + index_val: Value, + span_md_idx: Option, + ) -> Value { let extract_element_val = Value::new_instruction( self.context, Instruction::ExtractElement { @@ -304,6 +335,7 @@ impl<'a> InstructionInserter<'a> { ty, index_val, }, + span_md_idx, ); self.context.blocks[self.block.0] .instructions @@ -311,7 +343,13 @@ impl<'a> InstructionInserter<'a> { extract_element_val } - pub fn extract_value(self, aggregate: Value, ty: Aggregate, indices: Vec) -> Value { + pub fn extract_value( + self, + aggregate: Value, + ty: Aggregate, + indices: Vec, + span_md_idx: Option, + ) -> Value { let extract_value_val = Value::new_instruction( self.context, Instruction::ExtractValue { @@ -319,6 +357,7 @@ impl<'a> InstructionInserter<'a> { ty, indices, }, + span_md_idx, ); self.context.blocks[self.block.0] .instructions @@ -326,8 +365,9 @@ impl<'a> InstructionInserter<'a> { extract_value_val } - pub fn get_ptr(self, ptr: Pointer) -> Value { - let get_ptr_val = Value::new_instruction(self.context, Instruction::GetPointer(ptr)); + pub fn get_ptr(self, ptr: Pointer, span_md_idx: Option) -> Value { + let get_ptr_val = + Value::new_instruction(self.context, Instruction::GetPointer(ptr), span_md_idx); self.context.blocks[self.block.0] .instructions .push(get_ptr_val); @@ -340,6 +380,7 @@ impl<'a> InstructionInserter<'a> { ty: Aggregate, value: Value, index_val: Value, + span_md_idx: Option, ) -> Value { let insert_val = Value::new_instruction( self.context, @@ -349,6 +390,7 @@ impl<'a> InstructionInserter<'a> { value, index_val, }, + span_md_idx, ); self.context.blocks[self.block.0] .instructions @@ -362,6 +404,7 @@ impl<'a> InstructionInserter<'a> { ty: Aggregate, value: Value, indices: Vec, + span_md_idx: Option, ) -> Value { let insert_val = Value::new_instruction( self.context, @@ -371,6 +414,7 @@ impl<'a> InstructionInserter<'a> { value, indices, }, + span_md_idx, ); self.context.blocks[self.block.0] .instructions @@ -378,23 +422,32 @@ impl<'a> InstructionInserter<'a> { insert_val } - pub fn load(self, ptr: Pointer) -> Value { - let load_val = Value::new_instruction(self.context, Instruction::Load(ptr)); + pub fn load(self, ptr: Pointer, span_md_idx: Option) -> Value { + let load_val = Value::new_instruction(self.context, Instruction::Load(ptr), span_md_idx); self.context.blocks[self.block.0] .instructions .push(load_val); load_val } - pub fn ret(self, value: Value, ty: Type) -> Value { - let ret_val = Value::new_instruction(self.context, Instruction::Ret(value, ty)); + pub fn ret(self, value: Value, ty: Type, span_md_idx: Option) -> Value { + let ret_val = + Value::new_instruction(self.context, Instruction::Ret(value, ty), span_md_idx); self.context.blocks[self.block.0].instructions.push(ret_val); ret_val } - pub fn store(self, ptr: Pointer, stored_val: Value) -> Value { - let store_val = - Value::new_instruction(self.context, Instruction::Store { ptr, stored_val }); + pub fn store( + self, + ptr: Pointer, + stored_val: Value, + span_md_idx: Option, + ) -> Value { + let store_val = Value::new_instruction( + self.context, + Instruction::Store { ptr, stored_val }, + span_md_idx, + ); self.context.blocks[self.block.0] .instructions .push(store_val); diff --git a/sway-ir/src/lib.rs b/sway-ir/src/lib.rs index a54fc036ef6..088142759b0 100644 --- a/sway-ir/src/lib.rs +++ b/sway-ir/src/lib.rs @@ -51,6 +51,8 @@ pub mod instruction; pub use instruction::*; pub mod irtype; pub use irtype::*; +pub mod metadata; +pub use metadata::*; pub mod module; pub use module::*; pub mod optimize; diff --git a/sway-ir/src/metadata.rs b/sway-ir/src/metadata.rs new file mode 100644 index 00000000000..a1895b60075 --- /dev/null +++ b/sway-ir/src/metadata.rs @@ -0,0 +1,82 @@ +/// Associated metadata attached mostly to values. +/// +/// Each value (instruction, function argument or constant) has associated metadata which helps +/// describe properties which aren't required for code generation, but help with other +/// introspective tools (e.g., the debugger) or compiler error messages. +/// +/// NOTE: At the moment the Spans contain a source string and optional path. Any spans with no +/// path are ignored/rejected by this module. The source string is not (de)serialised and so the +/// string is assumed to always represent the entire contents of the file path. +use std::sync::Arc; + +use sway_types::span::Span; + +use crate::context::Context; + +pub enum Metadatum { + FileLocation(Arc, Arc), + Span { + loc_idx: MetadataIndex, + start: usize, + end: usize, + }, +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MetadataIndex(pub generational_arena::Index); + +impl MetadataIndex { + pub fn from_span(context: &mut Context, span: &Span) -> Option { + // Search for an existing matching path, otherwise insert it. + span.path.as_ref().map(|path_buf| { + let loc_idx = context + .metadata + .iter() + .find_map(|(idx, md)| match md { + Metadatum::FileLocation(file_loc_path_buf, _) + if Arc::ptr_eq(path_buf, file_loc_path_buf) => + { + Some(MetadataIndex(idx)) + } + _otherwise => None, + }) + .unwrap_or_else(|| { + // This is assuming that the string in this span represents the entire file + // found at `path_buf`. + MetadataIndex(context.metadata.insert(Metadatum::FileLocation( + path_buf.clone(), + span.span.input().clone(), + ))) + }); + + MetadataIndex(context.metadata.insert(Metadatum::Span { + loc_idx, + start: span.start(), + end: span.end(), + })) + }) + } + + pub fn to_span(&self, context: &Context) -> Result { + match &context.metadata[self.0] { + Metadatum::Span { + loc_idx, + start, + end, + } => { + let (path, src) = match &context.metadata[loc_idx.0] { + Metadatum::FileLocation(path, src) => Ok((path.clone(), src.clone())), + _otherwise => { + Err("Metadata cannot be converted to a file location.".to_owned()) + } + }?; + Ok(Span { + span: pest::Span::new(src, *start, *end) + .ok_or_else(|| "Cannot create span from invalid metadata.".to_owned())?, + path: Some(path), + }) + } + _otherwise => Err("Metadata cannot be converted to Span.".to_owned()), + } + } +} diff --git a/sway-ir/src/module.rs b/sway-ir/src/module.rs index efa30ec879f..74d330593e1 100644 --- a/sway-ir/src/module.rs +++ b/sway-ir/src/module.rs @@ -17,7 +17,7 @@ pub struct Module(pub generational_arena::Index); #[doc(hidden)] pub struct ModuleContent { - pub name: String, + pub name: String, // XXX unused; remove me pub kind: Kind, pub functions: Vec, pub globals: HashMap, diff --git a/sway-ir/src/optimize/constants.rs b/sway-ir/src/optimize/constants.rs index f8283807951..78b001272d0 100644 --- a/sway-ir/src/optimize/constants.rs +++ b/sway-ir/src/optimize/constants.rs @@ -9,7 +9,7 @@ use crate::{ context::Context, function::Function, instruction::Instruction, - value::{Value, ValueContent}, + value::{Value, ValueContent, ValueDatum}, }; /// Find constant expressions which can be reduced to fewer opterations. @@ -32,7 +32,7 @@ fn combine_const_insert_values(context: &mut Context, function: &Function) -> bo let candidate = function .instruction_iter(context) .find_map(|(block, ins_val)| { - match &context.values[ins_val.0] { + match &context.values[ins_val.0].value { // We only want inject this constant value into a constant aggregate declaration, // not another `insert_value` instruction. // @@ -40,15 +40,15 @@ fn combine_const_insert_values(context: &mut Context, function: &Function) -> bo // but we'd have to be careful that this constant value isn't clobbered by the // chain. It's simpler to just combine the instruction which modifies the // aggregate directly and then to iterate. - ValueContent::Instruction(Instruction::InsertValue { + ValueDatum::Instruction(Instruction::InsertValue { aggregate, ty: _, value, indices, }) if value.is_constant(context) && matches!( - &context.values[aggregate.0], - ValueContent::Constant(Constant { + &context.values[aggregate.0].value, + ValueDatum::Constant(Constant { value: ConstantValue::Struct(_), .. }), @@ -88,14 +88,17 @@ fn combine_const_aggregate_field( indices: &[u64], ) -> Value { // Create a copy of the aggregate constant and inserted value. - let mut new_aggregate = match &context.values[aggregate.0] { - ValueContent::Constant(c) => c.clone(), + let (mut new_aggregate, span_md_idx) = match &context.values[aggregate.0] { + ValueContent { + value: ValueDatum::Constant(c), + span_md_idx, + } => (c.clone(), *span_md_idx), _otherwise => { unreachable!("BUG! Invalid aggregate parameter to combine_const_insert_value()") } }; - let const_value = match &context.values[const_value.0] { - ValueContent::Constant(c) => c.clone(), + let const_value = match &context.values[const_value.0].value { + ValueDatum::Constant(c) => c.clone(), _otherwise => { unreachable!("BUG! Invalid const_value parameter to combine_const_insert_value()") } @@ -105,7 +108,7 @@ fn combine_const_aggregate_field( inject_constant_into_aggregate(&mut new_aggregate, const_value, indices); // Replace the old aggregate with the new aggregate. - let new_aggregate_value = Value::new_constant(context, new_aggregate); + let new_aggregate_value = Value::new_constant(context, new_aggregate, span_md_idx); function.replace_value(context, aggregate, new_aggregate_value, None); // Remove the old aggregate from the context. diff --git a/sway-ir/src/optimize/inline.rs b/sway-ir/src/optimize/inline.rs index e4ee9fb6094..eed12eecf23 100644 --- a/sway-ir/src/optimize/inline.rs +++ b/sway-ir/src/optimize/inline.rs @@ -11,7 +11,7 @@ use crate::{ function::Function, instruction::Instruction, pointer::Pointer, - value::{Value, ValueContent}, + value::{Value, ValueContent, ValueDatum}, }; /// Inline all calls made from a specific function, effectively removing all `Call` instructions. @@ -27,8 +27,8 @@ pub fn inline_all_function_calls( // Find the next call site. let call_data = function .instruction_iter(context) - .find_map(|(block, call_val)| match context.values[call_val.0] { - ValueContent::Instruction(Instruction::Call(inlined_function, _)) => { + .find_map(|(block, call_val)| match context.values[call_val.0].value { + ValueDatum::Instruction(Instruction::Call(inlined_function, _)) => { Some((block, call_val, inlined_function)) } _ => None, @@ -81,8 +81,8 @@ pub fn inline_function_call( let mut value_map = HashMap::new(); // Add the mapping from argument values in the inlined function to the args passed to the call. - if let ValueContent::Instruction(Instruction::Call(_, passed_vals)) = - &context.values[call_site.0] + if let ValueDatum::Instruction(Instruction::Call(_, passed_vals)) = + &context.values[call_site.0].value { for (arg_val, passed_val) in context.functions[inlined_function.0] .arguments @@ -150,8 +150,8 @@ pub fn inline_function_call( for old_block in inlined_blocks { let new_block = block_map.get(&old_block).unwrap(); let old_phi_val = old_block.get_phi(context); - if let ValueContent::Instruction(Instruction::Phi(pairs)) = - context.values[old_phi_val.0].clone() + if let ValueDatum::Instruction(Instruction::Phi(pairs)) = + context.values[old_phi_val.0].value.clone() { for (from_block, phi_value) in pairs { new_block.add_phi( @@ -191,7 +191,11 @@ fn inline_instruction( // // We need to clone the instruction here, which is unfortunate. Maybe in the future we // restructure instructions somehow, so we don't need a persistent `&Context` to access them. - if let ValueContent::Instruction(old_ins) = context.values[instruction.0].clone() { + if let ValueContent { + value: ValueDatum::Instruction(old_ins), + span_md_idx, + } = context.values[instruction.0].clone() + { let new_ins = match old_ins { Instruction::AsmBlock(asm, args) => { let new_args = args @@ -203,17 +207,24 @@ fn inline_instruction( .collect(); // We can re-use the old asm block with the updated args. - new_block.ins(context).asm_block_from_asm(asm, new_args) + new_block + .ins(context) + .asm_block_from_asm(asm, new_args, span_md_idx) } // For `br` and `cbr` below we don't need to worry about the phi values, they're // adjusted later in `inline_function_call()`. - Instruction::Branch(b) => new_block.ins(context).branch(map_block(b), None), + Instruction::Branch(b) => { + new_block + .ins(context) + .branch(map_block(b), None, span_md_idx) + } Instruction::Call(f, args) => new_block.ins(context).call( f, args.iter() .map(|old_val: &Value| map_value(*old_val)) .collect::>() .as_slice(), + span_md_idx, ), Instruction::ConditionalBranch { cond_value, @@ -224,22 +235,30 @@ fn inline_instruction( map_block(true_block), map_block(false_block), None, + span_md_idx, ), Instruction::ExtractElement { array, ty, index_val, - } => new_block - .ins(context) - .extract_element(map_value(array), ty, map_value(index_val)), + } => new_block.ins(context).extract_element( + map_value(array), + ty, + map_value(index_val), + span_md_idx, + ), Instruction::ExtractValue { aggregate, ty, indices, - } => new_block - .ins(context) - .extract_value(map_value(aggregate), ty, indices), - Instruction::GetPointer(ptr) => new_block.ins(context).get_ptr(map_ptr(ptr)), + } => { + new_block + .ins(context) + .extract_value(map_value(aggregate), ty, indices, span_md_idx) + } + Instruction::GetPointer(ptr) => { + new_block.ins(context).get_ptr(map_ptr(ptr), span_md_idx) + } Instruction::InsertElement { array, ty, @@ -250,6 +269,7 @@ fn inline_instruction( ty, map_value(value), map_value(index_val), + span_md_idx, ), Instruction::InsertValue { aggregate, @@ -261,15 +281,20 @@ fn inline_instruction( ty, map_value(value), indices, + span_md_idx, ), - Instruction::Load(ptr) => new_block.ins(context).load(map_ptr(ptr)), + Instruction::Load(ptr) => new_block.ins(context).load(map_ptr(ptr), span_md_idx), // We convert `ret` to `br post_block` and add the returned value as a phi value. - Instruction::Ret(val, _) => new_block - .ins(context) - .branch(*post_block, Some(map_value(val))), - Instruction::Store { ptr, stored_val } => new_block - .ins(context) - .store(map_ptr(ptr), map_value(stored_val)), + Instruction::Ret(val, _) => { + new_block + .ins(context) + .branch(*post_block, Some(map_value(val)), span_md_idx) + } + Instruction::Store { ptr, stored_val } => { + new_block + .ins(context) + .store(map_ptr(ptr), map_value(stored_val), span_md_idx) + } // NOTE: We're not translating the phi value yet, since this is the single instance of // use of a value which may not be mapped yet -- a branch from a subsequent block, diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index a1d7d239932..4882df96cf1 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -19,8 +19,12 @@ pub fn parse(input: &str) -> Result { // ------------------------------------------------------------------------------------------------- mod ir_builder { + use std::path::PathBuf; + use sway_types::{ident::Ident, span::Span}; + type MdIdxRef = u64; + peg::parser! { pub(in crate::parser) grammar parser() for str { pub(in crate::parser) rule ir_descrs() -> IrAstModule @@ -29,11 +33,12 @@ mod ir_builder { } rule script() -> IrAstModule - = "script" _ name:id() "{" _ fn_decls:fn_decl()* "}" _ { + = "script" _ name:id() "{" _ fn_decls:fn_decl()* "}" _ metadata:metadata_decl()* { IrAstModule { name, kind: crate::module::Kind::Script, - fn_decls + fn_decls, + metadata } } @@ -51,9 +56,9 @@ mod ir_builder { } } - rule fn_arg() -> (IrAstTy, String) - = name:id() ":" _ ty:ast_ty() { - (ty, name) + rule fn_arg() -> (IrAstTy, String, Option) + = name:id() mdi:metadata_idx()? ":" _ ty:ast_ty() { + (ty, name, mdi) } rule fn_local() -> (IrAstTy, String, bool, Option) @@ -75,10 +80,11 @@ mod ir_builder { } rule instr_decl() -> IrAstInstruction - = value_name:value_assign()? op:operation() { + = value_name:value_assign()? op:operation() meta_idx:comma_metadata_idx()? { IrAstInstruction { value_name, op, + meta_idx, } } @@ -87,6 +93,16 @@ mod ir_builder { name } + rule metadata_idx() -> MdIdxRef + = "!" i:decimal() { + i + } + + rule comma_metadata_idx() -> MdIdxRef + = "," _ mdi:metadata_idx() { + mdi + } + rule operation() -> IrAstOperation = op_asm() / op_branch() @@ -104,10 +120,10 @@ mod ir_builder { / op_store() rule op_asm() -> IrAstOperation - = "asm" _ "(" _ args:(asm_arg() ** comma()) ")" _ ret:asm_ret()? "{" _ + = "asm" _ "(" _ args:(asm_arg() ** comma()) ")" _ ret:asm_ret()? meta_idx:comma_metadata_idx()? "{" _ ops:asm_op()* "}" _ { - IrAstOperation::Asm(args, ret, ops) + IrAstOperation::Asm(args, ret, ops, meta_idx) } rule op_branch() -> IrAstOperation @@ -194,11 +210,12 @@ mod ir_builder { } rule asm_op() -> IrAstAsmOp - = name:id_id() args:asm_op_arg()* imm:asm_op_arg_imm()? { + = name:id_id() args:asm_op_arg()* imm:asm_op_arg_imm()? meta_idx:comma_metadata_idx()? { IrAstAsmOp { name, args, - imm + imm, + meta_idx } } @@ -215,7 +232,15 @@ mod ir_builder { }) } - rule constant() -> IrAstConstValue + rule constant() -> IrAstConst + = value:constant_value() meta_idx:metadata_idx()? { + IrAstConst { + value, + meta_idx + } + } + + rule constant_value() -> IrAstConstValue = "()" _ { IrAstConstValue::Unit } / "true" _ { IrAstConstValue::Bool(true) } / "false" _ { IrAstConstValue::Bool(false) } @@ -247,7 +272,7 @@ mod ir_builder { rule array_const() -> IrAstConstValue = "[" _ els:(field_or_element_const() ++ comma()) "]" _ { let el_ty = els[0].0.clone(); - let els = els.into_iter().map(|(_, cv)| cv).collect(); + let els = els.into_iter().map(|(_, cv)| cv).collect::>(); IrAstConstValue::Array(el_ty, els) } @@ -256,12 +281,12 @@ mod ir_builder { IrAstConstValue::Struct(flds) } - rule field_or_element_const() -> (IrAstTy, IrAstConstValue) + rule field_or_element_const() -> (IrAstTy, IrAstConst) = ty:ast_ty() cv:constant() { (ty, cv) } / ty:ast_ty() "undef" _ { - (ty.clone(), IrAstConstValue::Undef(ty)) + (ty.clone(), IrAstConst { value: IrAstConstValue::Undef(ty), meta_idx: None }) } rule ast_ty() -> IrAstTy @@ -302,6 +327,19 @@ mod ir_builder { }) } + rule metadata_decl() -> (MdIdxRef, IrMetadatum) + = "!" idx:decimal() "=" _ item:metadata_item() { + (idx, item) + } + + rule metadata_item() -> IrMetadatum + = "filepath" _ ['"'] path:$(([^ '"' | '\\'] / ['\\'] ['\\' | '"' ])+) ['"'] _ { + IrMetadatum::FilePath(PathBuf::from(path)) + } + / "span" _ "!" li:decimal() s:decimal() e:decimal() { + IrMetadatum::Span { loc_idx: li, start: s as usize, end: e as usize } + } + rule id_char0() = quiet!{ ['A'..='Z' | 'a'..='z' | '_'] } @@ -343,6 +381,7 @@ mod ir_builder { context::Context, function::Function, irtype::{Aggregate, Type}, + metadata::{MetadataIndex, Metadatum}, module::{Kind, Module}, pointer::Pointer, value::Value, @@ -353,12 +392,13 @@ mod ir_builder { name: String, kind: Kind, fn_decls: Vec, + metadata: Vec<(MdIdxRef, IrMetadatum)>, } #[derive(Debug)] struct IrAstFnDecl { name: String, - args: Vec<(IrAstTy, String)>, + args: Vec<(IrAstTy, String, Option)>, ret_type: IrAstTy, locals: Vec<(IrAstTy, String, bool, Option)>, blocks: Vec, @@ -374,6 +414,7 @@ mod ir_builder { struct IrAstInstruction { value_name: Option, op: IrAstOperation, + meta_idx: Option, } #[derive(Debug)] @@ -382,11 +423,12 @@ mod ir_builder { Vec<(Ident, Option)>, Option, Vec, + Option, ), Br(String), Call(String, Vec), Cbr(String, String, String), - Const(IrAstConstValue), + Const(IrAstConst), ExtractElement(String, IrAstTy, String), ExtractValue(String, IrAstTy, Vec), GetPtr(String), @@ -398,6 +440,12 @@ mod ir_builder { Store(String, String), } + #[derive(Debug)] + struct IrAstConst { + value: IrAstConstValue, + meta_idx: Option, + } + #[derive(Debug)] enum IrAstConstValue { Undef(IrAstTy), @@ -406,14 +454,14 @@ mod ir_builder { B256([u8; 32]), Number(u64), String(String), - Array(IrAstTy, Vec), - Struct(Vec<(IrAstTy, IrAstConstValue)>), + Array(IrAstTy, Vec), + Struct(Vec<(IrAstTy, IrAstConst)>), } #[derive(Debug)] enum IrAstAsmArgInit { Var(String), - Imm(IrAstConstValue), + Imm(IrAstConst), } #[derive(Debug)] @@ -421,6 +469,7 @@ mod ir_builder { name: Ident, args: Vec, imm: Option, + meta_idx: Option, } impl IrAstConstValue { @@ -436,7 +485,7 @@ mod ir_builder { IrAstConstValue::Number(n) => Constant::new_uint(64, *n), IrAstConstValue::String(s) => Constant::new_string(s.clone()), IrAstConstValue::Array(el_ty, els) => { - let els: Vec<_> = els.iter().map(|cv| cv.as_constant(context)).collect(); + let els: Vec<_> = els.iter().map(|cv| cv.value.as_constant(context)).collect(); let el_ty = el_ty.to_ir_type(context); let array = Aggregate::new_array(context, el_ty, els.len() as u64); Constant::new_array(&array, els) @@ -445,7 +494,7 @@ mod ir_builder { // To Make a Constant I need to create an aggregate, which requires a context. let (types, fields): (Vec<_>, Vec<_>) = flds .iter() - .map(|(ty, cv)| (ty.to_ir_type(context), cv.as_constant(context))) + .map(|(ty, cv)| (ty.to_ir_type(context), cv.value.as_constant(context))) .unzip(); let aggregate = Aggregate::new_struct(context, None, types); Constant::new_struct(&aggregate, fields) @@ -453,21 +502,21 @@ mod ir_builder { } } - fn as_value(&self, context: &mut Context) -> Value { + fn as_value(&self, context: &mut Context, span_md_idx: Option) -> Value { match self { IrAstConstValue::Undef(_) => unreachable!("Can't convert 'undef' to a value."), - IrAstConstValue::Unit => Constant::get_unit(context), - IrAstConstValue::Bool(b) => Constant::get_bool(context, *b), - IrAstConstValue::B256(bs) => Constant::get_b256(context, *bs), - IrAstConstValue::Number(n) => Constant::get_uint(context, 64, *n), - IrAstConstValue::String(s) => Constant::get_string(context, s.clone()), + IrAstConstValue::Unit => Constant::get_unit(context, span_md_idx), + IrAstConstValue::Bool(b) => Constant::get_bool(context, *b, span_md_idx), + IrAstConstValue::B256(bs) => Constant::get_b256(context, *bs, span_md_idx), + IrAstConstValue::Number(n) => Constant::get_uint(context, 64, *n, span_md_idx), + IrAstConstValue::String(s) => Constant::get_string(context, s.clone(), span_md_idx), IrAstConstValue::Array(..) => { let array_const = self.as_constant(context); - Constant::get_array(context, array_const) + Constant::get_array(context, array_const, span_md_idx) } IrAstConstValue::Struct(_) => { let struct_const = self.as_constant(context); - Constant::get_struct(context, struct_const) + Constant::get_struct(context, struct_const, span_md_idx) } } } @@ -516,16 +565,26 @@ mod ir_builder { } } + #[derive(Debug)] + enum IrMetadatum { + FilePath(PathBuf), + Span { + loc_idx: MdIdxRef, + start: usize, + end: usize, + }, + } + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - use std::collections::HashMap; - use std::iter::FromIterator; + use std::{collections::HashMap, iter::FromIterator, sync::Arc}; pub(super) fn build_context(ir_ast_mod: IrAstModule) -> Result { let mut ctx = Context::default(); let module = Module::new(&mut ctx, ir_ast_mod.kind, &ir_ast_mod.name); + let md_map = build_metadata_map(&mut ctx, &ir_ast_mod.metadata); for fn_decl in ir_ast_mod.fn_decls { - build_add_fn_decl(&mut ctx, module, fn_decl)?; + build_add_fn_decl(&mut ctx, module, fn_decl, &md_map)?; } Ok(ctx) } @@ -534,11 +593,18 @@ mod ir_builder { context: &mut Context, module: Module, fn_decl: IrAstFnDecl, + md_map: &HashMap, ) -> Result<(), String> { - let args: Vec<(String, Type)> = fn_decl + let args: Vec<(String, Type, Option)> = fn_decl .args .iter() - .map(|(ty, name)| (name.into(), ty.to_ir_type(context))) + .map(|(ty, name, md_idx)| { + ( + name.into(), + ty.to_ir_type(context), + md_idx.map(|mdi| md_map.get(&mdi).copied().unwrap()), + ) + }) .collect(); let ret_type = fn_decl.ret_type.to_ir_type(context); let func = Function::new( @@ -553,7 +619,7 @@ mod ir_builder { // Gather all the (new) arg values by name into a map. let mut arg_map: HashMap = - HashMap::from_iter(args.into_iter().map(|(name, _)| { + HashMap::from_iter(args.into_iter().map(|(name, _, _)| { let arg_val = func.get_arg(context, &name).unwrap(); (name, arg_val) })); @@ -561,7 +627,7 @@ mod ir_builder { for (ty, name, is_mutable, initializer) in fn_decl.locals { let initializer = initializer.map(|const_init| { if let IrAstOperation::Const(val) = const_init { - val.as_constant(context) + val.value.as_constant(context) } else { unreachable!("BUG! Initializer must be a const value."); } @@ -586,7 +652,14 @@ mod ir_builder { })); for block in fn_decl.blocks { - build_add_block_instructions(context, block, &named_blocks, &ptr_map, &mut arg_map); + build_add_block_instructions( + context, + block, + &named_blocks, + &ptr_map, + &mut arg_map, + md_map, + ); } Ok(()) } @@ -597,34 +670,53 @@ mod ir_builder { named_blocks: &HashMap, ptr_map: &HashMap, val_map: &mut HashMap, + md_map: &HashMap, ) { let block = named_blocks.get(&ir_block.label).unwrap(); for ins in ir_block.instructions { + let opt_ins_md_idx = ins.meta_idx.map(|mdi| md_map.get(&mdi).unwrap()).copied(); let ins_val = match ins.op { - IrAstOperation::Asm(args, return_name, ops) => { + IrAstOperation::Asm(args, return_name, ops, meta_idx) => { let args = args .into_iter() .map(|(name, opt_init)| AsmArg { name, initializer: opt_init.map(|init| match init { IrAstAsmArgInit::Var(var) => val_map.get(&var).cloned().unwrap(), - IrAstAsmArgInit::Imm(cv) => cv.as_value(context), + IrAstAsmArgInit::Imm(cv) => cv.value.as_value( + context, + md_map.get(cv.meta_idx.as_ref().unwrap()).copied(), + ), }), }) .collect(); let body = ops .into_iter() - .map(|IrAstAsmOp { name, args, imm }| AsmInstruction { - name, - args, - immediate: imm, //: Option, - }) + .map( + |IrAstAsmOp { + name, + args, + imm, + meta_idx, + }| AsmInstruction { + name, + args, + immediate: imm, + span_md_idx: meta_idx + .as_ref() + .map(|meta_idx| md_map.get(meta_idx).copied()) + .flatten(), + }, + ) .collect(); - block.ins(context).asm_block(args, body, return_name) + let md_idx = meta_idx.map(|mdi| md_map.get(&mdi).unwrap()).copied(); + block + .ins(context) + .asm_block(args, body, return_name, md_idx) } IrAstOperation::Br(to_block_name) => { let to_block = named_blocks.get(&to_block_name).unwrap(); - block.ins(context).branch(*to_block, None) + block.ins(context).branch(*to_block, None, opt_ins_md_idx) } IrAstOperation::Call(callee, args) => { let function = context @@ -645,6 +737,7 @@ mod ir_builder { .map(|arg_name| val_map.get(arg_name).unwrap()) .cloned() .collect::>(), + opt_ins_md_idx, ) } IrAstOperation::Cbr(cond_val_name, true_block_name, false_block_name) => { @@ -653,26 +746,31 @@ mod ir_builder { *named_blocks.get(&true_block_name).unwrap(), *named_blocks.get(&false_block_name).unwrap(), None, + opt_ins_md_idx, ) } - IrAstOperation::Const(val) => val.as_value(context), + IrAstOperation::Const(val) => val.value.as_value(context, opt_ins_md_idx), IrAstOperation::ExtractElement(aval, ty, idx) => { let ir_ty = ty.to_ir_aggregate_type(context); block.ins(context).extract_element( *val_map.get(&aval).unwrap(), ir_ty, *val_map.get(&idx).unwrap(), + opt_ins_md_idx, ) } IrAstOperation::ExtractValue(val, ty, idcs) => { let ir_ty = ty.to_ir_aggregate_type(context); - block - .ins(context) - .extract_value(*val_map.get(&val).unwrap(), ir_ty, idcs) - } - IrAstOperation::GetPtr(src_name) => { - block.ins(context).get_ptr(*ptr_map.get(&src_name).unwrap()) + block.ins(context).extract_value( + *val_map.get(&val).unwrap(), + ir_ty, + idcs, + opt_ins_md_idx, + ) } + IrAstOperation::GetPtr(src_name) => block + .ins(context) + .get_ptr(*ptr_map.get(&src_name).unwrap(), opt_ins_md_idx), IrAstOperation::InsertElement(aval, ty, val, idx) => { let ir_ty = ty.to_ir_aggregate_type(context); block.ins(context).insert_element( @@ -680,6 +778,7 @@ mod ir_builder { ir_ty, *val_map.get(&val).unwrap(), *val_map.get(&idx).unwrap(), + opt_ins_md_idx, ) } IrAstOperation::InsertValue(aval, ty, ival, idcs) => { @@ -689,11 +788,12 @@ mod ir_builder { ir_ty, *val_map.get(&ival).unwrap(), idcs, + opt_ins_md_idx, ) } - IrAstOperation::Load(src_name) => { - block.ins(context).load(*ptr_map.get(&src_name).unwrap()) - } + IrAstOperation::Load(src_name) => block + .ins(context) + .load(*ptr_map.get(&src_name).unwrap(), opt_ins_md_idx), IrAstOperation::Phi(pairs) => { for (block_name, val_name) in pairs { block.add_phi( @@ -708,16 +808,55 @@ mod ir_builder { let ty = ty.to_ir_type(context); block .ins(context) - .ret(*val_map.get(&ret_val_name).unwrap(), ty) + .ret(*val_map.get(&ret_val_name).unwrap(), ty, opt_ins_md_idx) } IrAstOperation::Store(stored_val_name, ptr_name) => block.ins(context).store( *ptr_map.get(&ptr_name).unwrap(), *val_map.get(&stored_val_name).unwrap(), + opt_ins_md_idx, ), }; ins.value_name.map(|vn| val_map.insert(vn, ins_val)); } } + + fn build_metadata_map( + context: &mut Context, + ir_metadata: &[(MdIdxRef, IrMetadatum)], + ) -> HashMap { + let mut md_map = ir_metadata + .iter() + .filter_map(|(idx_ref, md)| match md { + IrMetadatum::FilePath(path) => Some((idx_ref, path)), + _otherwise => None, + }) + .fold(HashMap::new(), |mut md_map, (idx_ref, path)| { + let path_content = Arc::from(std::fs::read_to_string(path).unwrap().as_str()); + let md_idx = context.metadata.insert(Metadatum::FileLocation( + Arc::new(path.clone()), + path_content, + )); + md_map.insert(*idx_ref, MetadataIndex(md_idx)); + md_map + }); + + for (idx_ref, md) in ir_metadata { + if let IrMetadatum::Span { + loc_idx, + start, + end, + } = md + { + let span_idx = context.metadata.insert(Metadatum::Span { + loc_idx: md_map.get(loc_idx).copied().unwrap(), + start: *start, + end: *end, + }); + md_map.insert(*idx_ref, MetadataIndex(span_idx)); + } + } + md_map + } } // ------------------------------------------------------------------------------------------------- diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 4317eaf7b58..a0e6e44849c 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -4,6 +4,8 @@ //! crates were assessed but didn't seem to work as well as this simple version, which is quite //! effective. +use std::collections::{BTreeMap, HashMap}; + use crate::{ asm::*, block::Block, @@ -12,9 +14,10 @@ use crate::{ function::{Function, FunctionContent}, instruction::Instruction, irtype::Type, + metadata::{MetadataIndex, Metadatum}, module::{Kind, ModuleContent}, pointer::{Pointer, PointerContent}, - value::{Value, ValueContent}, + value::{Value, ValueContent, ValueDatum}, }; #[derive(Debug)] @@ -76,16 +79,22 @@ impl Doc { /// /// The ouput from this function must always be suitable for [`crate::parser::parse`]. pub fn to_string(context: &Context) -> String { + let mut md_namer = MetadataNamer::new(); context .modules .iter() .fold(Doc::Empty, |doc, (_, module)| { - doc.append(module_to_doc(context, module)) + doc.append(module_to_doc(context, &mut md_namer, module)) }) + .append(metadata_to_doc(context, &md_namer)) .build() } -fn module_to_doc<'a>(context: &'a Context, module: &'a ModuleContent) -> Doc { +fn module_to_doc<'a>( + context: &'a Context, + md_namer: &mut MetadataNamer, + module: &'a ModuleContent, +) -> Doc { Doc::line(Doc::Text(format!( "{} {} {{", match module.kind { @@ -105,6 +114,7 @@ fn module_to_doc<'a>(context: &'a Context, module: &'a ModuleContent) -> Doc { .map(|function| { function_to_doc( context, + md_namer, &mut Namer::new(*function), &context.functions[function.0], ) @@ -118,6 +128,7 @@ fn module_to_doc<'a>(context: &'a Context, module: &'a ModuleContent) -> Doc { fn function_to_doc<'a>( context: &'a Context, + md_namer: &mut MetadataNamer, namer: &mut Namer, function: &'a FunctionContent, ) -> Doc { @@ -139,11 +150,18 @@ fn function_to_doc<'a>( .arguments .iter() .map(|(name, arg_val)| { - let ty = match &context.values[arg_val.0] { - ValueContent::Argument(ty) => ty, + let (ty, span_md_idx) = match &context.values[arg_val.0] { + ValueContent { + value: ValueDatum::Argument(ty), + span_md_idx, + } => (ty, span_md_idx), _ => unreachable!("Unexpected non argument value for function arguments."), }; - Doc::text(format!("{}: {}", name, ty.as_string(context),)) + Doc::text(format!( + "{name}{}: {}", + md_namer.meta_as_string(context, span_md_idx, false), + ty.as_string(context), + )) }) .collect(), )) @@ -180,7 +198,7 @@ fn function_to_doc<'a>( function .blocks .iter() - .map(|block| block_to_doc(context, namer, block)) + .map(|block| block_to_doc(context, md_namer, namer, block)) .collect(), Doc::line(Doc::Empty), ), @@ -191,28 +209,52 @@ fn function_to_doc<'a>( .append(Doc::line(Doc::text("}"))) } -fn block_to_doc<'a>(context: &'a Context, namer: &mut Namer, block: &Block) -> Doc { +fn block_to_doc<'a>( + context: &'a Context, + md_namer: &mut MetadataNamer, + namer: &mut Namer, + block: &Block, +) -> Doc { let block_content = &context.blocks[block.0]; Doc::line(Doc::text(format!("{}:", block_content.label))).append(Doc::List( block_content .instructions .iter() - .map(|ins| instruction_to_doc(context, namer, block, ins)) + .map(|ins| instruction_to_doc(context, md_namer, namer, block, ins)) .collect(), )) } -fn constant_to_doc(context: &Context, namer: &mut Namer, const_val: &Value) -> Doc { - Doc::text_line(format!( - "{} = const {}", - namer.name(context, const_val), - const_val.as_lit_string(context) - )) +fn constant_to_doc( + context: &Context, + md_namer: &mut MetadataNamer, + namer: &mut Namer, + const_val: &Value, +) -> Doc { + if let ValueContent { + value: ValueDatum::Constant(constant), + span_md_idx, + } = &context.values[const_val.0] + { + Doc::text_line(format!( + "{} = const {}{}", + namer.name(context, const_val), + constant.as_lit_string(context), + md_namer.meta_as_string(context, span_md_idx, true), + )) + } else { + unreachable!("Not a constant value.") + } } -fn maybe_constant_to_doc(context: &Context, namer: &mut Namer, maybe_const_val: &Value) -> Doc { +fn maybe_constant_to_doc( + context: &Context, + md_namer: &mut MetadataNamer, + namer: &mut Namer, + maybe_const_val: &Value, +) -> Doc { if maybe_const_val.is_constant(context) { - constant_to_doc(context, namer, maybe_const_val) + constant_to_doc(context, md_namer, namer, maybe_const_val) } else { Doc::Empty } @@ -220,17 +262,18 @@ fn maybe_constant_to_doc(context: &Context, namer: &mut Namer, maybe_const_val: fn maybe_constant_phi_to_doc( context: &Context, + md_namer: &mut MetadataNamer, namer: &mut Namer, caller: &Block, callee: &Block, ) -> Doc { - if let ValueContent::Instruction(Instruction::Phi(pairs)) = - &context.values[callee.get_phi(context).0] + if let ValueDatum::Instruction(Instruction::Phi(pairs)) = + &context.values[callee.get_phi(context).0].value { pairs .iter() .find(|(block, _)| block == caller) - .map(|(_, phi_val)| maybe_constant_to_doc(context, namer, phi_val)) + .map(|(_, phi_val)| maybe_constant_to_doc(context, md_namer, namer, phi_val)) .unwrap_or(Doc::Empty) } else { unreachable!("Phi must be an instruction.") @@ -239,28 +282,31 @@ fn maybe_constant_phi_to_doc( fn instruction_to_doc<'a>( context: &'a Context, + md_namer: &mut MetadataNamer, namer: &mut Namer, block: &Block, ins_value: &'a Value, ) -> Doc { match &context.values[ins_value.0] { - ValueContent::Instruction(instruction) => match instruction { + ValueContent { + value: ValueDatum::Instruction(instruction), + span_md_idx, + } => match instruction { Instruction::AsmBlock(asm, args) => { - asm_block_to_doc(context, namer, ins_value, asm, args) - } - Instruction::Branch(to_block) => { - maybe_constant_phi_to_doc(context, namer, block, to_block).append(Doc::text_line( - format!("br {}", context.blocks[to_block.0].label), - )) + asm_block_to_doc(context, md_namer, namer, ins_value, asm, args, span_md_idx) } + Instruction::Branch(to_block) => maybe_constant_phi_to_doc( + context, md_namer, namer, block, to_block, + ) + .append(Doc::text_line(format!( + "br {}{}", + context.blocks[to_block.0].label, + md_namer.meta_as_string(context, span_md_idx, true) + ))), Instruction::Call(func, args) => args .iter() .fold(Doc::Empty, |doc, arg_val| { - if arg_val.is_constant(context) { - doc.append(constant_to_doc(context, namer, arg_val)) - } else { - doc - } + doc.append(maybe_constant_to_doc(context, md_namer, namer, arg_val)) }) .append(Doc::line( Doc::text(format!( @@ -272,7 +318,11 @@ fn instruction_to_doc<'a>( args.iter() .map(|arg_val| Doc::text(namer.name(context, arg_val))) .collect(), - )), + )) + .append(match span_md_idx { + None => Doc::Empty, + Some(_) => Doc::text(md_namer.meta_as_string(context, span_md_idx, true)), + }), )), Instruction::ConditionalBranch { cond_value, @@ -281,28 +331,28 @@ fn instruction_to_doc<'a>( } => { let true_label = &context.blocks[true_block.0].label; let false_label = &context.blocks[false_block.0].label; - maybe_constant_phi_to_doc(context, namer, block, true_block) - .append(maybe_constant_to_doc(context, namer, cond_value)) + maybe_constant_phi_to_doc(context, md_namer, namer, block, true_block) + .append(maybe_constant_to_doc(context, md_namer, namer, cond_value)) .append(Doc::text_line(format!( - "cbr {}, {}, {}", + "cbr {}, {true_label}, {false_label}{}", namer.name(context, cond_value), - true_label, - false_label + md_namer.meta_as_string(context, span_md_idx, true), ))) } Instruction::ExtractElement { array, ty, index_val, - } => maybe_constant_to_doc(context, namer, index_val).append(Doc::line(Doc::text( - format!( - "{} = extract_element {}, {}, {}", + } => maybe_constant_to_doc(context, md_namer, namer, index_val).append(Doc::line( + Doc::text(format!( + "{} = extract_element {}, {}, {}{}", namer.name(context, ins_value), namer.name(context, array), Type::Array(*ty).as_string(context), namer.name(context, index_val), - ), - ))), + md_namer.meta_as_string(context, span_md_idx, true), + )), + )), Instruction::ExtractValue { aggregate, ty, @@ -317,10 +367,14 @@ fn instruction_to_doc<'a>( .append(Doc::list_sep( indices .iter() - .map(|idx| Doc::text(format!("{}", idx))) + .map(|idx| Doc::text(format!("{idx}"))) .collect(), Doc::Comma, - )), + )) + .append(match span_md_idx { + None => Doc::Empty, + Some(_) => Doc::text(md_namer.meta_as_string(context, span_md_idx, true)), + }), ), Instruction::GetPointer(ptr) => { let name = block @@ -328,9 +382,10 @@ fn instruction_to_doc<'a>( .lookup_local_name(context, ptr) .unwrap(); Doc::text_line(format!( - "{} = get_ptr {}", + "{} = get_ptr {}{}", namer.name(context, ins_value), - ptr.as_string(context, name) + ptr.as_string(context, name), + md_namer.meta_as_string(context, span_md_idx, true), )) } Instruction::InsertElement { @@ -338,24 +393,25 @@ fn instruction_to_doc<'a>( ty, value, index_val, - } => maybe_constant_to_doc(context, namer, array) - .append(maybe_constant_to_doc(context, namer, value)) - .append(maybe_constant_to_doc(context, namer, index_val)) + } => maybe_constant_to_doc(context, md_namer, namer, array) + .append(maybe_constant_to_doc(context, md_namer, namer, value)) + .append(maybe_constant_to_doc(context, md_namer, namer, index_val)) .append(Doc::line(Doc::text(format!( - "{} = insert_element {}, {}, {}, {}", + "{} = insert_element {}, {}, {}, {}{}", namer.name(context, ins_value), namer.name(context, array), Type::Array(*ty).as_string(context), namer.name(context, value), namer.name(context, index_val), + md_namer.meta_as_string(context, span_md_idx, true), )))), Instruction::InsertValue { aggregate, ty, value, indices, - } => maybe_constant_to_doc(context, namer, aggregate) - .append(maybe_constant_to_doc(context, namer, value)) + } => maybe_constant_to_doc(context, md_namer, namer, aggregate) + .append(maybe_constant_to_doc(context, md_namer, namer, value)) .append(Doc::line( Doc::text(format!( "{} = insert_value {}, {}, {}, ", @@ -367,10 +423,14 @@ fn instruction_to_doc<'a>( .append(Doc::list_sep( indices .iter() - .map(|idx| Doc::text(format!("{}", idx))) + .map(|idx| Doc::text(format!("{idx}"))) .collect(), Doc::Comma, - )), + )) + .append(match span_md_idx { + None => Doc::Empty, + Some(_) => Doc::text(md_namer.meta_as_string(context, span_md_idx, true)), + }), )), Instruction::Load(ptr) => { let name = block @@ -378,9 +438,10 @@ fn instruction_to_doc<'a>( .lookup_local_name(context, ptr) .unwrap(); Doc::text_line(format!( - "{} = load {}", + "{} = load {}{}", namer.name(context, ins_value), - ptr.as_string(context, name) + ptr.as_string(context, name), + md_namer.meta_as_string(context, span_md_idx, true), )) } Instruction::Phi(pairs) => { @@ -407,10 +468,11 @@ fn instruction_to_doc<'a>( } } Instruction::Ret(v, t) => { - maybe_constant_to_doc(context, namer, v).append(Doc::text_line(format!( - "ret {} {}", + maybe_constant_to_doc(context, md_namer, namer, v).append(Doc::text_line(format!( + "ret {} {}{}", t.as_string(context), - namer.name(context, v) + namer.name(context, v), + md_namer.meta_as_string(context, span_md_idx, true), ))) } Instruction::Store { ptr, stored_val } => { @@ -418,11 +480,14 @@ fn instruction_to_doc<'a>( .get_function(context) .lookup_local_name(context, ptr) .unwrap(); - maybe_constant_to_doc(context, namer, stored_val).append(Doc::text_line(format!( - "store {}, {}", - namer.name(context, stored_val), - ptr.as_string(context, name), - ))) + maybe_constant_to_doc(context, md_namer, namer, stored_val).append(Doc::text_line( + format!( + "store {}, {}{}", + namer.name(context, stored_val), + ptr.as_string(context, name), + md_namer.meta_as_string(context, span_md_idx, true), + ), + )) } }, _ => unreachable!("Unexpected non instruction for block contents."), @@ -431,10 +496,12 @@ fn instruction_to_doc<'a>( fn asm_block_to_doc( context: &Context, + md_namer: &mut MetadataNamer, namer: &mut Namer, ins_value: &Value, asm: &AsmBlock, args: &[AsmArg], + span_md_idx: &Option, ) -> Doc { let AsmBlockContent { body, return_name, .. @@ -444,7 +511,7 @@ fn asm_block_to_doc( Doc::Empty, |doc, AsmArg { initializer, .. }| match initializer { Some(init_val) if init_val.is_constant(context) => { - doc.append(constant_to_doc(context, namer, init_val)) + doc.append(constant_to_doc(context, md_namer, namer, init_val)) } _otherwise => doc, }, @@ -464,7 +531,10 @@ fn asm_block_to_doc( .collect(), )) .append(match return_name { - Some(rn) => Doc::text(format!(" -> {} {{", rn)), + Some(rn) => Doc::text(format!( + " -> {rn}{} {{", + md_namer.meta_as_string(context, span_md_idx, true) + )), None => Doc::text(" {"), }), )) @@ -477,6 +547,7 @@ fn asm_block_to_doc( name, args, immediate, + span_md_idx, }| { Doc::line( Doc::text(format!("{:6} ", name.as_str())).append( @@ -485,9 +556,12 @@ fn asm_block_to_doc( Doc::text(" "), ) .append(match immediate { - Some(imm_str) => Doc::text(format!(" {}", imm_str)), + Some(imm_str) => Doc::text(format!(" {imm_str}")), None => Doc::Empty, - }), + }) + .append(Doc::text( + md_namer.meta_as_string(context, span_md_idx, true), + )), ), ) }, @@ -498,13 +572,29 @@ fn asm_block_to_doc( .append(Doc::text_line("}")) } -impl Value { - fn as_lit_string(&self, context: &Context) -> String { - if let ValueContent::Constant(c) = &context.values[self.0] { - c.as_lit_string(context) - } else { - unreachable!("Not a literal value.") - } +fn metadata_to_doc(context: &Context, md_namer: &MetadataNamer) -> Doc { + let md_lines = md_namer + .values_sorted() + .filter_map(|(ref_idx, md_idx)| { + match &context.metadata[md_idx.0] { + Metadatum::FileLocation(path, _) => Some(format!("!{ref_idx} = filepath {path:?}")), + Metadatum::Span { + loc_idx, + start, + end, + } => md_namer + .get(loc_idx) + .map(|loc_ref_idx| format!("!{ref_idx} = span !{loc_ref_idx} {start} {end}")), + } + .map(&Doc::text_line) + }) + .collect::>(); + + // We want to add an empty line only when there are metadata. + if md_lines.is_empty() { + Doc::Empty + } else { + Doc::line(Doc::Empty).append(Doc::List(md_lines)) } } @@ -518,11 +608,11 @@ impl Constant { ConstantValue::B256(bs) => format!( "b256 0x{}", bs.iter() - .map(|b| format!("{:02x}", b)) + .map(|b| format!("{b:02x}")) .collect::>() .concat() ), - ConstantValue::String(s) => format!("{} \"{}\"", self.ty.as_string(context), s), + ConstantValue::String(s) => format!("{} \"{s}\"", self.ty.as_string(context)), ConstantValue::Array(elems) => format!( "{} [{}]", self.ty.as_string(context), @@ -549,53 +639,114 @@ impl Pointer { fn as_string(&self, context: &Context, name: &str) -> String { let PointerContent { ty, is_mutable, .. } = &context.pointers[self.0]; let mut_tag = if *is_mutable { "mut " } else { "" }; - format!("{}ptr {} {}", mut_tag, ty.as_string(context), name) + format!("{mut_tag}ptr {} {name}", ty.as_string(context)) } - - //fn as_string_no_type(&self, context: &Context, name: &str) -> String { - // let PointerContent { is_mutable, .. } = &context.pointers[self.0]; - // let mut_tag = if *is_mutable { "mut " } else { "" }; - // format!("{}ptr {}", mut_tag, name) - //} } struct Namer { function: Function, - next_idx: u64, - names: std::collections::HashMap, + + names: HashMap, + next_value_idx: u64, } impl Namer { fn new(function: Function) -> Self { Namer { function, - next_idx: 0, - names: std::collections::HashMap::new(), + names: HashMap::new(), + next_value_idx: 0, } } fn name(&mut self, context: &Context, value: &Value) -> String { - match &context.values[value.0] { - ValueContent::Argument(_) => self + match &context.values[value.0].value { + ValueDatum::Argument(_) => self .function .lookup_arg_name(context, value) .cloned() .unwrap_or_else(|| self.default_name(value)), - ValueContent::Constant(_) => self.default_name(value), - ValueContent::Instruction(_) => self.default_name(value), + ValueDatum::Constant(_) => self.default_name(value), + ValueDatum::Instruction(_) => self.default_name(value), } } fn default_name(&mut self, value: &Value) -> String { self.names.get(value).cloned().unwrap_or_else(|| { - let new_name = format!("v{}", self.next_idx); - self.next_idx += 1; + let new_name = format!("v{}", self.next_value_idx); + self.next_value_idx += 1; self.names.insert(*value, new_name.clone()); new_name }) } } +struct MetadataNamer { + md_map: BTreeMap, + next_md_idx: u64, +} + +impl MetadataNamer { + // Use Default instead? + fn new() -> Self { + MetadataNamer { + md_map: BTreeMap::new(), + next_md_idx: 0, + } + } + + fn values_sorted(&self) -> impl Iterator { + let mut items = self + .md_map + .clone() + .into_iter() + .map(|(a, b)| (b, a)) + .collect::>(); + items.sort_unstable(); + items.into_iter() + } + + fn get(&self, md_idx: &MetadataIndex) -> Option { + self.md_map.get(md_idx).copied() + } + + // This method is how we introduce 'valid' metadata to the namer, as only valid metadata are + // printed at the end. Since metadata are stored globally to the context there may be a bunch + // in there which aren't relevant (e.g., library code). Hopefully this will go away when the + // Sway compiler becomes properly modular and eschews all the inlining it does. + // + // So, we insert a reference index into the namer whenever we see a new metadata index passed + // here. But we also need to recursively 'validate' any other metadata referred to, i.e., the + // file paths. It's done in `get_or_add()` below. + fn meta_as_string( + &mut self, + context: &Context, + md_idx: &Option, + comma_prefix: bool, + ) -> String { + md_idx + .map(|md_idx| { + // We prefix always with a space here, comma or not, so it can be used up against + // its owner. + let idx = self.get_or_add(context, &md_idx); + format!("{} !{idx}", if comma_prefix { "," } else { "" }) + }) + .unwrap_or_else(|| "".to_owned()) + } + + fn get_or_add(&mut self, context: &Context, md_idx: &MetadataIndex) -> u64 { + if let Metadatum::Span { loc_idx, .. } = &context.metadata[md_idx.0] { + self.get_or_add(context, loc_idx); + } + self.md_map.get(md_idx).copied().unwrap_or_else(|| { + let new_idx = self.next_md_idx; + self.next_md_idx += 1; + self.md_map.insert(*md_idx, new_idx); + new_idx + }) + } +} + /// There will be a much more efficient way to do this, but for now this will do. fn build_doc(doc: Doc, indent: i64) -> String { match doc { diff --git a/sway-ir/src/value.rs b/sway-ir/src/value.rs index 49cdffde0a5..6f30b3ba3b2 100644 --- a/sway-ir/src/value.rs +++ b/sway-ir/src/value.rs @@ -6,7 +6,12 @@ //! Like most IR data structures they are `Copy` and cheap to pass around by value. They are //! therefore also easy to replace, a common practise for optimization passes. -use crate::{constant::Constant, context::Context, instruction::Instruction, irtype::Type}; +use sway_types::span::Span; + +use crate::{ + constant::Constant, context::Context, instruction::Instruction, irtype::Type, + metadata::MetadataIndex, +}; /// A wrapper around an [ECS](https://github.com/fitzgen/generational-arena) handle into the /// [`Context`]. @@ -15,7 +20,14 @@ pub struct Value(pub generational_arena::Index); #[doc(hidden)] #[derive(Debug, Clone)] -pub enum ValueContent { +pub struct ValueContent { + pub value: ValueDatum, + pub span_md_idx: Option, +} + +#[doc(hidden)] +#[derive(Debug, Clone)] +pub enum ValueDatum { Argument(Type), Constant(Constant), Instruction(Instruction), @@ -23,26 +35,58 @@ pub enum ValueContent { impl Value { /// Return a new argument [`Value`]. - pub fn new_argument(context: &mut Context, ty: Type) -> Value { - let content = ValueContent::Argument(ty); + pub fn new_argument( + context: &mut Context, + ty: Type, + span_md_idx: Option, + ) -> Value { + let content = ValueContent { + value: ValueDatum::Argument(ty), + span_md_idx, + }; Value(context.values.insert(content)) } /// Return a new constant [`Value`]. - pub fn new_constant(context: &mut Context, constant: Constant) -> Value { - let content = ValueContent::Constant(constant); + pub fn new_constant( + context: &mut Context, + constant: Constant, + span_md_idx: Option, + ) -> Value { + let content = ValueContent { + value: ValueDatum::Constant(constant), + span_md_idx, + }; Value(context.values.insert(content)) } /// Return a new instruction [`Value`]. - pub fn new_instruction(context: &mut Context, instruction: Instruction) -> Value { - let content = ValueContent::Instruction(instruction); + pub fn new_instruction( + context: &mut Context, + instruction: Instruction, + opt_span_md_idx: Option, + ) -> Value { + let content = ValueContent { + value: ValueDatum::Instruction(instruction), + span_md_idx: opt_span_md_idx, + }; Value(context.values.insert(content)) } + /// Return this value's source span. + pub fn get_span(&self, context: &Context) -> Option { + // We unwrap the Result for now, until we refactor Span to not need a source string, in + // which case there will be no need to open and read a file, and no Result involved. + context.values[self.0] + .span_md_idx + .map(|idx| idx.to_span(context)) + .transpose() + .expect("A valid span.") + } + /// Return whether this is a constant value. pub fn is_constant(&self, context: &Context) -> bool { - matches!(context.values[self.0], ValueContent::Constant(_)) + matches!(context.values[self.0].value, ValueDatum::Constant(_)) } /// Return whether this value is an instruction, and specifically a 'terminator'. @@ -50,8 +94,8 @@ impl Value { /// A terminator is always the last instruction in a block (and may not appear anywhere else) /// and is either a branch or return. pub fn is_terminator(&self, context: &Context) -> bool { - match &context.values[self.0] { - ValueContent::Instruction(ins) => matches!( + match &context.values[self.0].value { + ValueDatum::Instruction(ins) => matches!( ins, Instruction::Branch(_) | Instruction::ConditionalBranch { .. } @@ -72,7 +116,8 @@ impl Value { /// If this value is an instruction and if any of its parameters is `old_val` then replace them /// with `new_val`. pub fn replace_instruction_value(&self, context: &mut Context, old_val: Value, new_val: Value) { - if let ValueContent::Instruction(instruction) = &mut context.values.get_mut(self.0).unwrap() + if let ValueDatum::Instruction(instruction) = + &mut context.values.get_mut(self.0).unwrap().value { instruction.replace_value(old_val, new_val); } @@ -82,10 +127,10 @@ impl Value { /// /// Arguments and constants always have a type, but only some instructions do. pub fn get_type(&self, context: &Context) -> Option { - match &context.values[self.0] { - ValueContent::Argument(ty) => Some(*ty), - ValueContent::Constant(c) => Some(c.ty), - ValueContent::Instruction(ins) => ins.get_type(context), + match &context.values[self.0].value { + ValueDatum::Argument(ty) => Some(*ty), + ValueDatum::Constant(c) => Some(c.ty), + ValueDatum::Instruction(ins) => ins.get_type(context), } } diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 3c80a90192e..6ee58cdbd77 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -18,7 +18,7 @@ use crate::{ irtype::{Aggregate, Type}, module::ModuleContent, pointer::Pointer, - value::{Value, ValueContent}, + value::{Value, ValueDatum}, }; impl Context { @@ -46,7 +46,7 @@ impl Context { fn verify_block(&self, function: &FunctionContent, block: &BlockContent) -> Result<(), String> { for ins in &block.instructions { - self.verify_instruction(function, &self.values[ins.0])?; + self.verify_instruction(function, &self.values[ins.0].value)?; } let (last_is_term, num_terms) = block.instructions.iter().fold((false, 0), |(_, n), ins| { @@ -69,9 +69,9 @@ impl Context { fn verify_instruction( &self, function: &FunctionContent, - instruction: &ValueContent, + instruction: &ValueDatum, ) -> Result<(), String> { - if let ValueContent::Instruction(instruction) = instruction { + if let ValueDatum::Instruction(instruction) = instruction { match instruction { Instruction::AsmBlock(asm, args) => self.verify_asm_block(asm, args)?, Instruction::Branch(block) => self.verify_br(block)?, diff --git a/sway-ir/tests/tests.rs b/sway-ir/tests/tests.rs index 734e9a23d5c..476138c9135 100644 --- a/sway-ir/tests/tests.rs +++ b/sway-ir/tests/tests.rs @@ -15,8 +15,9 @@ fn ir_to_ir_tests() { // Run the tests! // // We currently choose a single pass based on the test file name, but eventually we - // should use a comment within the test file to invoke `lit`. + // should use a comment within the test file to invoke `FileCheck`. + println!("--- TESTING: {}", path.display()); let path_str = path.file_name().unwrap().to_string_lossy(); if path_str.starts_with("inline") { test_inline(path);