diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index c4fbd07fc266..21c05d114edb 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -285,8 +285,92 @@ class CopyOnWriteBlock { }; } // namespace +// A block of code that contains pending instructions that might be needed but +// that haven't been inserted yet. +class Context::PendingBlock { + public: + PendingBlock(Context& context) : context_(context) {} + + PendingBlock(const PendingBlock&) = delete; + PendingBlock& operator=(const PendingBlock&) = delete; + + // A scope in which we will tentatively add nodes to a pending block. If we + // leave the scope without inserting or merging the block, nodes added after + // this point will be removed again. + class DiscardUnusedNodesScope { + public: + DiscardUnusedNodesScope(PendingBlock& block) + : block_(block), size_(block.nodes_.size()) {} + ~DiscardUnusedNodesScope() { + if (block_.nodes_.size() > size_) { + block_.nodes_.truncate(size_); + } + } + + private: + PendingBlock& block_; + size_t size_; + }; + + auto AddNode(SemIR::Node node) -> SemIR::NodeId { + auto node_id = context_.semantics_ir().AddNodeInNoBlock(node); + nodes_.push_back(node_id); + return node_id; + } + + // Insert the pending block of code at the current position. + auto InsertHere() -> void { + for (auto id : nodes_) { + context_.node_block_stack().AddNodeId(id); + } + nodes_.clear(); + } + + // Replace the node at target_id with the nodes in this block. The new value + // for target_id should be value_id. + auto MergeReplacing(SemIR::NodeId target_id, SemIR::NodeId value_id) -> void { + auto value = context_.semantics_ir().GetNode(value_id); + + // There are three cases here: + + if (nodes_.empty()) { + // 1) The block is empty. Replace `target_id` with an empty splice + // pointing at `value_id`. + context_.semantics_ir().ReplaceNode( + target_id, + SemIR::Node::SpliceBlock::Make(value.parse_node(), value.type_id(), + SemIR::NodeBlockId::Empty, value_id)); + } else if (nodes_.size() == 1 && nodes_[0] == value_id) { + // 2) The block is {value_id}. Replace `target_id` with the node referred + // to by `value_id`. This is intended to be the common case. + context_.semantics_ir().ReplaceNode(target_id, value); + } else { + // 3) Anything else: splice it into the IR, replacing `target_id`. + context_.semantics_ir().ReplaceNode( + target_id, + SemIR::Node::SpliceBlock::Make( + value.parse_node(), value.type_id(), + context_.semantics_ir().AddNodeBlock(nodes_), value_id)); + } + + // Prepare to stash more pending instructions. + nodes_.clear(); + } + + private: + Context& context_; + llvm::SmallVector nodes_; +}; + auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, SemIR::NodeId value_id) -> SemIR::NodeId { + PendingBlock target_block(*this); + return InitializeImpl(parse_node, target_id, target_block, value_id); +} + +auto Context::InitializeImpl(Parse::Node parse_node, SemIR::NodeId target_id, + PendingBlock& target_block, SemIR::NodeId value_id) + -> SemIR::NodeId { // Implicitly convert the value to the type of the target. auto type_id = semantics_ir().GetNode(target_id).type_id(); auto expr_id = ImplicitAs(parse_node, value_id, type_id); @@ -314,7 +398,7 @@ auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, return expr_id; case SemIR::ExpressionCategory::Initializing: - MarkInitializerFor(expr_id, target_id); + MarkInitializerFor(expr_id, target_id, target_block); return expr_id; case SemIR::ExpressionCategory::Mixed: @@ -340,19 +424,19 @@ auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, // `ImplicitAsRequired`, but this will need to change once we stop // doing that. auto inner_target_type = semantics_ir().GetNode(elem_id).type_id(); - // TODO: This should be placed into the return slot, and only - // created if needed. - auto inner_target_id = - AddNode(is_tuple ? SemIR::Node::TupleAccess::Make( - parse_node, inner_target_type, target_id, - SemIR::MemberIndex(i)) - : SemIR::Node::StructAccess::Make( - parse_node, inner_target_type, target_id, - SemIR::MemberIndex(i))); + PendingBlock::DiscardUnusedNodesScope scope(target_block); + auto inner_target_id = target_block.AddNode( + is_tuple ? SemIR::Node::TupleAccess::Make( + parse_node, inner_target_type, target_id, + SemIR::MemberIndex(i)) + : SemIR::Node::StructAccess::Make( + parse_node, inner_target_type, target_id, + SemIR::MemberIndex(i))); auto new_id = is_in_place ? InitializeAndFinalize(parse_node, inner_target_id, - elem_id) - : Initialize(parse_node, inner_target_id, elem_id); + target_block, elem_id) + : InitializeImpl(parse_node, inner_target_id, + target_block, elem_id); new_block.Set(i, new_id); } return AddNode( @@ -371,8 +455,9 @@ auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, auto Context::InitializeAndFinalize(Parse::Node parse_node, SemIR::NodeId target_id, + PendingBlock& target_block, SemIR::NodeId value_id) -> SemIR::NodeId { - auto init_id = Initialize(parse_node, target_id, value_id); + auto init_id = InitializeImpl(parse_node, target_id, target_block, value_id); if (init_id == SemIR::NodeId::BuiltinError) { return init_id; } @@ -380,6 +465,7 @@ auto Context::InitializeAndFinalize(Parse::Node parse_node, if (auto init_rep = SemIR::GetInitializingRepresentation(semantics_ir(), target_type_id); init_rep.kind == SemIR::InitializingRepresentation::ByCopy) { + target_block.InsertHere(); init_id = AddNode(SemIR::Node::InitializeFrom::Make( parse_node, target_type_id, init_id, target_id)); } @@ -510,8 +596,8 @@ static auto FindReturnSlotForInitializer(SemIR::File& semantics_ir, } } -auto Context::MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id) - -> void { +auto Context::MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id, + PendingBlock& target_block) -> void { auto return_slot_id = FindReturnSlotForInitializer(semantics_ir(), init_id); if (return_slot_id.is_valid()) { // Replace the temporary in the return slot with a reference to our target. @@ -520,11 +606,7 @@ auto Context::MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id) << "Return slot for initializer does not contain a temporary; " << "initialized multiple times? Have " << semantics_ir().GetNode(return_slot_id); - semantics_ir().ReplaceNode( - return_slot_id, - SemIR::Node::StubReference::Make( - semantics_ir().GetNode(init_id).parse_node(), - semantics_ir().GetNode(target_id).type_id(), target_id)); + target_block.MergeReplacing(return_slot_id, target_id); } } @@ -681,9 +763,11 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, value_id = context.ConvertToValueOrReferenceExpression(value_id); } + Context::PendingBlock target_block(context); + // Arrays are always initialized in-place. Tentatively allocate a temporary // as the destination for the array initialization. - auto return_slot_id = context.AddNode( + auto return_slot_id = target_block.AddNode( SemIR::Node::TemporaryStorage::Make(value.parse_node(), array_type_id)); // Initialize each element of the array from the corresponding element of the @@ -691,21 +775,25 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, llvm::SmallVector inits; inits.reserve(array_bound + 1); for (auto [i, src_type_id] : llvm::enumerate(tuple_elem_types)) { + Context::PendingBlock::DiscardUnusedNodesScope scope(target_block); // TODO: Add a new node kind for indexing an array at a constant index // so that we don't need an integer literal node here. - auto index_id = context.AddNode(SemIR::Node::IntegerLiteral::Make( + auto index_id = target_block.AddNode(SemIR::Node::IntegerLiteral::Make( value.parse_node(), context.CanonicalizeType(SemIR::NodeId::BuiltinIntegerType), context.semantics_ir().AddIntegerLiteral(llvm::APInt(32, i)))); - auto target_id = context.AddNode(SemIR::Node::ArrayIndex::Make( + auto target_id = target_block.AddNode(SemIR::Node::ArrayIndex::Make( value.parse_node(), element_type_id, return_slot_id, index_id)); - auto src_id = - !literal_elems.empty() - ? literal_elems[i] - : context.AddNode(SemIR::Node::TupleIndex::Make( - value.parse_node(), src_type_id, value_id, index_id)); - auto init_id = - context.InitializeAndFinalize(value.parse_node(), target_id, src_id); + // Note, this is computing the source location not the destination, so it + // goes into the current code block, not into the target block. + // TODO: Ideally we would also discard this node if it's unused. + auto src_id = !literal_elems.empty() + ? literal_elems[i] + : context.AddNode(SemIR::Node::TupleAccess::Make( + value.parse_node(), src_type_id, value_id, + SemIR::MemberIndex(i))); + auto init_id = context.InitializeAndFinalize(value.parse_node(), target_id, + target_block, src_id); if (init_id == SemIR::NodeId::BuiltinError) { return SemIR::NodeId::BuiltinError; } @@ -713,7 +801,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, } // The last element of the refs block contains the return slot for the array - // initialization. + // initialization. Flush the temporary here if we didn't insert it earlier. + target_block.InsertHere(); inits.push_back(return_slot_id); return context.AddNode( diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 1549045a531a..97f4f70b10fb 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -21,6 +21,9 @@ namespace Carbon::Check { // Context and shared functionality for semantics handlers. class Context { public: + // A block of code that has not yet been inserted into SemIR. + class PendingBlock; + // Stores references for work. explicit Context(const Lex::TokenizedBuffer& tokens, DiagnosticEmitter& emitter, @@ -127,7 +130,8 @@ class Context { // of the initializer, such as an `Assign` or `ReturnExpression` node. The // resulting node describes the initialization operation that was performed. auto InitializeAndFinalize(Parse::Node parse_node, SemIR::NodeId target_id, - SemIR::NodeId value_id) -> SemIR::NodeId; + PendingBlock& target_block, SemIR::NodeId value_id) + -> SemIR::NodeId; // Converts `value_id` to a value expression of type `type_id`. auto ConvertToValueOfType(Parse::Node parse_node, SemIR::NodeId value_id, @@ -177,17 +181,8 @@ class Context { -> SemIR::TypeId; // Converts an expression for use as a type. - // TODO: This should eventually return a type ID. auto ExpressionAsType(Parse::Node parse_node, SemIR::NodeId value_id) -> SemIR::TypeId { - auto node = semantics_ir_->GetNode(value_id); - if (node.kind() == SemIR::NodeKind::StubReference) { - value_id = node.GetAsStubReference(); - CARBON_CHECK(semantics_ir_->GetNode(value_id).kind() != - SemIR::NodeKind::StubReference) - << "Stub reference should not point to another stub reference"; - } - return CanonicalizeType( ConvertToValueOfType(parse_node, value_id, SemIR::TypeId::TypeType)); } @@ -273,6 +268,13 @@ class Context { // TODO: This likely needs to track things which need to be destructed. }; + // Implementation of `Initialize`. Takes a `target_block` which contains + // pending instructions that are needed to form the value of `target_id`. + // These can be discarded if no initialization is needed. + auto InitializeImpl(Parse::Node parse_node, SemIR::NodeId target_id, + PendingBlock& target_block, SemIR::NodeId value_id) + -> SemIR::NodeId; + // Commits to using a temporary to store the result of the initializing // expression described by `init_id`, and returns the location of the // temporary. If `discarded` is `true`, the result is discarded, and no @@ -282,8 +284,8 @@ class Context { -> SemIR::NodeId; // Marks the initializer `init_id` as initializing `target_id`. - auto MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id) - -> void; + auto MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id, + PendingBlock& target_block) -> void; // Runs ImplicitAs behavior to convert `value` to `as_type`, returning the // converted result. Prints a diagnostic and returns an Error if the diff --git a/toolchain/check/node_block_stack.cpp b/toolchain/check/node_block_stack.cpp index 0b3edfaf2e46..07b190a34491 100644 --- a/toolchain/check/node_block_stack.cpp +++ b/toolchain/check/node_block_stack.cpp @@ -53,6 +53,12 @@ auto NodeBlockStack::Pop() -> SemIR::NodeBlockId { return back.id; } +auto NodeBlockStack::PopAndDiscard() -> void { + CARBON_CHECK(!empty()) << "no current block"; + --size_; + CARBON_VLOG() << name_ << " PopAndDiscard " << size_ << "\n"; +} + auto NodeBlockStack::PrintForStackDump(llvm::raw_ostream& output) const -> void { output << name_ << ":\n"; diff --git a/toolchain/check/node_block_stack.h b/toolchain/check/node_block_stack.h index 3a43f221022a..b7b67a8c4b05 100644 --- a/toolchain/check/node_block_stack.h +++ b/toolchain/check/node_block_stack.h @@ -42,6 +42,9 @@ class NodeBlockStack { // SemIR::NodeBlockId::Empty is returned if one wasn't allocated. auto Pop() -> SemIR::NodeBlockId; + // Pops the top node block, and discards it if it hasn't had an ID allocated. + auto PopAndDiscard() -> void; + // Adds the given node to the block at the top of the stack and returns its // ID. auto AddNode(SemIR::Node node) -> SemIR::NodeId { diff --git a/toolchain/check/testdata/array/array_in_place.carbon b/toolchain/check/testdata/array/array_in_place.carbon new file mode 100644 index 000000000000..f2dd99074c69 --- /dev/null +++ b/toolchain/check/testdata/array/array_in_place.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: [(i32, i32, i32); 2] = (F(), F()); +} + +// CHECK:STDOUT: file "array_in_place.carbon" { +// CHECK:STDOUT: %.loc7 = fn_decl @F +// CHECK:STDOUT: %.loc9 = fn_decl @G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> %return: (i32, i32, i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_25: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc10_28: i32 = int_literal 2 +// CHECK:STDOUT: %.loc10_29: type = array_type %.loc10_28, (i32, i32, i32) +// CHECK:STDOUT: %v: ref [(i32, i32, i32); 2] = var "v" +// CHECK:STDOUT: %.loc10_42.3: ref (i32, i32, i32) = splice_block %.loc10_42.2 { +// CHECK:STDOUT: %.loc10_7: ref [(i32, i32, i32); 2] = splice_block %v {} +// CHECK:STDOUT: %.loc10_42.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc10_42.2: ref (i32, i32, i32) = array_index %.loc10_7, %.loc10_42.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_35: init (i32, i32, i32) = call @F() to %.loc10_42.3 +// CHECK:STDOUT: %.loc10_42.6: ref (i32, i32, i32) = splice_block %.loc10_42.5 { +// CHECK:STDOUT: %.loc10_42.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc10_42.5: ref (i32, i32, i32) = array_index %.loc10_7, %.loc10_42.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_40: init (i32, i32, i32) = call @F() to %.loc10_42.6 +// CHECK:STDOUT: %.loc10_42.7: type = tuple_type ((i32, i32, i32), (i32, i32, i32)) +// CHECK:STDOUT: %.loc10_42.8: ((i32, i32, i32), (i32, i32, i32)) = tuple_literal (%.loc10_35, %.loc10_40) +// CHECK:STDOUT: %.loc10_42.9: init [(i32, i32, i32); 2] = array_init %.loc10_42.8, (%.loc10_35, %.loc10_40) to %.loc10_7 +// CHECK:STDOUT: assign %v, %.loc10_42.9 +// CHECK:STDOUT: return +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/array/assign_return_value.carbon b/toolchain/check/testdata/array/assign_return_value.carbon index 1445c0b267a0..760a6c2042d8 100644 --- a/toolchain/check/testdata/array/assign_return_value.carbon +++ b/toolchain/check/testdata/array/assign_return_value.carbon @@ -31,13 +31,13 @@ fn Run() { // CHECK:STDOUT: %.loc10_22.1: init (i32,) = call @F() // CHECK:STDOUT: %.loc10_22.2: ref (i32,) = temporary_storage // CHECK:STDOUT: %.loc10_22.3: ref (i32,) = temporary %.loc10_22.2, %.loc10_22.1 -// CHECK:STDOUT: %.loc10_22.4: ref [i32; 1] = stub_reference %t -// CHECK:STDOUT: %.loc10_22.5: i32 = int_literal 0 -// CHECK:STDOUT: %.loc10_22.6: ref i32 = array_index %.loc10_22.4, %.loc10_22.5 -// CHECK:STDOUT: %.loc10_22.7: ref i32 = tuple_index %.loc10_22.3, %.loc10_22.5 -// CHECK:STDOUT: %.loc10_22.8: i32 = bind_value %.loc10_22.7 -// CHECK:STDOUT: %.loc10_22.9: init i32 = initialize_from %.loc10_22.8 to %.loc10_22.6 -// CHECK:STDOUT: %.loc10_22.10: init [i32; 1] = array_init %.loc10_22.3, (%.loc10_22.9) to %.loc10_22.4 -// CHECK:STDOUT: assign %t, %.loc10_22.10 +// CHECK:STDOUT: %.loc10_22.4: ref i32 = tuple_access %.loc10_22.3, member0 +// CHECK:STDOUT: %.loc10_22.5: i32 = bind_value %.loc10_22.4 +// CHECK:STDOUT: %.loc10_7: ref [i32; 1] = splice_block %t {} +// CHECK:STDOUT: %.loc10_22.6: i32 = int_literal 0 +// CHECK:STDOUT: %.loc10_22.7: ref i32 = array_index %.loc10_7, %.loc10_22.6 +// CHECK:STDOUT: %.loc10_22.8: init i32 = initialize_from %.loc10_22.5 to %.loc10_22.7 +// CHECK:STDOUT: %.loc10_22.9: init [i32; 1] = array_init %.loc10_22.3, (%.loc10_22.8) to %.loc10_7 +// CHECK:STDOUT: assign %t, %.loc10_22.9 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/array/assign_var.carbon b/toolchain/check/testdata/array/assign_var.carbon index 2bb92816fc4a..05e7dcae3eb2 100644 --- a/toolchain/check/testdata/array/assign_var.carbon +++ b/toolchain/check/testdata/array/assign_var.carbon @@ -27,22 +27,22 @@ var b: [i32; 3] = a; // CHECK:STDOUT: %.loc8_14: i32 = int_literal 3 // CHECK:STDOUT: %.loc8_15: type = array_type %.loc8_14, i32 // CHECK:STDOUT: %b: ref [i32; 3] = var "b" -// CHECK:STDOUT: %.loc7_5.1: ref [i32; 3] = stub_reference %b -// CHECK:STDOUT: %.loc7_5.2: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_5.3: ref i32 = array_index %.loc7_5.1, %.loc7_5.2 -// CHECK:STDOUT: %.loc7_5.4: ref i32 = tuple_index %a, %.loc7_5.2 -// CHECK:STDOUT: %.loc7_5.5: i32 = bind_value %.loc7_5.4 -// CHECK:STDOUT: %.loc7_5.6: init i32 = initialize_from %.loc7_5.5 to %.loc7_5.3 -// CHECK:STDOUT: %.loc7_5.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc7_5.8: ref i32 = array_index %.loc7_5.1, %.loc7_5.7 -// CHECK:STDOUT: %.loc7_5.9: ref i32 = tuple_index %a, %.loc7_5.7 -// CHECK:STDOUT: %.loc7_5.10: i32 = bind_value %.loc7_5.9 -// CHECK:STDOUT: %.loc7_5.11: init i32 = initialize_from %.loc7_5.10 to %.loc7_5.8 -// CHECK:STDOUT: %.loc7_5.12: i32 = int_literal 2 -// CHECK:STDOUT: %.loc7_5.13: ref i32 = array_index %.loc7_5.1, %.loc7_5.12 -// CHECK:STDOUT: %.loc7_5.14: ref i32 = tuple_index %a, %.loc7_5.12 -// CHECK:STDOUT: %.loc7_5.15: i32 = bind_value %.loc7_5.14 -// CHECK:STDOUT: %.loc7_5.16: init i32 = initialize_from %.loc7_5.15 to %.loc7_5.13 -// CHECK:STDOUT: %.loc7_5.17: init [i32; 3] = array_init %a, (%.loc7_5.6, %.loc7_5.11, %.loc7_5.16) to %.loc7_5.1 -// CHECK:STDOUT: assign %b, %.loc7_5.17 +// CHECK:STDOUT: %.loc7_5.1: ref i32 = tuple_access %a, member0 +// CHECK:STDOUT: %.loc7_5.2: i32 = bind_value %.loc7_5.1 +// CHECK:STDOUT: %.loc8_5: ref [i32; 3] = splice_block %b {} +// CHECK:STDOUT: %.loc7_5.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_5.4: ref i32 = array_index %.loc8_5, %.loc7_5.3 +// CHECK:STDOUT: %.loc7_5.5: init i32 = initialize_from %.loc7_5.2 to %.loc7_5.4 +// CHECK:STDOUT: %.loc7_5.6: ref i32 = tuple_access %a, member1 +// CHECK:STDOUT: %.loc7_5.7: i32 = bind_value %.loc7_5.6 +// CHECK:STDOUT: %.loc7_5.8: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_5.9: ref i32 = array_index %.loc8_5, %.loc7_5.8 +// CHECK:STDOUT: %.loc7_5.10: init i32 = initialize_from %.loc7_5.7 to %.loc7_5.9 +// CHECK:STDOUT: %.loc7_5.11: ref i32 = tuple_access %a, member2 +// CHECK:STDOUT: %.loc7_5.12: i32 = bind_value %.loc7_5.11 +// CHECK:STDOUT: %.loc7_5.13: i32 = int_literal 2 +// CHECK:STDOUT: %.loc7_5.14: ref i32 = array_index %.loc8_5, %.loc7_5.13 +// CHECK:STDOUT: %.loc7_5.15: init i32 = initialize_from %.loc7_5.12 to %.loc7_5.14 +// CHECK:STDOUT: %.loc7_5.16: init [i32; 3] = array_init %a, (%.loc7_5.5, %.loc7_5.10, %.loc7_5.15) to %.loc8_5 +// CHECK:STDOUT: assign %b, %.loc7_5.16 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/array/base.carbon b/toolchain/check/testdata/array/base.carbon index 4d3de21ed0e6..4ad1db1b1bf6 100644 --- a/toolchain/check/testdata/array/base.carbon +++ b/toolchain/check/testdata/array/base.carbon @@ -15,12 +15,12 @@ var c: [(); 5] = ((), (), (), (), (),); // CHECK:STDOUT: %.loc7_20: i32 = int_literal 1 // CHECK:STDOUT: %.loc7_22.1: type = tuple_type (i32) // CHECK:STDOUT: %.loc7_22.2: (i32,) = tuple_literal (%.loc7_20) -// CHECK:STDOUT: %.loc7_22.3: ref [i32; 1] = stub_reference %a -// CHECK:STDOUT: %.loc7_22.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_22.5: ref i32 = array_index %.loc7_22.3, %.loc7_22.4 -// CHECK:STDOUT: %.loc7_22.6: init i32 = initialize_from %.loc7_20 to %.loc7_22.5 -// CHECK:STDOUT: %.loc7_22.7: init [i32; 1] = array_init %.loc7_22.2, (%.loc7_22.6) to %.loc7_22.3 -// CHECK:STDOUT: assign %a, %.loc7_22.7 +// CHECK:STDOUT: %.loc7_5: ref [i32; 1] = splice_block %a {} +// CHECK:STDOUT: %.loc7_22.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_22.4: ref i32 = array_index %.loc7_5, %.loc7_22.3 +// CHECK:STDOUT: %.loc7_22.5: init i32 = initialize_from %.loc7_20 to %.loc7_22.4 +// CHECK:STDOUT: %.loc7_22.6: init [i32; 1] = array_init %.loc7_22.2, (%.loc7_22.5) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_22.6 // CHECK:STDOUT: %.loc8_14: i32 = int_literal 2 // CHECK:STDOUT: %.loc8_15: type = array_type %.loc8_14, f64 // CHECK:STDOUT: %b: ref [f64; 2] = var "b" @@ -28,15 +28,15 @@ var c: [(); 5] = ((), (), (), (), (),); // CHECK:STDOUT: %.loc8_26: f64 = real_literal 22e-1 // CHECK:STDOUT: %.loc8_30.1: type = tuple_type (f64, f64) // CHECK:STDOUT: %.loc8_30.2: (f64, f64) = tuple_literal (%.loc8_20, %.loc8_26) -// CHECK:STDOUT: %.loc8_30.3: ref [f64; 2] = stub_reference %b -// CHECK:STDOUT: %.loc8_30.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc8_30.5: ref f64 = array_index %.loc8_30.3, %.loc8_30.4 -// CHECK:STDOUT: %.loc8_30.6: init f64 = initialize_from %.loc8_20 to %.loc8_30.5 -// CHECK:STDOUT: %.loc8_30.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc8_30.8: ref f64 = array_index %.loc8_30.3, %.loc8_30.7 -// CHECK:STDOUT: %.loc8_30.9: init f64 = initialize_from %.loc8_26 to %.loc8_30.8 -// CHECK:STDOUT: %.loc8_30.10: init [f64; 2] = array_init %.loc8_30.2, (%.loc8_30.6, %.loc8_30.9) to %.loc8_30.3 -// CHECK:STDOUT: assign %b, %.loc8_30.10 +// CHECK:STDOUT: %.loc8_5: ref [f64; 2] = splice_block %b {} +// CHECK:STDOUT: %.loc8_30.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc8_30.4: ref f64 = array_index %.loc8_5, %.loc8_30.3 +// CHECK:STDOUT: %.loc8_30.5: init f64 = initialize_from %.loc8_20 to %.loc8_30.4 +// CHECK:STDOUT: %.loc8_30.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc8_30.7: ref f64 = array_index %.loc8_5, %.loc8_30.6 +// CHECK:STDOUT: %.loc8_30.8: init f64 = initialize_from %.loc8_26 to %.loc8_30.7 +// CHECK:STDOUT: %.loc8_30.9: init [f64; 2] = array_init %.loc8_30.2, (%.loc8_30.5, %.loc8_30.8) to %.loc8_5 +// CHECK:STDOUT: assign %b, %.loc8_30.9 // CHECK:STDOUT: %.loc9_10.1: type = tuple_type () // CHECK:STDOUT: %.loc9_10.2: () = tuple_literal () // CHECK:STDOUT: %.loc9_13: i32 = int_literal 5 @@ -49,22 +49,12 @@ var c: [(); 5] = ((), (), (), (), (),); // CHECK:STDOUT: %.loc9_36: () = tuple_literal () // CHECK:STDOUT: %.loc9_38.1: type = tuple_type ((), (), (), (), ()) // CHECK:STDOUT: %.loc9_38.2: ((), (), (), (), ()) = tuple_literal (%.loc9_20, %.loc9_24, %.loc9_28, %.loc9_32, %.loc9_36) -// CHECK:STDOUT: %.loc9_38.3: ref [(); 5] = stub_reference %c -// CHECK:STDOUT: %.loc9_38.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc9_38.5: ref () = array_index %.loc9_38.3, %.loc9_38.4 -// CHECK:STDOUT: %.loc9_38.6: init () = tuple_init %.loc9_20, () -// CHECK:STDOUT: %.loc9_38.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc9_38.8: ref () = array_index %.loc9_38.3, %.loc9_38.7 -// CHECK:STDOUT: %.loc9_38.9: init () = tuple_init %.loc9_24, () -// CHECK:STDOUT: %.loc9_38.10: i32 = int_literal 2 -// CHECK:STDOUT: %.loc9_38.11: ref () = array_index %.loc9_38.3, %.loc9_38.10 -// CHECK:STDOUT: %.loc9_38.12: init () = tuple_init %.loc9_28, () -// CHECK:STDOUT: %.loc9_38.13: i32 = int_literal 3 -// CHECK:STDOUT: %.loc9_38.14: ref () = array_index %.loc9_38.3, %.loc9_38.13 -// CHECK:STDOUT: %.loc9_38.15: init () = tuple_init %.loc9_32, () -// CHECK:STDOUT: %.loc9_38.16: i32 = int_literal 4 -// CHECK:STDOUT: %.loc9_38.17: ref () = array_index %.loc9_38.3, %.loc9_38.16 -// CHECK:STDOUT: %.loc9_38.18: init () = tuple_init %.loc9_36, () -// CHECK:STDOUT: %.loc9_38.19: init [(); 5] = array_init %.loc9_38.2, (%.loc9_38.6, %.loc9_38.9, %.loc9_38.12, %.loc9_38.15, %.loc9_38.18) to %.loc9_38.3 -// CHECK:STDOUT: assign %c, %.loc9_38.19 +// CHECK:STDOUT: %.loc9_38.3: init () = tuple_init %.loc9_20, () +// CHECK:STDOUT: %.loc9_38.4: init () = tuple_init %.loc9_24, () +// CHECK:STDOUT: %.loc9_38.5: init () = tuple_init %.loc9_28, () +// CHECK:STDOUT: %.loc9_38.6: init () = tuple_init %.loc9_32, () +// CHECK:STDOUT: %.loc9_38.7: init () = tuple_init %.loc9_36, () +// CHECK:STDOUT: %.loc9_5: ref [(); 5] = splice_block %c {} +// CHECK:STDOUT: %.loc9_38.8: init [(); 5] = array_init %.loc9_38.2, (%.loc9_38.3, %.loc9_38.4, %.loc9_38.5, %.loc9_38.6, %.loc9_38.7) to %.loc9_5 +// CHECK:STDOUT: assign %c, %.loc9_38.8 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/array/fail_type_mismatch.carbon b/toolchain/check/testdata/array/fail_type_mismatch.carbon index d4efbf691511..4876cce99685 100644 --- a/toolchain/check/testdata/array/fail_type_mismatch.carbon +++ b/toolchain/check/testdata/array/fail_type_mismatch.carbon @@ -39,8 +39,6 @@ var d: [i32; 3] = t2; // CHECK:STDOUT: %.loc10_39.4: i32 = int_literal 0 // CHECK:STDOUT: %.loc10_39.5: ref i32 = array_index %.loc10_39.3, %.loc10_39.4 // CHECK:STDOUT: %.loc10_39.6: init i32 = initialize_from %.loc10_20 to %.loc10_39.5 -// CHECK:STDOUT: %.loc10_39.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc10_39.8: ref i32 = array_index %.loc10_39.3, %.loc10_39.7 // CHECK:STDOUT: assign %a, // CHECK:STDOUT: %.loc15_29.1: type = tuple_type (type, type, type) // CHECK:STDOUT: %.loc15_29.2: (type, type, type) = tuple_literal (i32, String, String) @@ -48,15 +46,13 @@ var d: [i32; 3] = t2; // CHECK:STDOUT: %.loc16_14: i32 = int_literal 3 // CHECK:STDOUT: %.loc16_15: type = array_type %.loc16_14, i32 // CHECK:STDOUT: %b: ref [i32; 3] = var "b" -// CHECK:STDOUT: %.loc15_5.1: ref [i32; 3] = temporary_storage -// CHECK:STDOUT: %.loc15_5.2: i32 = int_literal 0 -// CHECK:STDOUT: %.loc15_5.3: ref i32 = array_index %.loc15_5.1, %.loc15_5.2 -// CHECK:STDOUT: %.loc15_5.4: ref i32 = tuple_index %t1, %.loc15_5.2 -// CHECK:STDOUT: %.loc15_5.5: i32 = bind_value %.loc15_5.4 -// CHECK:STDOUT: %.loc15_5.6: init i32 = initialize_from %.loc15_5.5 to %.loc15_5.3 -// CHECK:STDOUT: %.loc15_5.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc15_5.8: ref i32 = array_index %.loc15_5.1, %.loc15_5.7 -// CHECK:STDOUT: %.loc15_5.9: ref String = tuple_index %t1, %.loc15_5.7 +// CHECK:STDOUT: %.loc15_5.1: ref i32 = tuple_access %t1, member0 +// CHECK:STDOUT: %.loc15_5.2: i32 = bind_value %.loc15_5.1 +// CHECK:STDOUT: %.loc15_5.3: ref [i32; 3] = temporary_storage +// CHECK:STDOUT: %.loc15_5.4: i32 = int_literal 0 +// CHECK:STDOUT: %.loc15_5.5: ref i32 = array_index %.loc15_5.3, %.loc15_5.4 +// CHECK:STDOUT: %.loc15_5.6: init i32 = initialize_from %.loc15_5.2 to %.loc15_5.5 +// CHECK:STDOUT: %.loc15_5.7: ref String = tuple_access %t1, member1 // CHECK:STDOUT: assign %b, // CHECK:STDOUT: %.loc21_14: i32 = int_literal 3 // CHECK:STDOUT: %.loc21_15: type = array_type %.loc21_14, i32 diff --git a/toolchain/check/testdata/array/nine_elements.carbon b/toolchain/check/testdata/array/nine_elements.carbon index d1b26fe4d904..b87b63206bbe 100644 --- a/toolchain/check/testdata/array/nine_elements.carbon +++ b/toolchain/check/testdata/array/nine_elements.carbon @@ -21,34 +21,34 @@ var a: [i32; 9] = (1, 2, 3, 4, 5, 6, 7, 8, 9); // CHECK:STDOUT: %.loc7_44: i32 = int_literal 9 // CHECK:STDOUT: %.loc7_45.1: type = tuple_type (i32, i32, i32, i32, i32, i32, i32, i32, i32) // CHECK:STDOUT: %.loc7_45.2: (i32, i32, i32, i32, i32, i32, i32, i32, i32) = tuple_literal (%.loc7_20, %.loc7_23, %.loc7_26, %.loc7_29, %.loc7_32, %.loc7_35, %.loc7_38, %.loc7_41, %.loc7_44) -// CHECK:STDOUT: %.loc7_45.3: ref [i32; 9] = stub_reference %a -// CHECK:STDOUT: %.loc7_45.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_45.5: ref i32 = array_index %.loc7_45.3, %.loc7_45.4 -// CHECK:STDOUT: %.loc7_45.6: init i32 = initialize_from %.loc7_20 to %.loc7_45.5 -// CHECK:STDOUT: %.loc7_45.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc7_45.8: ref i32 = array_index %.loc7_45.3, %.loc7_45.7 -// CHECK:STDOUT: %.loc7_45.9: init i32 = initialize_from %.loc7_23 to %.loc7_45.8 -// CHECK:STDOUT: %.loc7_45.10: i32 = int_literal 2 -// CHECK:STDOUT: %.loc7_45.11: ref i32 = array_index %.loc7_45.3, %.loc7_45.10 -// CHECK:STDOUT: %.loc7_45.12: init i32 = initialize_from %.loc7_26 to %.loc7_45.11 -// CHECK:STDOUT: %.loc7_45.13: i32 = int_literal 3 -// CHECK:STDOUT: %.loc7_45.14: ref i32 = array_index %.loc7_45.3, %.loc7_45.13 -// CHECK:STDOUT: %.loc7_45.15: init i32 = initialize_from %.loc7_29 to %.loc7_45.14 -// CHECK:STDOUT: %.loc7_45.16: i32 = int_literal 4 -// CHECK:STDOUT: %.loc7_45.17: ref i32 = array_index %.loc7_45.3, %.loc7_45.16 -// CHECK:STDOUT: %.loc7_45.18: init i32 = initialize_from %.loc7_32 to %.loc7_45.17 -// CHECK:STDOUT: %.loc7_45.19: i32 = int_literal 5 -// CHECK:STDOUT: %.loc7_45.20: ref i32 = array_index %.loc7_45.3, %.loc7_45.19 -// CHECK:STDOUT: %.loc7_45.21: init i32 = initialize_from %.loc7_35 to %.loc7_45.20 -// CHECK:STDOUT: %.loc7_45.22: i32 = int_literal 6 -// CHECK:STDOUT: %.loc7_45.23: ref i32 = array_index %.loc7_45.3, %.loc7_45.22 -// CHECK:STDOUT: %.loc7_45.24: init i32 = initialize_from %.loc7_38 to %.loc7_45.23 -// CHECK:STDOUT: %.loc7_45.25: i32 = int_literal 7 -// CHECK:STDOUT: %.loc7_45.26: ref i32 = array_index %.loc7_45.3, %.loc7_45.25 -// CHECK:STDOUT: %.loc7_45.27: init i32 = initialize_from %.loc7_41 to %.loc7_45.26 -// CHECK:STDOUT: %.loc7_45.28: i32 = int_literal 8 -// CHECK:STDOUT: %.loc7_45.29: ref i32 = array_index %.loc7_45.3, %.loc7_45.28 -// CHECK:STDOUT: %.loc7_45.30: init i32 = initialize_from %.loc7_44 to %.loc7_45.29 -// CHECK:STDOUT: %.loc7_45.31: init [i32; 9] = array_init %.loc7_45.2, (%.loc7_45.6, %.loc7_45.9, %.loc7_45.12, %.loc7_45.15, %.loc7_45.18, %.loc7_45.21, %.loc7_45.24, %.loc7_45.27, %.loc7_45.30) to %.loc7_45.3 -// CHECK:STDOUT: assign %a, %.loc7_45.31 +// CHECK:STDOUT: %.loc7_5: ref [i32; 9] = splice_block %a {} +// CHECK:STDOUT: %.loc7_45.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_45.4: ref i32 = array_index %.loc7_5, %.loc7_45.3 +// CHECK:STDOUT: %.loc7_45.5: init i32 = initialize_from %.loc7_20 to %.loc7_45.4 +// CHECK:STDOUT: %.loc7_45.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_45.7: ref i32 = array_index %.loc7_5, %.loc7_45.6 +// CHECK:STDOUT: %.loc7_45.8: init i32 = initialize_from %.loc7_23 to %.loc7_45.7 +// CHECK:STDOUT: %.loc7_45.9: i32 = int_literal 2 +// CHECK:STDOUT: %.loc7_45.10: ref i32 = array_index %.loc7_5, %.loc7_45.9 +// CHECK:STDOUT: %.loc7_45.11: init i32 = initialize_from %.loc7_26 to %.loc7_45.10 +// CHECK:STDOUT: %.loc7_45.12: i32 = int_literal 3 +// CHECK:STDOUT: %.loc7_45.13: ref i32 = array_index %.loc7_5, %.loc7_45.12 +// CHECK:STDOUT: %.loc7_45.14: init i32 = initialize_from %.loc7_29 to %.loc7_45.13 +// CHECK:STDOUT: %.loc7_45.15: i32 = int_literal 4 +// CHECK:STDOUT: %.loc7_45.16: ref i32 = array_index %.loc7_5, %.loc7_45.15 +// CHECK:STDOUT: %.loc7_45.17: init i32 = initialize_from %.loc7_32 to %.loc7_45.16 +// CHECK:STDOUT: %.loc7_45.18: i32 = int_literal 5 +// CHECK:STDOUT: %.loc7_45.19: ref i32 = array_index %.loc7_5, %.loc7_45.18 +// CHECK:STDOUT: %.loc7_45.20: init i32 = initialize_from %.loc7_35 to %.loc7_45.19 +// CHECK:STDOUT: %.loc7_45.21: i32 = int_literal 6 +// CHECK:STDOUT: %.loc7_45.22: ref i32 = array_index %.loc7_5, %.loc7_45.21 +// CHECK:STDOUT: %.loc7_45.23: init i32 = initialize_from %.loc7_38 to %.loc7_45.22 +// CHECK:STDOUT: %.loc7_45.24: i32 = int_literal 7 +// CHECK:STDOUT: %.loc7_45.25: ref i32 = array_index %.loc7_5, %.loc7_45.24 +// CHECK:STDOUT: %.loc7_45.26: init i32 = initialize_from %.loc7_41 to %.loc7_45.25 +// CHECK:STDOUT: %.loc7_45.27: i32 = int_literal 8 +// CHECK:STDOUT: %.loc7_45.28: ref i32 = array_index %.loc7_5, %.loc7_45.27 +// CHECK:STDOUT: %.loc7_45.29: init i32 = initialize_from %.loc7_44 to %.loc7_45.28 +// CHECK:STDOUT: %.loc7_45.30: init [i32; 9] = array_init %.loc7_45.2, (%.loc7_45.5, %.loc7_45.8, %.loc7_45.11, %.loc7_45.14, %.loc7_45.17, %.loc7_45.20, %.loc7_45.23, %.loc7_45.26, %.loc7_45.29) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_45.30 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/numeric_literals.carbon b/toolchain/check/testdata/basics/numeric_literals.carbon index 997370f23297..bd45e8337910 100644 --- a/toolchain/check/testdata/basics/numeric_literals.carbon +++ b/toolchain/check/testdata/basics/numeric_literals.carbon @@ -41,24 +41,24 @@ fn F() { // CHECK:STDOUT: %.loc15: i32 = int_literal 39999999999999999993 // CHECK:STDOUT: %.loc16_3.1: type = tuple_type (i32, i32, i32, i32, i32) // CHECK:STDOUT: %.loc16_3.2: (i32, i32, i32, i32, i32) = tuple_literal (%.loc11, %.loc12, %.loc13, %.loc14, %.loc15) -// CHECK:STDOUT: %.loc16_3.3: ref [i32; 5] = stub_reference %ints -// CHECK:STDOUT: %.loc16_3.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc16_3.5: ref i32 = array_index %.loc16_3.3, %.loc16_3.4 -// CHECK:STDOUT: %.loc16_3.6: init i32 = initialize_from %.loc11 to %.loc16_3.5 -// CHECK:STDOUT: %.loc16_3.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc16_3.8: ref i32 = array_index %.loc16_3.3, %.loc16_3.7 -// CHECK:STDOUT: %.loc16_3.9: init i32 = initialize_from %.loc12 to %.loc16_3.8 -// CHECK:STDOUT: %.loc16_3.10: i32 = int_literal 2 -// CHECK:STDOUT: %.loc16_3.11: ref i32 = array_index %.loc16_3.3, %.loc16_3.10 -// CHECK:STDOUT: %.loc16_3.12: init i32 = initialize_from %.loc13 to %.loc16_3.11 -// CHECK:STDOUT: %.loc16_3.13: i32 = int_literal 3 -// CHECK:STDOUT: %.loc16_3.14: ref i32 = array_index %.loc16_3.3, %.loc16_3.13 -// CHECK:STDOUT: %.loc16_3.15: init i32 = initialize_from %.loc14 to %.loc16_3.14 -// CHECK:STDOUT: %.loc16_3.16: i32 = int_literal 4 -// CHECK:STDOUT: %.loc16_3.17: ref i32 = array_index %.loc16_3.3, %.loc16_3.16 -// CHECK:STDOUT: %.loc16_3.18: init i32 = initialize_from %.loc15 to %.loc16_3.17 -// CHECK:STDOUT: %.loc16_3.19: init [i32; 5] = array_init %.loc16_3.2, (%.loc16_3.6, %.loc16_3.9, %.loc16_3.12, %.loc16_3.15, %.loc16_3.18) to %.loc16_3.3 -// CHECK:STDOUT: assign %ints, %.loc16_3.19 +// CHECK:STDOUT: %.loc10_7: ref [i32; 5] = splice_block %ints {} +// CHECK:STDOUT: %.loc16_3.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc16_3.4: ref i32 = array_index %.loc10_7, %.loc16_3.3 +// CHECK:STDOUT: %.loc16_3.5: init i32 = initialize_from %.loc11 to %.loc16_3.4 +// CHECK:STDOUT: %.loc16_3.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc16_3.7: ref i32 = array_index %.loc10_7, %.loc16_3.6 +// CHECK:STDOUT: %.loc16_3.8: init i32 = initialize_from %.loc12 to %.loc16_3.7 +// CHECK:STDOUT: %.loc16_3.9: i32 = int_literal 2 +// CHECK:STDOUT: %.loc16_3.10: ref i32 = array_index %.loc10_7, %.loc16_3.9 +// CHECK:STDOUT: %.loc16_3.11: init i32 = initialize_from %.loc13 to %.loc16_3.10 +// CHECK:STDOUT: %.loc16_3.12: i32 = int_literal 3 +// CHECK:STDOUT: %.loc16_3.13: ref i32 = array_index %.loc10_7, %.loc16_3.12 +// CHECK:STDOUT: %.loc16_3.14: init i32 = initialize_from %.loc14 to %.loc16_3.13 +// CHECK:STDOUT: %.loc16_3.15: i32 = int_literal 4 +// CHECK:STDOUT: %.loc16_3.16: ref i32 = array_index %.loc10_7, %.loc16_3.15 +// CHECK:STDOUT: %.loc16_3.17: init i32 = initialize_from %.loc15 to %.loc16_3.16 +// CHECK:STDOUT: %.loc16_3.18: init [i32; 5] = array_init %.loc16_3.2, (%.loc16_3.5, %.loc16_3.8, %.loc16_3.11, %.loc16_3.14, %.loc16_3.17) to %.loc10_7 +// CHECK:STDOUT: assign %ints, %.loc16_3.18 // CHECK:STDOUT: %.loc17_21: i32 = int_literal 7 // CHECK:STDOUT: %.loc17_22: type = array_type %.loc17_21, f64 // CHECK:STDOUT: %floats: ref [f64; 7] = var "floats" @@ -71,29 +71,29 @@ fn F() { // CHECK:STDOUT: %.loc24: f64 = real_literal 399999999999999999930e39999999999999999992 // CHECK:STDOUT: %.loc25_3.1: type = tuple_type (f64, f64, f64, f64, f64, f64, f64) // CHECK:STDOUT: %.loc25_3.2: (f64, f64, f64, f64, f64, f64, f64) = tuple_literal (%.loc18, %.loc19, %.loc20, %.loc21, %.loc22, %.loc23, %.loc24) -// CHECK:STDOUT: %.loc25_3.3: ref [f64; 7] = stub_reference %floats -// CHECK:STDOUT: %.loc25_3.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc25_3.5: ref f64 = array_index %.loc25_3.3, %.loc25_3.4 -// CHECK:STDOUT: %.loc25_3.6: init f64 = initialize_from %.loc18 to %.loc25_3.5 -// CHECK:STDOUT: %.loc25_3.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc25_3.8: ref f64 = array_index %.loc25_3.3, %.loc25_3.7 -// CHECK:STDOUT: %.loc25_3.9: init f64 = initialize_from %.loc19 to %.loc25_3.8 -// CHECK:STDOUT: %.loc25_3.10: i32 = int_literal 2 -// CHECK:STDOUT: %.loc25_3.11: ref f64 = array_index %.loc25_3.3, %.loc25_3.10 -// CHECK:STDOUT: %.loc25_3.12: init f64 = initialize_from %.loc20 to %.loc25_3.11 -// CHECK:STDOUT: %.loc25_3.13: i32 = int_literal 3 -// CHECK:STDOUT: %.loc25_3.14: ref f64 = array_index %.loc25_3.3, %.loc25_3.13 -// CHECK:STDOUT: %.loc25_3.15: init f64 = initialize_from %.loc21 to %.loc25_3.14 -// CHECK:STDOUT: %.loc25_3.16: i32 = int_literal 4 -// CHECK:STDOUT: %.loc25_3.17: ref f64 = array_index %.loc25_3.3, %.loc25_3.16 -// CHECK:STDOUT: %.loc25_3.18: init f64 = initialize_from %.loc22 to %.loc25_3.17 -// CHECK:STDOUT: %.loc25_3.19: i32 = int_literal 5 -// CHECK:STDOUT: %.loc25_3.20: ref f64 = array_index %.loc25_3.3, %.loc25_3.19 -// CHECK:STDOUT: %.loc25_3.21: init f64 = initialize_from %.loc23 to %.loc25_3.20 -// CHECK:STDOUT: %.loc25_3.22: i32 = int_literal 6 -// CHECK:STDOUT: %.loc25_3.23: ref f64 = array_index %.loc25_3.3, %.loc25_3.22 -// CHECK:STDOUT: %.loc25_3.24: init f64 = initialize_from %.loc24 to %.loc25_3.23 -// CHECK:STDOUT: %.loc25_3.25: init [f64; 7] = array_init %.loc25_3.2, (%.loc25_3.6, %.loc25_3.9, %.loc25_3.12, %.loc25_3.15, %.loc25_3.18, %.loc25_3.21, %.loc25_3.24) to %.loc25_3.3 -// CHECK:STDOUT: assign %floats, %.loc25_3.25 +// CHECK:STDOUT: %.loc17_7: ref [f64; 7] = splice_block %floats {} +// CHECK:STDOUT: %.loc25_3.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc25_3.4: ref f64 = array_index %.loc17_7, %.loc25_3.3 +// CHECK:STDOUT: %.loc25_3.5: init f64 = initialize_from %.loc18 to %.loc25_3.4 +// CHECK:STDOUT: %.loc25_3.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc25_3.7: ref f64 = array_index %.loc17_7, %.loc25_3.6 +// CHECK:STDOUT: %.loc25_3.8: init f64 = initialize_from %.loc19 to %.loc25_3.7 +// CHECK:STDOUT: %.loc25_3.9: i32 = int_literal 2 +// CHECK:STDOUT: %.loc25_3.10: ref f64 = array_index %.loc17_7, %.loc25_3.9 +// CHECK:STDOUT: %.loc25_3.11: init f64 = initialize_from %.loc20 to %.loc25_3.10 +// CHECK:STDOUT: %.loc25_3.12: i32 = int_literal 3 +// CHECK:STDOUT: %.loc25_3.13: ref f64 = array_index %.loc17_7, %.loc25_3.12 +// CHECK:STDOUT: %.loc25_3.14: init f64 = initialize_from %.loc21 to %.loc25_3.13 +// CHECK:STDOUT: %.loc25_3.15: i32 = int_literal 4 +// CHECK:STDOUT: %.loc25_3.16: ref f64 = array_index %.loc17_7, %.loc25_3.15 +// CHECK:STDOUT: %.loc25_3.17: init f64 = initialize_from %.loc22 to %.loc25_3.16 +// CHECK:STDOUT: %.loc25_3.18: i32 = int_literal 5 +// CHECK:STDOUT: %.loc25_3.19: ref f64 = array_index %.loc17_7, %.loc25_3.18 +// CHECK:STDOUT: %.loc25_3.20: init f64 = initialize_from %.loc23 to %.loc25_3.19 +// CHECK:STDOUT: %.loc25_3.21: i32 = int_literal 6 +// CHECK:STDOUT: %.loc25_3.22: ref f64 = array_index %.loc17_7, %.loc25_3.21 +// CHECK:STDOUT: %.loc25_3.23: init f64 = initialize_from %.loc24 to %.loc25_3.22 +// CHECK:STDOUT: %.loc25_3.24: init [f64; 7] = array_init %.loc25_3.2, (%.loc25_3.5, %.loc25_3.8, %.loc25_3.11, %.loc25_3.14, %.loc25_3.17, %.loc25_3.20, %.loc25_3.23) to %.loc17_7 +// CHECK:STDOUT: assign %floats, %.loc25_3.24 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/expression_category/in_place_tuple_initialization.carbon b/toolchain/check/testdata/expression_category/in_place_tuple_initialization.carbon index c45be3b7bef2..311c1024e5a4 100644 --- a/toolchain/check/testdata/expression_category/in_place_tuple_initialization.carbon +++ b/toolchain/check/testdata/expression_category/in_place_tuple_initialization.carbon @@ -28,15 +28,15 @@ fn H() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10_19: (type, type) = tuple_literal (i32, i32) // CHECK:STDOUT: %v: ref (i32, i32) = var "v" -// CHECK:STDOUT: %.loc10_24.1: ref (i32, i32) = stub_reference %v -// CHECK:STDOUT: %.loc10_24.2: init (i32, i32) = call @F() to %.loc10_24.1 -// CHECK:STDOUT: assign %v, %.loc10_24.2 -// CHECK:STDOUT: %.loc11_8.1: ref (i32, i32) = stub_reference %v -// CHECK:STDOUT: %.loc11_8.2: init (i32, i32) = call @F() to %.loc11_8.1 -// CHECK:STDOUT: assign %v, %.loc11_8.2 -// CHECK:STDOUT: %.loc12_11.1: ref (i32, i32) = stub_reference %return -// CHECK:STDOUT: %.loc12_11.2: init (i32, i32) = call @F() to %.loc12_11.1 -// CHECK:STDOUT: return %.loc12_11.2 +// CHECK:STDOUT: %.loc10_7.1: ref (i32, i32) = splice_block %v {} +// CHECK:STDOUT: %.loc10_24: init (i32, i32) = call @F() to %.loc10_7.1 +// CHECK:STDOUT: assign %v, %.loc10_24 +// CHECK:STDOUT: %.loc10_7.2: ref (i32, i32) = splice_block %v {} +// CHECK:STDOUT: %.loc11: init (i32, i32) = call @F() to %.loc10_7.2 +// CHECK:STDOUT: assign %v, %.loc11 +// CHECK:STDOUT: %.loc9: ref (i32, i32) = splice_block %return {} +// CHECK:STDOUT: %.loc12: init (i32, i32) = call @F() to %.loc9 +// CHECK:STDOUT: return %.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() -> i32 { diff --git a/toolchain/check/testdata/index/array_element_access.carbon b/toolchain/check/testdata/index/array_element_access.carbon index c7c340abe9b9..1e637db389de 100644 --- a/toolchain/check/testdata/index/array_element_access.carbon +++ b/toolchain/check/testdata/index/array_element_access.carbon @@ -17,15 +17,15 @@ var d: i32 = a[b]; // CHECK:STDOUT: %.loc7_24: i32 = int_literal 24 // CHECK:STDOUT: %.loc7_26.1: type = tuple_type (i32, i32) // CHECK:STDOUT: %.loc7_26.2: (i32, i32) = tuple_literal (%.loc7_20, %.loc7_24) -// CHECK:STDOUT: %.loc7_26.3: ref [i32; 2] = stub_reference %a -// CHECK:STDOUT: %.loc7_26.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_26.5: ref i32 = array_index %.loc7_26.3, %.loc7_26.4 -// CHECK:STDOUT: %.loc7_26.6: init i32 = initialize_from %.loc7_20 to %.loc7_26.5 -// CHECK:STDOUT: %.loc7_26.7: i32 = int_literal 1 -// CHECK:STDOUT: %.loc7_26.8: ref i32 = array_index %.loc7_26.3, %.loc7_26.7 -// CHECK:STDOUT: %.loc7_26.9: init i32 = initialize_from %.loc7_24 to %.loc7_26.8 -// CHECK:STDOUT: %.loc7_26.10: init [i32; 2] = array_init %.loc7_26.2, (%.loc7_26.6, %.loc7_26.9) to %.loc7_26.3 -// CHECK:STDOUT: assign %a, %.loc7_26.10 +// CHECK:STDOUT: %.loc7_5: ref [i32; 2] = splice_block %a {} +// CHECK:STDOUT: %.loc7_26.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_26.4: ref i32 = array_index %.loc7_5, %.loc7_26.3 +// CHECK:STDOUT: %.loc7_26.5: init i32 = initialize_from %.loc7_20 to %.loc7_26.4 +// CHECK:STDOUT: %.loc7_26.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_26.7: ref i32 = array_index %.loc7_5, %.loc7_26.6 +// CHECK:STDOUT: %.loc7_26.8: init i32 = initialize_from %.loc7_24 to %.loc7_26.7 +// CHECK:STDOUT: %.loc7_26.9: init [i32; 2] = array_init %.loc7_26.2, (%.loc7_26.5, %.loc7_26.8) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_26.9 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc8_14: i32 = int_literal 1 // CHECK:STDOUT: assign %b, %.loc8_14 diff --git a/toolchain/check/testdata/index/fail_array_large_index.carbon b/toolchain/check/testdata/index/fail_array_large_index.carbon index 83d0d7954530..78c52fa12c31 100644 --- a/toolchain/check/testdata/index/fail_array_large_index.carbon +++ b/toolchain/check/testdata/index/fail_array_large_index.carbon @@ -17,12 +17,12 @@ var b: i32 = a[0xFFFFFFFFFFFFFFFFF]; // CHECK:STDOUT: %.loc7_20: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_23.1: type = tuple_type (i32) // CHECK:STDOUT: %.loc7_23.2: (i32,) = tuple_literal (%.loc7_20) -// CHECK:STDOUT: %.loc7_23.3: ref [i32; 1] = stub_reference %a -// CHECK:STDOUT: %.loc7_23.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_23.5: ref i32 = array_index %.loc7_23.3, %.loc7_23.4 -// CHECK:STDOUT: %.loc7_23.6: init i32 = initialize_from %.loc7_20 to %.loc7_23.5 -// CHECK:STDOUT: %.loc7_23.7: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.6) to %.loc7_23.3 -// CHECK:STDOUT: assign %a, %.loc7_23.7 +// CHECK:STDOUT: %.loc7_5: ref [i32; 1] = splice_block %a {} +// CHECK:STDOUT: %.loc7_23.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_23.4: ref i32 = array_index %.loc7_5, %.loc7_23.3 +// CHECK:STDOUT: %.loc7_23.5: init i32 = initialize_from %.loc7_20 to %.loc7_23.4 +// CHECK:STDOUT: %.loc7_23.6: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.5) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_23.6 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc11_16: i32 = int_literal 295147905179352825855 // CHECK:STDOUT: %.loc11_35.1: ref i32 = array_index %a, diff --git a/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon b/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon index 281bce49438c..03b96d7de6bf 100644 --- a/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon +++ b/toolchain/check/testdata/index/fail_array_non_int_indexing.carbon @@ -17,12 +17,12 @@ var b: i32 = a[2.6]; // CHECK:STDOUT: %.loc7_20: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_23.1: type = tuple_type (i32) // CHECK:STDOUT: %.loc7_23.2: (i32,) = tuple_literal (%.loc7_20) -// CHECK:STDOUT: %.loc7_23.3: ref [i32; 1] = stub_reference %a -// CHECK:STDOUT: %.loc7_23.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_23.5: ref i32 = array_index %.loc7_23.3, %.loc7_23.4 -// CHECK:STDOUT: %.loc7_23.6: init i32 = initialize_from %.loc7_20 to %.loc7_23.5 -// CHECK:STDOUT: %.loc7_23.7: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.6) to %.loc7_23.3 -// CHECK:STDOUT: assign %a, %.loc7_23.7 +// CHECK:STDOUT: %.loc7_5: ref [i32; 1] = splice_block %a {} +// CHECK:STDOUT: %.loc7_23.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_23.4: ref i32 = array_index %.loc7_5, %.loc7_23.3 +// CHECK:STDOUT: %.loc7_23.5: init i32 = initialize_from %.loc7_20 to %.loc7_23.4 +// CHECK:STDOUT: %.loc7_23.6: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.5) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_23.6 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc11_16: f64 = real_literal 26e-1 // CHECK:STDOUT: %.loc11_19.1: ref i32 = array_index %a, diff --git a/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon b/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon index 125d0bc3d570..633d6a1aa270 100644 --- a/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon +++ b/toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon @@ -17,12 +17,12 @@ var b: i32 = a[2]; // CHECK:STDOUT: %.loc7_20: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_23.1: type = tuple_type (i32) // CHECK:STDOUT: %.loc7_23.2: (i32,) = tuple_literal (%.loc7_20) -// CHECK:STDOUT: %.loc7_23.3: ref [i32; 1] = stub_reference %a -// CHECK:STDOUT: %.loc7_23.4: i32 = int_literal 0 -// CHECK:STDOUT: %.loc7_23.5: ref i32 = array_index %.loc7_23.3, %.loc7_23.4 -// CHECK:STDOUT: %.loc7_23.6: init i32 = initialize_from %.loc7_20 to %.loc7_23.5 -// CHECK:STDOUT: %.loc7_23.7: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.6) to %.loc7_23.3 -// CHECK:STDOUT: assign %a, %.loc7_23.7 +// CHECK:STDOUT: %.loc7_5: ref [i32; 1] = splice_block %a {} +// CHECK:STDOUT: %.loc7_23.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_23.4: ref i32 = array_index %.loc7_5, %.loc7_23.3 +// CHECK:STDOUT: %.loc7_23.5: init i32 = initialize_from %.loc7_20 to %.loc7_23.4 +// CHECK:STDOUT: %.loc7_23.6: init [i32; 1] = array_init %.loc7_23.2, (%.loc7_23.5) to %.loc7_5 +// CHECK:STDOUT: assign %a, %.loc7_23.6 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc11_16: i32 = int_literal 2 // CHECK:STDOUT: %.loc11_17.1: ref i32 = array_index %a, diff --git a/toolchain/check/testdata/index/fail_tuple_large_index.carbon b/toolchain/check/testdata/index/fail_tuple_large_index.carbon index f401679ecdaf..a2ef0201c35d 100644 --- a/toolchain/check/testdata/index/fail_tuple_large_index.carbon +++ b/toolchain/check/testdata/index/fail_tuple_large_index.carbon @@ -18,9 +18,8 @@ var c: i32 = b[0xFFFFFFFFFFFFFFFFF]; // CHECK:STDOUT: %a: ref (i32,) = var "a" // CHECK:STDOUT: %.loc7_18: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_21: (i32,) = tuple_literal (%.loc7_18) -// CHECK:STDOUT: %.loc7_22.1: ref i32 = tuple_access %a, member0 -// CHECK:STDOUT: %.loc7_22.2: init (i32,) = tuple_init %.loc7_21, (%.loc7_18) -// CHECK:STDOUT: assign %a, %.loc7_22.2 +// CHECK:STDOUT: %.loc7_22: init (i32,) = tuple_init %.loc7_21, (%.loc7_18) +// CHECK:STDOUT: assign %a, %.loc7_22 // CHECK:STDOUT: %.loc8: (type,) = tuple_literal (i32) // CHECK:STDOUT: %b: ref (i32,) = var "b" // CHECK:STDOUT: %.loc7_5: (i32,) = bind_value %a diff --git a/toolchain/check/testdata/index/tuple_element_access.carbon b/toolchain/check/testdata/index/tuple_element_access.carbon index fc15d2a9fcb4..1c2ac7a9755e 100644 --- a/toolchain/check/testdata/index/tuple_element_access.carbon +++ b/toolchain/check/testdata/index/tuple_element_access.carbon @@ -15,9 +15,8 @@ var c: i32 = b[0]; // CHECK:STDOUT: %a: ref (i32,) = var "a" // CHECK:STDOUT: %.loc7_18: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_21: (i32,) = tuple_literal (%.loc7_18) -// CHECK:STDOUT: %.loc7_22.1: ref i32 = tuple_access %a, member0 -// CHECK:STDOUT: %.loc7_22.2: init (i32,) = tuple_init %.loc7_21, (%.loc7_18) -// CHECK:STDOUT: assign %a, %.loc7_22.2 +// CHECK:STDOUT: %.loc7_22: init (i32,) = tuple_init %.loc7_21, (%.loc7_18) +// CHECK:STDOUT: assign %a, %.loc7_22 // CHECK:STDOUT: %.loc8: (type,) = tuple_literal (i32) // CHECK:STDOUT: %b: ref (i32,) = var "b" // CHECK:STDOUT: %.loc7_5: (i32,) = bind_value %a diff --git a/toolchain/check/testdata/struct/fail_member_access_type.carbon b/toolchain/check/testdata/struct/fail_member_access_type.carbon index 3ddfe37b1b08..0208a71641f3 100644 --- a/toolchain/check/testdata/struct/fail_member_access_type.carbon +++ b/toolchain/check/testdata/struct/fail_member_access_type.carbon @@ -15,9 +15,8 @@ var y: i32 = x.b; // CHECK:STDOUT: %x: ref {.a: f64} = var "x" // CHECK:STDOUT: %.loc7_26: f64 = real_literal 40e-1 // CHECK:STDOUT: %.loc7_29: {.a: f64} = struct_literal (%.loc7_26) -// CHECK:STDOUT: %.loc7_30.1: ref f64 = struct_access %x, member0 -// CHECK:STDOUT: %.loc7_30.2: init {.a: f64} = struct_init %.loc7_29, (%.loc7_26) -// CHECK:STDOUT: assign %x, %.loc7_30.2 +// CHECK:STDOUT: %.loc7_30: init {.a: f64} = struct_init %.loc7_29, (%.loc7_26) +// CHECK:STDOUT: assign %x, %.loc7_30 // CHECK:STDOUT: %y: ref i32 = var "y" // CHECK:STDOUT: assign %y, // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/fail_non_member_access.carbon b/toolchain/check/testdata/struct/fail_non_member_access.carbon index 5ac9fbd6add0..0ffc2bb6dbe1 100644 --- a/toolchain/check/testdata/struct/fail_non_member_access.carbon +++ b/toolchain/check/testdata/struct/fail_non_member_access.carbon @@ -15,9 +15,8 @@ var y: i32 = x.b; // CHECK:STDOUT: %x: ref {.a: i32} = var "x" // CHECK:STDOUT: %.loc7_26: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_27: {.a: i32} = struct_literal (%.loc7_26) -// CHECK:STDOUT: %.loc7_28.1: ref i32 = struct_access %x, member0 -// CHECK:STDOUT: %.loc7_28.2: init {.a: i32} = struct_init %.loc7_27, (%.loc7_26) -// CHECK:STDOUT: assign %x, %.loc7_28.2 +// CHECK:STDOUT: %.loc7_28: init {.a: i32} = struct_init %.loc7_27, (%.loc7_26) +// CHECK:STDOUT: assign %x, %.loc7_28 // CHECK:STDOUT: %y: ref i32 = var "y" // CHECK:STDOUT: assign %y, // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/nested_struct_in_place.carbon b/toolchain/check/testdata/struct/nested_struct_in_place.carbon new file mode 100644 index 000000000000..dba62433fce9 --- /dev/null +++ b/toolchain/check/testdata/struct/nested_struct_in_place.carbon @@ -0,0 +1,34 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: {.a: (i32, i32, i32), .b: (i32, i32, i32)} = {.a = F(), .b = F()}; +} + +// CHECK:STDOUT: file "nested_struct_in_place.carbon" { +// CHECK:STDOUT: %.loc7 = fn_decl @F +// CHECK:STDOUT: %.loc9 = fn_decl @G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> %return: (i32, i32, i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_29: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc10_50: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc10_51: type = struct_type {.a: (i32, i32, i32), .b: (i32, i32, i32)} +// CHECK:STDOUT: %v: ref {.a: (i32, i32, i32), .b: (i32, i32, i32)} = var "v" +// CHECK:STDOUT: %.loc10_75.1: ref (i32, i32, i32) = struct_access %v, member0 +// CHECK:STDOUT: %.loc10_62: init (i32, i32, i32) = call @F() to %.loc10_75.1 +// CHECK:STDOUT: %.loc10_75.2: ref (i32, i32, i32) = struct_access %v, member1 +// CHECK:STDOUT: %.loc10_72: init (i32, i32, i32) = call @F() to %.loc10_75.2 +// CHECK:STDOUT: %.loc10_74: {.a: (i32, i32, i32), .b: (i32, i32, i32)} = struct_literal (%.loc10_62, %.loc10_72) +// CHECK:STDOUT: %.loc10_75.3: init {.a: (i32, i32, i32), .b: (i32, i32, i32)} = struct_init %.loc10_74, (%.loc10_62, %.loc10_72) +// CHECK:STDOUT: assign %v, %.loc10_75.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/one_entry.carbon b/toolchain/check/testdata/struct/one_entry.carbon index d68296c174f8..7f1f60131b33 100644 --- a/toolchain/check/testdata/struct/one_entry.carbon +++ b/toolchain/check/testdata/struct/one_entry.carbon @@ -12,9 +12,8 @@ var y: {.a: i32} = x; // CHECK:STDOUT: %x: ref {.a: i32} = var "x" // CHECK:STDOUT: %.loc7_26: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_27: {.a: i32} = struct_literal (%.loc7_26) -// CHECK:STDOUT: %.loc7_28.1: ref i32 = struct_access %x, member0 -// CHECK:STDOUT: %.loc7_28.2: init {.a: i32} = struct_init %.loc7_27, (%.loc7_26) -// CHECK:STDOUT: assign %x, %.loc7_28.2 +// CHECK:STDOUT: %.loc7_28: init {.a: i32} = struct_init %.loc7_27, (%.loc7_26) +// CHECK:STDOUT: assign %x, %.loc7_28 // CHECK:STDOUT: %.loc8: type = struct_type {.a: i32} // CHECK:STDOUT: %y: ref {.a: i32} = var "y" // CHECK:STDOUT: %.loc7_5: {.a: i32} = bind_value %x diff --git a/toolchain/check/testdata/struct/tuple_as_element.carbon b/toolchain/check/testdata/struct/tuple_as_element.carbon index b0ba61ef11fc..5fe83ab53fe3 100644 --- a/toolchain/check/testdata/struct/tuple_as_element.carbon +++ b/toolchain/check/testdata/struct/tuple_as_element.carbon @@ -19,12 +19,11 @@ var y: {.a: i32, .b: (i32,)} = x; // CHECK:STDOUT: %.loc7_50: {.a: i32, .b: (i32,)} = struct_literal (%.loc7_38, %.loc7_49) // CHECK:STDOUT: %.loc7_51.1: ref i32 = struct_access %x, member0 // CHECK:STDOUT: %.loc7_51.2: init i32 = initialize_from %.loc7_38 to %.loc7_51.1 -// CHECK:STDOUT: %.loc7_51.3: ref (i32,) = struct_access %x, member1 -// CHECK:STDOUT: %.loc7_51.4: ref i32 = tuple_access %.loc7_51.3, member0 -// CHECK:STDOUT: %.loc7_51.5: init (i32,) = tuple_init %.loc7_49, (%.loc7_47) -// CHECK:STDOUT: %.loc7_51.6: init (i32,) = initialize_from %.loc7_51.5 to %.loc7_51.3 -// CHECK:STDOUT: %.loc7_51.7: init {.a: i32, .b: (i32,)} = struct_init %.loc7_50, (%.loc7_51.2, %.loc7_51.6) -// CHECK:STDOUT: assign %x, %.loc7_51.7 +// CHECK:STDOUT: %.loc7_51.3: init (i32,) = tuple_init %.loc7_49, (%.loc7_47) +// CHECK:STDOUT: %.loc7_51.4: ref (i32,) = struct_access %x, member1 +// CHECK:STDOUT: %.loc7_51.5: init (i32,) = initialize_from %.loc7_51.3 to %.loc7_51.4 +// CHECK:STDOUT: %.loc7_51.6: init {.a: i32, .b: (i32,)} = struct_init %.loc7_50, (%.loc7_51.2, %.loc7_51.5) +// CHECK:STDOUT: assign %x, %.loc7_51.6 // CHECK:STDOUT: %.loc8_27: (type,) = tuple_literal (i32) // CHECK:STDOUT: %.loc8_28: type = struct_type {.a: i32, .b: (i32,)} // CHECK:STDOUT: %y: ref {.a: i32, .b: (i32,)} = var "y" diff --git a/toolchain/check/testdata/tuples/nested_tuple_in_place.carbon b/toolchain/check/testdata/tuples/nested_tuple_in_place.carbon new file mode 100644 index 000000000000..5f4c1f6fd6aa --- /dev/null +++ b/toolchain/check/testdata/tuples/nested_tuple_in_place.carbon @@ -0,0 +1,62 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: ((i32, i32, i32), (i32, i32, i32)) = (F(), F()); +} + +fn H() { + var v: (i32, (i32, i32, i32), i32) = (1, F(), 2); +} + +// CHECK:STDOUT: file "nested_tuple_in_place.carbon" { +// CHECK:STDOUT: %.loc7 = fn_decl @F +// CHECK:STDOUT: %.loc9 = fn_decl @G +// CHECK:STDOUT: %.loc13 = fn_decl @H +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> %return: (i32, i32, i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_25: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc10_42: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc10_43.1: type = tuple_type ((type, type, type), (type, type, type)) +// CHECK:STDOUT: %.loc10_43.2: ((type, type, type), (type, type, type)) = tuple_literal (%.loc10_25, %.loc10_42) +// CHECK:STDOUT: %.loc10_43.3: type = tuple_type ((i32, i32, i32), (i32, i32, i32)) +// CHECK:STDOUT: %v: ref ((i32, i32, i32), (i32, i32, i32)) = var "v" +// CHECK:STDOUT: %.loc10_57.1: ref (i32, i32, i32) = tuple_access %v, member0 +// CHECK:STDOUT: %.loc10_49: init (i32, i32, i32) = call @F() to %.loc10_57.1 +// CHECK:STDOUT: %.loc10_57.2: ref (i32, i32, i32) = tuple_access %v, member1 +// CHECK:STDOUT: %.loc10_54: init (i32, i32, i32) = call @F() to %.loc10_57.2 +// CHECK:STDOUT: %.loc10_56: ((i32, i32, i32), (i32, i32, i32)) = tuple_literal (%.loc10_49, %.loc10_54) +// CHECK:STDOUT: %.loc10_57.3: init ((i32, i32, i32), (i32, i32, i32)) = tuple_init %.loc10_56, (%.loc10_49, %.loc10_54) +// CHECK:STDOUT: assign %v, %.loc10_57.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @H() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc14_30: (type, type, type) = tuple_literal (i32, i32, i32) +// CHECK:STDOUT: %.loc14_36.1: type = tuple_type (type, (type, type, type), type) +// CHECK:STDOUT: %.loc14_36.2: (type, (type, type, type), type) = tuple_literal (i32, %.loc14_30, i32) +// CHECK:STDOUT: %.loc14_36.3: type = tuple_type (i32, (i32, i32, i32), i32) +// CHECK:STDOUT: %v: ref (i32, (i32, i32, i32), i32) = var "v" +// CHECK:STDOUT: %.loc14_41: i32 = int_literal 1 +// CHECK:STDOUT: %.loc14_51.1: ref (i32, i32, i32) = tuple_access %v, member1 +// CHECK:STDOUT: %.loc14_45: init (i32, i32, i32) = call @F() to %.loc14_51.1 +// CHECK:STDOUT: %.loc14_49: i32 = int_literal 2 +// CHECK:STDOUT: %.loc14_50: (i32, (i32, i32, i32), i32) = tuple_literal (%.loc14_41, %.loc14_45, %.loc14_49) +// CHECK:STDOUT: %.loc14_51.2: ref i32 = tuple_access %v, member0 +// CHECK:STDOUT: %.loc14_51.3: init i32 = initialize_from %.loc14_41 to %.loc14_51.2 +// CHECK:STDOUT: %.loc14_51.4: ref i32 = tuple_access %v, member2 +// CHECK:STDOUT: %.loc14_51.5: init i32 = initialize_from %.loc14_49 to %.loc14_51.4 +// CHECK:STDOUT: %.loc14_51.6: init (i32, (i32, i32, i32), i32) = tuple_init %.loc14_50, (%.loc14_51.3, %.loc14_45, %.loc14_51.5) +// CHECK:STDOUT: assign %v, %.loc14_51.6 +// CHECK:STDOUT: return +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/tuples/one_element.carbon b/toolchain/check/testdata/tuples/one_element.carbon index 7caf473b7bf5..33924adc46be 100644 --- a/toolchain/check/testdata/tuples/one_element.carbon +++ b/toolchain/check/testdata/tuples/one_element.carbon @@ -14,9 +14,8 @@ var y: (i32,) = x; // CHECK:STDOUT: %x: ref (i32,) = var "x" // CHECK:STDOUT: %.loc7_18: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_20: (i32,) = tuple_literal (%.loc7_18) -// CHECK:STDOUT: %.loc7_21.1: ref i32 = tuple_access %x, member0 -// CHECK:STDOUT: %.loc7_21.2: init (i32,) = tuple_init %.loc7_20, (%.loc7_18) -// CHECK:STDOUT: assign %x, %.loc7_21.2 +// CHECK:STDOUT: %.loc7_21: init (i32,) = tuple_init %.loc7_20, (%.loc7_18) +// CHECK:STDOUT: assign %x, %.loc7_21 // CHECK:STDOUT: %.loc8: (type,) = tuple_literal (i32) // CHECK:STDOUT: %y: ref (i32,) = var "y" // CHECK:STDOUT: %.loc7_5: (i32,) = bind_value %x diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 7f19d31928d2..d511f1688668 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -139,7 +139,7 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) } llvm::Function* llvm_function = GetFunction(function_id); - FunctionContext function_lowering(*this, llvm_function); + FunctionContext function_lowering(*this, llvm_function, vlog_stream_); const bool has_return_slot = function.return_slot_id.is_valid(); @@ -174,19 +174,7 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) // Keep the LLVM blocks in lexical order. llvm_block->moveBefore(llvm_function->end()); function_lowering.builder().SetInsertPoint(llvm_block); - for (const auto& node_id : semantics_ir().GetNodeBlock(block_id)) { - auto node = semantics_ir().GetNode(node_id); - CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n"; - // clang warns on unhandled enum values; clang-tidy is incorrect here. - // NOLINTNEXTLINE(bugprone-switch-missing-default-case) - switch (node.kind()) { -#define CARBON_SEMANTICS_NODE_KIND(Name) \ - case SemIR::NodeKind::Name: \ - Handle##Name(function_lowering, node_id, node); \ - break; -#include "toolchain/sem_ir/node_kind.def" - } - } + function_lowering.LowerBlock(block_id); } } diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 11e916c2a174..839361c90acc 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -4,15 +4,18 @@ #include "toolchain/lower/function_context.h" +#include "common/vlog.h" #include "toolchain/sem_ir/file.h" namespace Carbon::Lower { FunctionContext::FunctionContext(FileContext& file_context, - llvm::Function* function) + llvm::Function* function, + llvm::raw_ostream* vlog_stream) : file_context_(&file_context), function_(function), - builder_(file_context.llvm_context()) {} + builder_(file_context.llvm_context()), + vlog_stream_(vlog_stream) {} auto FunctionContext::GetBlock(SemIR::NodeBlockId block_id) -> llvm::BasicBlock* { @@ -34,6 +37,22 @@ auto FunctionContext::TryToReuseBlock(SemIR::NodeBlockId block_id, return true; } +auto FunctionContext::LowerBlock(SemIR::NodeBlockId block_id) -> void { + for (const auto& node_id : semantics_ir().GetNodeBlock(block_id)) { + auto node = semantics_ir().GetNode(node_id); + CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n"; + // clang warns on unhandled enum values; clang-tidy is incorrect here. + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) + switch (node.kind()) { +#define CARBON_SEMANTICS_NODE_KIND(Name) \ + case SemIR::NodeKind::Name: \ + Handle##Name(*this, node_id, node); \ + break; +#include "toolchain/sem_ir/node_kind.def" + } + } +} + auto FunctionContext::GetBlockArg(SemIR::NodeBlockId block_id, SemIR::TypeId type_id) -> llvm::PHINode* { llvm::BasicBlock* block = GetBlock(block_id); diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index ae1c9b79e20f..9b2df0b2f48f 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -18,7 +18,8 @@ namespace Carbon::Lower { // `llvm::Function` definition. class FunctionContext { public: - explicit FunctionContext(FileContext& file_context, llvm::Function* function); + explicit FunctionContext(FileContext& file_context, llvm::Function* function, + llvm::raw_ostream* vlog_stream); // Returns a basic block corresponding to the start of the given semantics // block, and enqueues it for emission. @@ -30,6 +31,9 @@ class FunctionContext { auto TryToReuseBlock(SemIR::NodeBlockId block_id, llvm::BasicBlock* block) -> bool; + // Builds LLVM IR for the sequence of instructions in `block_id`. + auto LowerBlock(SemIR::NodeBlockId block_id) -> void; + // Returns a phi node corresponding to the block argument of the given basic // block. auto GetBlockArg(SemIR::NodeBlockId block_id, SemIR::TypeId type_id) @@ -124,6 +128,9 @@ class FunctionContext { llvm::IRBuilder<> builder_; + // The optional vlog stream. + llvm::raw_ostream* vlog_stream_; + // Maps a function's SemIR::File blocks to lowered blocks. llvm::DenseMap blocks_; diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 03a8d693ce1d..c93b0458575f 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -182,6 +182,7 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, context.builder().CreateCall(llvm_function, args); // The value of a function call with a void return type shouldn't used, but // StubReference needs a value to propagate. + // TODO: Remove this now the StubReferences are gone. context.SetLocal(node_id, llvm::PoisonValue::get(context.GetType(node.type_id()))); } else { @@ -274,6 +275,13 @@ auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/, } } +auto HandleSpliceBlock(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + auto [block_id, result_id] = node.GetAsSpliceBlock(); + context.LowerBlock(block_id); + context.SetLocal(node_id, context.GetLocal(result_id)); +} + auto HandleStringLiteral(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, SemIR::Node node) -> void { CARBON_FATAL() << "TODO: Add support: " << node; @@ -389,11 +397,6 @@ auto HandleStructTypeField(FunctionContext& /*context*/, // No action to take. } -auto HandleStubReference(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal(node_id, context.GetLocal(node.GetAsStubReference())); -} - auto HandleTupleAccess(FunctionContext& context, SemIR::NodeId node_id, SemIR::Node node) -> void { auto [tuple_node_id, index] = node.GetAsTupleAccess(); diff --git a/toolchain/lower/handle_expression_category.cpp b/toolchain/lower/handle_expression_category.cpp index ff78ec28c77c..3b48becb8e59 100644 --- a/toolchain/lower/handle_expression_category.cpp +++ b/toolchain/lower/handle_expression_category.cpp @@ -14,6 +14,7 @@ auto HandleBindValue(FunctionContext& context, SemIR::NodeId node_id, case SemIR::ValueRepresentation::None: // Nothing should use this value, but StubReference needs a value to // propagate. + // TODO: Remove this now the StubReferences are gone. context.SetLocal(node_id, llvm::PoisonValue::get(context.GetType(node.type_id()))); break; diff --git a/toolchain/lower/testdata/array/array_in_place.carbon b/toolchain/lower/testdata/array/array_in_place.carbon new file mode 100644 index 000000000000..9bda376d9c6c --- /dev/null +++ b/toolchain/lower/testdata/array/array_in_place.carbon @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: [(i32, i32, i32); 2] = (F(), F()); +} + +// CHECK:STDOUT: ; ModuleID = 'array_in_place.carbon' +// CHECK:STDOUT: source_filename = "array_in_place.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @F(ptr sret({ i32, i32, i32 })) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @G() { +// CHECK:STDOUT: %v = alloca [2 x { i32, i32, i32 }], align 8 +// CHECK:STDOUT: %array.index = getelementptr inbounds [2 x { i32, i32, i32 }], ptr %v, i32 0, i32 0 +// CHECK:STDOUT: call void @F(ptr %array.index) +// CHECK:STDOUT: %array.index1 = getelementptr inbounds [2 x { i32, i32, i32 }], ptr %v, i32 0, i32 1 +// CHECK:STDOUT: call void @F(ptr %array.index1) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/array/assign_return_value.carbon b/toolchain/lower/testdata/array/assign_return_value.carbon index f9e4781a519f..210bfa3a5188 100644 --- a/toolchain/lower/testdata/array/assign_return_value.carbon +++ b/toolchain/lower/testdata/array/assign_return_value.carbon @@ -25,13 +25,13 @@ fn Run() { // CHECK:STDOUT: %t = alloca [2 x i32], align 4 // CHECK:STDOUT: %temp = alloca { i32, i32 }, align 8 // CHECK:STDOUT: call void @F(ptr %temp) +// CHECK:STDOUT: %tuple.elem = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.elem, align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [2 x i32], ptr %t, i32 0, i32 0 -// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 0 -// CHECK:STDOUT: %1 = load i32, ptr %tuple.index, align 4 // CHECK:STDOUT: store i32 %1, ptr %array.index, align 4 -// CHECK:STDOUT: %array.index1 = getelementptr inbounds [2 x i32], ptr %t, i32 0, i32 1 -// CHECK:STDOUT: %tuple.index2 = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 1 -// CHECK:STDOUT: %2 = load i32, ptr %tuple.index2, align 4 -// CHECK:STDOUT: store i32 %2, ptr %array.index1, align 4 +// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 1 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.elem1, align 4 +// CHECK:STDOUT: %array.index2 = getelementptr inbounds [2 x i32], ptr %t, i32 0, i32 1 +// CHECK:STDOUT: store i32 %2, ptr %array.index2, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/array/base.carbon b/toolchain/lower/testdata/array/base.carbon index 4b15fbf1bdf8..048a867ad463 100644 --- a/toolchain/lower/testdata/array/base.carbon +++ b/toolchain/lower/testdata/array/base.carbon @@ -25,30 +25,25 @@ fn Run() { // CHECK:STDOUT: %array.index2 = getelementptr inbounds [2 x double], ptr %b, i32 0, i32 1 // CHECK:STDOUT: store double 2.200000e+00, ptr %array.index2, align 8 // CHECK:STDOUT: %c = alloca [5 x {}], align 8 -// CHECK:STDOUT: %array.index3 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 0 -// CHECK:STDOUT: %array.index4 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 1 -// CHECK:STDOUT: %array.index5 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 2 -// CHECK:STDOUT: %array.index6 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 3 -// CHECK:STDOUT: %array.index7 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 4 // CHECK:STDOUT: %d = alloca { i32, i32, i32 }, align 8 // CHECK:STDOUT: %tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %tuple.elem, align 4 -// CHECK:STDOUT: %tuple.elem8 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %tuple.elem8, align 4 -// CHECK:STDOUT: %tuple.elem9 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %tuple.elem9, align 4 +// CHECK:STDOUT: %tuple.elem3 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 +// CHECK:STDOUT: store i32 2, ptr %tuple.elem3, align 4 +// CHECK:STDOUT: %tuple.elem4 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 +// CHECK:STDOUT: store i32 3, ptr %tuple.elem4, align 4 // CHECK:STDOUT: %e = alloca [3 x i32], align 4 -// CHECK:STDOUT: %array.index10 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 0 -// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 0 -// CHECK:STDOUT: %1 = load i32, ptr %tuple.index, align 4 -// CHECK:STDOUT: store i32 %1, ptr %array.index10, align 4 -// CHECK:STDOUT: %array.index11 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 1 -// CHECK:STDOUT: %tuple.index12 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 -// CHECK:STDOUT: %2 = load i32, ptr %tuple.index12, align 4 -// CHECK:STDOUT: store i32 %2, ptr %array.index11, align 4 -// CHECK:STDOUT: %array.index13 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 2 -// CHECK:STDOUT: %tuple.index14 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 -// CHECK:STDOUT: %3 = load i32, ptr %tuple.index14, align 4 -// CHECK:STDOUT: store i32 %3, ptr %array.index13, align 4 +// CHECK:STDOUT: %tuple.elem5 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.elem5, align 4 +// CHECK:STDOUT: %array.index6 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 0 +// CHECK:STDOUT: store i32 %1, ptr %array.index6, align 4 +// CHECK:STDOUT: %tuple.elem7 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.elem7, align 4 +// CHECK:STDOUT: %array.index8 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 1 +// CHECK:STDOUT: store i32 %2, ptr %array.index8, align 4 +// CHECK:STDOUT: %tuple.elem9 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 +// CHECK:STDOUT: %3 = load i32, ptr %tuple.elem9, align 4 +// CHECK:STDOUT: %array.index10 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 2 +// CHECK:STDOUT: store i32 %3, ptr %array.index10, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/index/array_element_access.carbon b/toolchain/lower/testdata/index/array_element_access.carbon index f7db974a25e1..b74f82a8152e 100644 --- a/toolchain/lower/testdata/index/array_element_access.carbon +++ b/toolchain/lower/testdata/index/array_element_access.carbon @@ -37,30 +37,30 @@ fn Run() { // CHECK:STDOUT: %a = alloca [2 x i32], align 4 // CHECK:STDOUT: %temp = alloca { i32, i32 }, align 8 // CHECK:STDOUT: call void @A(ptr %temp) +// CHECK:STDOUT: %tuple.elem = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.elem, align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [2 x i32], ptr %a, i32 0, i32 0 -// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 0 -// CHECK:STDOUT: %1 = load i32, ptr %tuple.index, align 4 // CHECK:STDOUT: store i32 %1, ptr %array.index, align 4 -// CHECK:STDOUT: %array.index1 = getelementptr inbounds [2 x i32], ptr %a, i32 0, i32 1 -// CHECK:STDOUT: %tuple.index2 = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 1 -// CHECK:STDOUT: %2 = load i32, ptr %tuple.index2, align 4 -// CHECK:STDOUT: store i32 %2, ptr %array.index1, align 4 +// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds { i32, i32 }, ptr %temp, i32 0, i32 1 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.elem1, align 4 +// CHECK:STDOUT: %array.index2 = getelementptr inbounds [2 x i32], ptr %a, i32 0, i32 1 +// CHECK:STDOUT: store i32 %2, ptr %array.index2, align 4 // CHECK:STDOUT: %b = alloca i32, align 4 // CHECK:STDOUT: %temp3 = alloca { i32, i32 }, align 8 // CHECK:STDOUT: call void @A(ptr %temp3) -// CHECK:STDOUT: %tuple.index4 = getelementptr inbounds { i32, i32 }, ptr %temp3, i32 0, i32 0 -// CHECK:STDOUT: %3 = load i32, ptr %tuple.index4, align 4 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %temp3, i32 0, i32 0 +// CHECK:STDOUT: %3 = load i32, ptr %tuple.index, align 4 // CHECK:STDOUT: store i32 %3, ptr %b, align 4 // CHECK:STDOUT: %c = alloca i32, align 4 // CHECK:STDOUT: %4 = load i32, ptr %b, align 4 -// CHECK:STDOUT: %array.index5 = getelementptr inbounds [2 x i32], ptr %a, i32 0, i32 %4 -// CHECK:STDOUT: %5 = load i32, ptr %array.index5, align 4 +// CHECK:STDOUT: %array.index4 = getelementptr inbounds [2 x i32], ptr %a, i32 0, i32 %4 +// CHECK:STDOUT: %5 = load i32, ptr %array.index4, align 4 // CHECK:STDOUT: store i32 %5, ptr %c, align 4 // CHECK:STDOUT: %d = alloca i32, align 4 -// CHECK:STDOUT: %temp6 = alloca [2 x i32], align 4 -// CHECK:STDOUT: call void @B(ptr %temp6) -// CHECK:STDOUT: %array.index7 = getelementptr inbounds [2 x i32], ptr %temp6, i32 0, i32 1 -// CHECK:STDOUT: %6 = load i32, ptr %array.index7, align 4 +// CHECK:STDOUT: %temp5 = alloca [2 x i32], align 4 +// CHECK:STDOUT: call void @B(ptr %temp5) +// CHECK:STDOUT: %array.index6 = getelementptr inbounds [2 x i32], ptr %temp5, i32 0, i32 1 +// CHECK:STDOUT: %6 = load i32, ptr %array.index6, align 4 // CHECK:STDOUT: store i32 %6, ptr %d, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/nested_struct_in_place.carbon b/toolchain/lower/testdata/struct/nested_struct_in_place.carbon new file mode 100644 index 000000000000..98e19fb2dd31 --- /dev/null +++ b/toolchain/lower/testdata/struct/nested_struct_in_place.carbon @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: {.a: (i32, i32, i32), .b: (i32, i32, i32)} = {.a = F(), .b = F()}; +} + +// CHECK:STDOUT: ; ModuleID = 'nested_struct_in_place.carbon' +// CHECK:STDOUT: source_filename = "nested_struct_in_place.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @F(ptr sret({ i32, i32, i32 })) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @G() { +// CHECK:STDOUT: %v = alloca { { i32, i32, i32 }, { i32, i32, i32 } }, align 8 +// CHECK:STDOUT: %a = getelementptr inbounds { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v, i32 0, i32 0 +// CHECK:STDOUT: call void @F(ptr %a) +// CHECK:STDOUT: %b = getelementptr inbounds { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v, i32 0, i32 1 +// CHECK:STDOUT: call void @F(ptr %b) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/one_entry.carbon b/toolchain/lower/testdata/struct/one_entry.carbon index 963c797805c2..f815ed492bd9 100644 --- a/toolchain/lower/testdata/struct/one_entry.carbon +++ b/toolchain/lower/testdata/struct/one_entry.carbon @@ -15,7 +15,6 @@ fn Run() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { // CHECK:STDOUT: %x = alloca { i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0 // CHECK:STDOUT: store { i32 } { i32 4 }, ptr %x, align 4 // CHECK:STDOUT: %y = alloca { i32 }, align 8 // CHECK:STDOUT: %1 = load { i32 }, ptr %x, align 4 diff --git a/toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon b/toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon new file mode 100644 index 000000000000..495f05ad803b --- /dev/null +++ b/toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() -> (i32, i32, i32); + +fn G() { + var v: ((i32, i32, i32), (i32, i32, i32)) = (F(), F()); +} + +// CHECK:STDOUT: ; ModuleID = 'nested_tuple_in_place.carbon' +// CHECK:STDOUT: source_filename = "nested_tuple_in_place.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @F(ptr sret({ i32, i32, i32 })) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @G() { +// CHECK:STDOUT: %v = alloca { { i32, i32, i32 }, { i32, i32, i32 } }, align 8 +// CHECK:STDOUT: %tuple.elem = getelementptr inbounds { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v, i32 0, i32 0 +// CHECK:STDOUT: call void @F(ptr %tuple.elem) +// CHECK:STDOUT: %tuple.elem1 = getelementptr inbounds { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v, i32 0, i32 1 +// CHECK:STDOUT: call void @F(ptr %tuple.elem1) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/tuple/one_entry.carbon b/toolchain/lower/testdata/tuple/one_entry.carbon index 69083ec66889..30ccec8b0105 100644 --- a/toolchain/lower/testdata/tuple/one_entry.carbon +++ b/toolchain/lower/testdata/tuple/one_entry.carbon @@ -15,7 +15,6 @@ fn Run() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { // CHECK:STDOUT: %x = alloca { i32 }, align 8 -// CHECK:STDOUT: %tuple.elem = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0 // CHECK:STDOUT: store { i32 } { i32 1 }, ptr %x, align 4 // CHECK:STDOUT: %y = alloca { i32 }, align 8 // CHECK:STDOUT: %1 = load { i32 }, ptr %x, align 4 diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 4e4b82d115b3..f230c2ab45e2 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -216,13 +216,13 @@ static auto GetTypePrecedence(NodeKind kind) -> int { case NodeKind::RealLiteral: case NodeKind::Return: case NodeKind::ReturnExpression: + case NodeKind::SpliceBlock: case NodeKind::StringLiteral: case NodeKind::StructAccess: case NodeKind::StructTypeField: case NodeKind::StructLiteral: case NodeKind::StructInit: case NodeKind::StructValue: - case NodeKind::StubReference: case NodeKind::Temporary: case NodeKind::TemporaryStorage: case NodeKind::TupleAccess: @@ -384,12 +384,12 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const case NodeKind::RealLiteral: case NodeKind::Return: case NodeKind::ReturnExpression: + case NodeKind::SpliceBlock: case NodeKind::StringLiteral: case NodeKind::StructAccess: case NodeKind::StructLiteral: case NodeKind::StructInit: case NodeKind::StructValue: - case NodeKind::StubReference: case NodeKind::Temporary: case NodeKind::TemporaryStorage: case NodeKind::TupleAccess: @@ -495,8 +495,9 @@ auto GetExpressionCategory(const File& file, NodeId node_id) continue; } - case NodeKind::StubReference: { - node_id = node.GetAsStubReference(); + case NodeKind::SpliceBlock: { + auto [block_id, result_id] = node.GetAsSpliceBlock(); + node_id = result_id; continue; } @@ -578,8 +579,9 @@ auto GetValueRepresentation(const File& file, TypeId type_id) continue; } - case NodeKind::StubReference: { - node_id = node.GetAsStubReference(); + case NodeKind::SpliceBlock: { + auto [block_id, result_id] = node.GetAsSpliceBlock(); + node_id = result_id; continue; } diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index cf6b3064a6c3..dd330a9a7112 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -351,13 +351,18 @@ class NodeNamer { AddBlockLabel(scope_idx, dest_id, node); break; } + case NodeKind::SpliceBlock: { + auto [block_id, result_id] = node.GetAsSpliceBlock(); + CollectNamesInBlock(scope_idx, block_id); + break; + } case NodeKind::Parameter: { auto name_id = node.GetAsParameter(); nodes[node_id.index] = { scope_idx, scope.nodes.AllocateName(*this, node.parse_node(), semantics_ir_.GetString(name_id).str())}; - break; + continue; } case NodeKind::VarStorage: { // TODO: Eventually this name will be optional, and we'll want to @@ -368,17 +373,18 @@ class NodeNamer { scope_idx, scope.nodes.AllocateName(*this, node.parse_node(), semantics_ir_.GetString(name_id).str())}; - break; + continue; } default: { - // Sequentially number all remaining values. - if (node.kind().value_kind() != NodeValueKind::None) { - nodes[node_id.index] = { - scope_idx, scope.nodes.AllocateName(*this, node.parse_node())}; - } break; } } + + // Sequentially number all remaining values. + if (node.kind().value_kind() != NodeValueKind::None) { + nodes[node_id.index] = { + scope_idx, scope.nodes.AllocateName(*this, node.parse_node())}; + } } } @@ -483,7 +489,8 @@ class Formatter { auto FormatInstruction(NodeId node_id) -> void { if (!node_id.is_valid()) { - out_ << " " << NodeKind::Invalid.ir_name() << "\n"; + Indent(); + out_ << NodeKind::Invalid.ir_name() << "\n"; return; } @@ -502,9 +509,11 @@ class Formatter { } } + auto Indent() -> void { out_.indent(indent_); } + template auto FormatInstruction(NodeId node_id, Node node) -> void { - out_ << " "; + Indent(); FormatInstructionLHS(node_id, node); out_ << node.kind().ir_name(); FormatInstructionRHS(node); @@ -556,8 +565,8 @@ class Formatter { template <> auto FormatInstruction(NodeId /*node_id*/, Node node) -> void { - if (!in_terminator_sequence) { - out_ << " "; + if (!in_terminator_sequence_) { + Indent(); } auto [label_id, cond_id] = node.GetAsBranchIf(); out_ << "if "; @@ -565,14 +574,14 @@ class Formatter { out_ << " " << NodeKind::Branch.ir_name() << " "; FormatLabel(label_id); out_ << " else "; - in_terminator_sequence = true; + in_terminator_sequence_ = true; } template <> auto FormatInstruction(NodeId /*node_id*/, Node node) -> void { - if (!in_terminator_sequence) { - out_ << " "; + if (!in_terminator_sequence_) { + Indent(); } auto [label_id, arg_id] = node.GetAsBranchWithArg(); out_ << NodeKind::BranchWithArg.ir_name() << " "; @@ -580,18 +589,18 @@ class Formatter { out_ << "("; FormatNodeName(arg_id); out_ << ")\n"; - in_terminator_sequence = false; + in_terminator_sequence_ = false; } template <> auto FormatInstruction(NodeId /*node_id*/, Node node) -> void { - if (!in_terminator_sequence) { - out_ << " "; + if (!in_terminator_sequence_) { + Indent(); } out_ << NodeKind::Branch.ir_name() << " "; FormatLabel(node.GetAsBranch()); out_ << "\n"; - in_terminator_sequence = false; + in_terminator_sequence_ = false; } template <> @@ -658,6 +667,21 @@ class Formatter { out_ << " " << xref_id << "." << node_id; } + template <> + auto FormatInstructionRHS(Node node) -> void { + auto [block_id, result_id] = node.GetAsSpliceBlock(); + FormatArgs(result_id); + out_ << " {"; + if (!semantics_ir_.GetNodeBlock(block_id).empty()) { + out_ << "\n"; + indent_ += 2; + FormatCodeBlock(block_id); + indent_ -= 2; + Indent(); + } + out_ << "}"; + } + // StructTypeFields are formatted as part of their StructType. template <> auto FormatInstruction(NodeId /*node_id*/, @@ -800,7 +824,8 @@ class Formatter { llvm::raw_ostream& out_; NodeNamer node_namer_; NodeNamer::ScopeIndex scope_ = NodeNamer::ScopeIndex::None; - bool in_terminator_sequence = false; + bool in_terminator_sequence_ = false; + int indent_ = 2; }; auto FormatFile(const Lex::TokenizedBuffer& tokenized_buffer, diff --git a/toolchain/sem_ir/node.h b/toolchain/sem_ir/node.h index 92e7eb9f936d..48fd2984aa7f 100644 --- a/toolchain/sem_ir/node.h +++ b/toolchain/sem_ir/node.h @@ -401,6 +401,9 @@ class Node : public Printable { using ReturnExpression = FactoryNoType; + using SpliceBlock = Factory; + using StringLiteral = Factory; @@ -422,8 +425,6 @@ class Node : public Printable { using StructValue = Factory; - using StubReference = Factory; - using Temporary = Factory; diff --git a/toolchain/sem_ir/node_kind.def b/toolchain/sem_ir/node_kind.def index 0b78d62aaecc..4dda72a6c06d 100644 --- a/toolchain/sem_ir/node_kind.def +++ b/toolchain/sem_ir/node_kind.def @@ -77,6 +77,8 @@ CARBON_SEMANTICS_NODE_KIND_IMPL(RealLiteral, "real_literal", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(ReturnExpression, "return", None, Terminator) CARBON_SEMANTICS_NODE_KIND_IMPL(Return, "return", None, Terminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(SpliceBlock, "splice_block", Typed, + NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StringLiteral, "string_literal", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StructAccess, "struct_access", Typed, @@ -89,8 +91,6 @@ CARBON_SEMANTICS_NODE_KIND_IMPL(StructTypeField, "struct_type_field", None, CARBON_SEMANTICS_NODE_KIND_IMPL(StructType, "struct_type", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StructValue, "struct_value", Typed, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(StubReference, "stub_reference", Typed, - NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(TemporaryStorage, "temporary_storage", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(Temporary, "temporary", Typed, NotTerminator)