From 491fa1bdd539a736389a5583f0901af09a920b82 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 25 Sep 2023 18:31:24 -0700 Subject: [PATCH] Place the computation of the destination of an initialization into the return slot. (#3252) Fix a bug where we would perform the computation of the return location in SemIR after we have already used it in some cases, leading to assertion failures during lowering. Instead, accumulate a sequence of instructions to compute the return location in a temporary block, and overwrite the return slot with those instructions when we perform initialization. StubReference is replaced by a more general SpliceBlock node, that takes a code block and a result value, executes the instructions in the block, and produces the result. This is used in the uncommon case where more than one instruction is required to compute the return slot, which can happen if we need to first emit a temporary and then index into it, or if we need to perform multiple levels of indexing before we reach an entity to initialize. --- toolchain/check/context.cpp | 151 ++++++++++++++---- toolchain/check/context.h | 26 +-- toolchain/check/node_block_stack.cpp | 6 + toolchain/check/node_block_stack.h | 3 + .../testdata/array/array_in_place.carbon | 42 +++++ .../testdata/array/assign_return_value.carbon | 16 +- .../check/testdata/array/assign_var.carbon | 36 ++--- toolchain/check/testdata/array/base.carbon | 56 +++---- .../testdata/array/fail_type_mismatch.carbon | 18 +-- .../check/testdata/array/nine_elements.carbon | 60 +++---- .../testdata/basics/numeric_literals.carbon | 84 +++++----- .../in_place_tuple_initialization.carbon | 18 +-- .../index/array_element_access.carbon | 18 +-- .../index/fail_array_large_index.carbon | 12 +- .../index/fail_array_non_int_indexing.carbon | 12 +- .../fail_array_out_of_bound_access.carbon | 12 +- .../index/fail_tuple_large_index.carbon | 5 +- .../index/tuple_element_access.carbon | 5 +- .../struct/fail_member_access_type.carbon | 5 +- .../struct/fail_non_member_access.carbon | 5 +- .../struct/nested_struct_in_place.carbon | 34 ++++ .../check/testdata/struct/one_entry.carbon | 5 +- .../testdata/struct/tuple_as_element.carbon | 11 +- .../tuples/nested_tuple_in_place.carbon | 62 +++++++ .../check/testdata/tuples/one_element.carbon | 5 +- toolchain/lower/file_context.cpp | 16 +- toolchain/lower/function_context.cpp | 23 ++- toolchain/lower/function_context.h | 9 +- toolchain/lower/handle.cpp | 13 +- .../lower/handle_expression_category.cpp | 1 + .../testdata/array/array_in_place.carbon | 25 +++ .../testdata/array/assign_return_value.carbon | 12 +- toolchain/lower/testdata/array/base.carbon | 37 ++--- .../index/array_element_access.carbon | 28 ++-- .../struct/nested_struct_in_place.carbon | 25 +++ .../lower/testdata/struct/one_entry.carbon | 1 - .../tuple/nested_tuple_in_place.carbon | 25 +++ .../lower/testdata/tuple/one_entry.carbon | 1 - toolchain/sem_ir/file.cpp | 14 +- toolchain/sem_ir/formatter.cpp | 63 +++++--- toolchain/sem_ir/node.h | 5 +- toolchain/sem_ir/node_kind.def | 4 +- 42 files changed, 670 insertions(+), 339 deletions(-) create mode 100644 toolchain/check/testdata/array/array_in_place.carbon create mode 100644 toolchain/check/testdata/struct/nested_struct_in_place.carbon create mode 100644 toolchain/check/testdata/tuples/nested_tuple_in_place.carbon create mode 100644 toolchain/lower/testdata/array/array_in_place.carbon create mode 100644 toolchain/lower/testdata/struct/nested_struct_in_place.carbon create mode 100644 toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon 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)