Prechádzať zdrojové kódy

Ensure that all allocas are created in the entry block. (#4685)

Non-entry-block allocas will allocate new stack memory each time they're
reached, resulting in leaking stack memory over time for allocas in a
loop. Move all such allocas to the entry block instead, and use an LLVM
intrinsic to mark when the lifetime of the variable actually begins.
Richard Smith 1 rok pred
rodič
commit
c04d62a7d1
44 zmenil súbory, kde vykonal 796 pridanie a 375 odobranie
  1. 20 0
      toolchain/lower/function_context.h
  2. 35 3
      toolchain/lower/handle.cpp
  3. 6 0
      toolchain/lower/testdata/alias/local.carbon
  4. 6 0
      toolchain/lower/testdata/array/array_in_place.carbon
  5. 5 0
      toolchain/lower/testdata/array/assign_return_value.carbon
  6. 51 44
      toolchain/lower/testdata/array/base.carbon
  7. 21 15
      toolchain/lower/testdata/basics/numeric_literals.carbon
  8. 19 10
      toolchain/lower/testdata/builtins/no_prelude/types.carbon
  9. 5 0
      toolchain/lower/testdata/class/adapt.carbon
  10. 15 6
      toolchain/lower/testdata/class/basic.carbon
  11. 7 2
      toolchain/lower/testdata/class/convert.carbon
  12. 6 0
      toolchain/lower/testdata/class/field.carbon
  13. 44 26
      toolchain/lower/testdata/class/virtual.carbon
  14. 6 0
      toolchain/lower/testdata/function/call/empty_struct.carbon
  15. 6 0
      toolchain/lower/testdata/function/call/empty_tuple.carbon
  16. 6 0
      toolchain/lower/testdata/function/call/i32.carbon
  17. 6 0
      toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon
  18. 6 0
      toolchain/lower/testdata/function/call/return_implicit.carbon
  19. 6 0
      toolchain/lower/testdata/function/call/var_param.carbon
  20. 28 22
      toolchain/lower/testdata/function/generic/call.carbon
  21. 20 12
      toolchain/lower/testdata/function/generic/call_method.carbon
  22. 31 24
      toolchain/lower/testdata/index/array_element_access.carbon
  23. 53 46
      toolchain/lower/testdata/let/tuple.carbon
  24. 19 11
      toolchain/lower/testdata/operators/assignment.carbon
  25. 7 2
      toolchain/lower/testdata/pointer/address_of_field.carbon
  26. 6 0
      toolchain/lower/testdata/pointer/address_of_unused.carbon
  27. 6 0
      toolchain/lower/testdata/pointer/basic.carbon
  28. 27 18
      toolchain/lower/testdata/pointer/pointer_to_pointer.carbon
  29. 6 0
      toolchain/lower/testdata/return/return_var_byval.carbon
  30. 6 0
      toolchain/lower/testdata/return/var.carbon
  31. 13 4
      toolchain/lower/testdata/struct/empty.carbon
  32. 26 17
      toolchain/lower/testdata/struct/member_access.carbon
  33. 6 0
      toolchain/lower/testdata/struct/nested_struct_in_place.carbon
  34. 19 10
      toolchain/lower/testdata/struct/one_entry.carbon
  35. 24 15
      toolchain/lower/testdata/struct/two_entries.carbon
  36. 27 18
      toolchain/lower/testdata/tuple/access/element_access.carbon
  37. 5 0
      toolchain/lower/testdata/tuple/access/return_value_access.carbon
  38. 13 4
      toolchain/lower/testdata/tuple/empty.carbon
  39. 6 0
      toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon
  40. 18 11
      toolchain/lower/testdata/tuple/one_entry.carbon
  41. 24 15
      toolchain/lower/testdata/tuple/two_entries.carbon
  42. 49 40
      toolchain/lower/testdata/tuple/value_formation.carbon
  43. 6 0
      toolchain/lower/testdata/var/local.carbon
  44. 75 0
      toolchain/lower/testdata/var/nested.carbon

+ 20 - 0
toolchain/lower/function_context.h

@@ -89,6 +89,19 @@ class FunctionContext {
     return file_context_->GetTypeAsValue();
   }
 
+  // Returns the instruction immediately after all the existing static allocas.
+  // This is the insert point for future static allocas.
+  auto GetInstructionAfterAllocas() const -> llvm::Instruction* {
+    return after_allocas_;
+  }
+
+  // Sets the instruction after static allocas. This should be called once,
+  // after the first alloca is created.
+  auto SetInstructionAfterAllocas(llvm::Instruction* after_allocas) {
+    CARBON_CHECK(!after_allocas_);
+    after_allocas_ = after_allocas;
+  }
+
   // Create a synthetic block that corresponds to no SemIR::InstBlockId. Such
   // a block should only ever have a single predecessor, and is used when we
   // need multiple `llvm::BasicBlock`s to model the linear control flow in a
@@ -111,6 +124,7 @@ class FunctionContext {
     return file_context_->llvm_context();
   }
   auto llvm_module() -> llvm::Module& { return file_context_->llvm_module(); }
+  auto llvm_function() -> llvm::Function& { return *function_; }
   auto builder() -> llvm::IRBuilderBase& { return builder_; }
   auto sem_ir() -> const SemIR::File& { return file_context_->sem_ir(); }
 
@@ -155,8 +169,14 @@ class FunctionContext {
   // The IR function we're generating.
   llvm::Function* function_;
 
+  // Builder for creating code in this function. The insertion point is held at
+  // the location of the current SemIR instruction.
   llvm::IRBuilder<llvm::ConstantFolder, Inserter> builder_;
 
+  // The instruction after all allocas. This is used as the insert point for new
+  // allocas.
+  llvm::Instruction* after_allocas_ = nullptr;
+
   llvm::DISubprogram* di_subprogram_;
 
   // The optional vlog stream.

+ 35 - 3
toolchain/lower/handle.cpp

@@ -264,9 +264,41 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
                 SemIR::VarStorage inst) -> void {
-  context.SetLocal(inst_id,
-                   context.builder().CreateAlloca(context.GetType(inst.type_id),
-                                                  /*ArraySize=*/nullptr));
+  auto* type = context.GetType(inst.type_id);
+
+  // Position the first alloca right before the start of the executable code in
+  // the function.
+  auto saved_ip = context.builder().saveIP();
+  if (auto* after_allocas = context.GetInstructionAfterAllocas()) {
+    context.builder().SetInsertPoint(after_allocas);
+  } else {
+    context.builder().SetInsertPointPastAllocas(&context.llvm_function());
+  }
+
+  // Create an alloca for this variable in the entry block.
+  auto* alloca = context.builder().CreateAlloca(type);
+  context.builder().restoreIP(saved_ip);
+
+  // Create a lifetime start intrinsic here to indicate where its scope really
+  // begins.
+  auto size = context.llvm_module().getDataLayout().getTypeAllocSize(type);
+  context.builder().CreateLifetimeStart(
+      alloca,
+      llvm::ConstantInt::get(context.llvm_context(), llvm::APInt(64, size)));
+
+  // If we just created the first alloca, there is now definitely at least one
+  // instruction after it -- there is a lifetime start instruction if nothing
+  // else. Use that instruction as our insert point for all future allocas.
+  if (!context.GetInstructionAfterAllocas()) {
+    auto loc = alloca->getIterator();
+    ++loc;
+    context.SetInstructionAfterAllocas(&*loc);
+  }
+
+  // TODO: Create a matching `@llvm.lifetime.end` intrinsic call when the
+  // variable goes out of scope.
+
+  context.SetLocal(inst_id, alloca);
 }
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,

+ 6 - 0
toolchain/lower/testdata/alias/local.carbon

@@ -20,11 +20,17 @@ fn F() -> i32 {
 // CHECK:STDOUT: define i32 @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   store i32 0, ptr %a.var, align 4, !dbg !8
 // CHECK:STDOUT:   %.loc14 = load i32, ptr %a.var, align 4, !dbg !9
 // CHECK:STDOUT:   ret i32 %.loc14, !dbg !10
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/array/array_in_place.carbon

@@ -22,6 +22,7 @@ fn G() {
 // CHECK:STDOUT: define void @_CG.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %v.var = alloca [2 x { i32, i32, i32 }], align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 24, ptr %v.var), !dbg !7
 // CHECK:STDOUT:   %.loc14_42.1.array.index = getelementptr inbounds [2 x { i32, i32, i32 }], ptr %v.var, i32 0, i64 0, !dbg !8
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc14_42.1.array.index), !dbg !9
 // CHECK:STDOUT:   %.loc14_42.3.array.index = getelementptr inbounds [2 x { i32, i32, i32 }], ptr %v.var, i32 0, i64 1, !dbg !8
@@ -29,6 +30,11 @@ fn G() {
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 5 - 0
toolchain/lower/testdata/array/assign_return_value.carbon

@@ -30,6 +30,7 @@ fn Run() {
 // CHECK:STDOUT: define void @main() !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %t.var = alloca [2 x i32], align 4, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %t.var), !dbg !10
 // CHECK:STDOUT:   %.loc14_23.1.temp = alloca { i32, i32 }, align 8, !dbg !11
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc14_23.1.temp), !dbg !11
 // CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc14_23.1.temp, i32 0, i32 0, !dbg !11
@@ -46,7 +47,11 @@ fn Run() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}

+ 51 - 44
toolchain/lower/testdata/array/base.carbon

@@ -27,47 +27,58 @@ fn Run() {
 // CHECK:STDOUT: define void @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca [1 x i32], align 4, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca [2 x double], align 8, !dbg !7
+// CHECK:STDOUT:   %c.var = alloca [5 x {}], align 8, !dbg !7
+// CHECK:STDOUT:   %d.var = alloca { i32, i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %e.var = alloca [3 x i32], align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   %.loc12_24.3.array.index = getelementptr inbounds [1 x i32], ptr %a.var, i32 0, i64 0, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @array.1.loc12_25, i64 4, i1 false), !dbg !9
-// CHECK:STDOUT:   %b.var = alloca [2 x double], align 8, !dbg !10
-// CHECK:STDOUT:   %.loc13_32.2.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i64 0, !dbg !11
-// CHECK:STDOUT:   %.loc13_32.4.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i64 1, !dbg !11
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %b.var, ptr align 8 @array.2.loc13_33, i64 16, i1 false), !dbg !12
-// CHECK:STDOUT:   %c.var = alloca [5 x {}], align 8, !dbg !13
-// CHECK:STDOUT:   %.loc14_40.2.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 0, !dbg !14
-// CHECK:STDOUT:   %.loc14_40.4.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 1, !dbg !14
-// CHECK:STDOUT:   %.loc14_40.6.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 2, !dbg !14
-// CHECK:STDOUT:   %.loc14_40.8.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 3, !dbg !14
-// CHECK:STDOUT:   %.loc14_40.10.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 4, !dbg !14
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @array.3.loc14_41, i64 0, i1 false), !dbg !15
-// CHECK:STDOUT:   %d.var = alloca { i32, i32, i32 }, align 8, !dbg !16
-// CHECK:STDOUT:   %tuple.elem0.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 0, !dbg !17
-// CHECK:STDOUT:   %tuple.elem1.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 1, !dbg !17
-// CHECK:STDOUT:   %tuple.elem2.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 2, !dbg !17
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %d.var, ptr align 4 @tuple.loc15_37, i64 12, i1 false), !dbg !18
-// CHECK:STDOUT:   %e.var = alloca [3 x i32], align 4, !dbg !19
-// CHECK:STDOUT:   %tuple.elem0.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 0, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.1 = load i32, ptr %tuple.elem0.loc16.tuple.elem, align 4, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.2.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 0, !dbg !20
-// CHECK:STDOUT:   store i32 %.loc16_21.1, ptr %.loc16_21.2.array.index, align 4, !dbg !20
-// CHECK:STDOUT:   %tuple.elem1.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 1, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.4 = load i32, ptr %tuple.elem1.loc16.tuple.elem, align 4, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.5.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 1, !dbg !20
-// CHECK:STDOUT:   store i32 %.loc16_21.4, ptr %.loc16_21.5.array.index, align 4, !dbg !20
-// CHECK:STDOUT:   %tuple.elem2.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 2, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.7 = load i32, ptr %tuple.elem2.loc16.tuple.elem, align 4, !dbg !20
-// CHECK:STDOUT:   %.loc16_21.8.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 2, !dbg !20
-// CHECK:STDOUT:   store i32 %.loc16_21.7, ptr %.loc16_21.8.array.index, align 4, !dbg !20
-// CHECK:STDOUT:   ret void, !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %.loc13_32.2.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i64 0, !dbg !10
+// CHECK:STDOUT:   %.loc13_32.4.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i64 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %b.var, ptr align 8 @array.2.loc13_33, i64 16, i1 false), !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %c.var), !dbg !7
+// CHECK:STDOUT:   %.loc14_40.2.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 0, !dbg !12
+// CHECK:STDOUT:   %.loc14_40.4.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 1, !dbg !12
+// CHECK:STDOUT:   %.loc14_40.6.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 2, !dbg !12
+// CHECK:STDOUT:   %.loc14_40.8.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 3, !dbg !12
+// CHECK:STDOUT:   %.loc14_40.10.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i64 4, !dbg !12
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @array.3.loc14_41, i64 0, i1 false), !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %d.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 0, !dbg !14
+// CHECK:STDOUT:   %tuple.elem1.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 1, !dbg !14
+// CHECK:STDOUT:   %tuple.elem2.loc15.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 2, !dbg !14
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %d.var, ptr align 4 @tuple.loc15_37, i64 12, i1 false), !dbg !15
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %e.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 0, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.1 = load i32, ptr %tuple.elem0.loc16.tuple.elem, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.2.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 0, !dbg !16
+// CHECK:STDOUT:   store i32 %.loc16_21.1, ptr %.loc16_21.2.array.index, align 4, !dbg !16
+// CHECK:STDOUT:   %tuple.elem1.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 1, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.4 = load i32, ptr %tuple.elem1.loc16.tuple.elem, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.5.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 1, !dbg !16
+// CHECK:STDOUT:   store i32 %.loc16_21.4, ptr %.loc16_21.5.array.index, align 4, !dbg !16
+// CHECK:STDOUT:   %tuple.elem2.loc16.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %d.var, i32 0, i32 2, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.7 = load i32, ptr %tuple.elem2.loc16.tuple.elem, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc16_21.8.array.index = getelementptr inbounds [3 x i32], ptr %e.var, i32 0, i64 2, !dbg !16
+// CHECK:STDOUT:   store i32 %.loc16_21.7, ptr %.loc16_21.8.array.index, align 4, !dbg !16
+// CHECK:STDOUT:   ret void, !dbg !17
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 2, 1, 3, 4, 5, 6, 7 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 4, 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 3, 2, 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -82,15 +93,11 @@ fn Run() {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 21, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 21, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 20, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 3, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 7, scope: !4)
-// CHECK:STDOUT: !17 = !DILocation(line: 15, column: 28, scope: !4)
-// CHECK:STDOUT: !18 = !DILocation(line: 15, column: 3, scope: !4)
-// CHECK:STDOUT: !19 = !DILocation(line: 16, column: 7, scope: !4)
-// CHECK:STDOUT: !20 = !DILocation(line: 16, column: 21, scope: !4)
-// CHECK:STDOUT: !21 = !DILocation(line: 11, column: 1, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 21, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 20, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 28, scope: !4)
+// CHECK:STDOUT: !15 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !16 = !DILocation(line: 16, column: 21, scope: !4)
+// CHECK:STDOUT: !17 = !DILocation(line: 11, column: 1, scope: !4)

+ 21 - 15
toolchain/lower/testdata/basics/numeric_literals.carbon

@@ -36,29 +36,36 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %ints.var = alloca [4 x i32], align 4, !dbg !7
+// CHECK:STDOUT:   %floats.var = alloca [6 x double], align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %ints.var), !dbg !7
 // CHECK:STDOUT:   %.loc19_3.3.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i64 0, !dbg !8
 // CHECK:STDOUT:   %.loc19_3.6.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i64 1, !dbg !8
 // CHECK:STDOUT:   %.loc19_3.9.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i64 2, !dbg !8
 // CHECK:STDOUT:   %.loc19_3.12.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i64 3, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %ints.var, ptr align 4 @array.1.loc19_4, i64 16, i1 false), !dbg !9
-// CHECK:STDOUT:   %floats.var = alloca [6 x double], align 8, !dbg !10
-// CHECK:STDOUT:   %.loc27_3.2.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 0, !dbg !11
-// CHECK:STDOUT:   %.loc27_3.4.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 1, !dbg !11
-// CHECK:STDOUT:   %.loc27_3.6.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 2, !dbg !11
-// CHECK:STDOUT:   %.loc27_3.8.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 3, !dbg !11
-// CHECK:STDOUT:   %.loc27_3.10.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 4, !dbg !11
-// CHECK:STDOUT:   %.loc27_3.12.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 5, !dbg !11
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %floats.var, ptr align 8 @array.2.loc27_4, i64 48, i1 false), !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 48, ptr %floats.var), !dbg !7
+// CHECK:STDOUT:   %.loc27_3.2.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 0, !dbg !10
+// CHECK:STDOUT:   %.loc27_3.4.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 1, !dbg !10
+// CHECK:STDOUT:   %.loc27_3.6.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 2, !dbg !10
+// CHECK:STDOUT:   %.loc27_3.8.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 3, !dbg !10
+// CHECK:STDOUT:   %.loc27_3.10.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 4, !dbg !10
+// CHECK:STDOUT:   %.loc27_3.12.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i64 5, !dbg !10
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %floats.var, ptr align 8 @array.2.loc27_4, i64 48, i1 false), !dbg !11
+// CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -73,7 +80,6 @@ fn F() {
 // CHECK:STDOUT: !7 = !DILocation(line: 14, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 14, column: 24, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 14, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 26, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 20, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 1, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 26, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 11, column: 1, scope: !4)

+ 19 - 10
toolchain/lower/testdata/builtins/no_prelude/types.carbon

@@ -26,14 +26,25 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %i.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %f.var = alloca double, align 8, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca i1, align 1, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %i.var), !dbg !7
 // CHECK:STDOUT:   store i32 0, ptr %i.var, align 4, !dbg !8
-// CHECK:STDOUT:   %f.var = alloca double, align 8, !dbg !9
-// CHECK:STDOUT:   store double 0.000000e+00, ptr %f.var, align 8, !dbg !10
-// CHECK:STDOUT:   %b.var = alloca i1, align 1, !dbg !11
-// CHECK:STDOUT:   store i1 false, ptr %b.var, align 1, !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %f.var), !dbg !7
+// CHECK:STDOUT:   store double 0.000000e+00, ptr %f.var, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   store i1 false, ptr %b.var, align 1, !dbg !10
+// CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -46,8 +57,6 @@ fn F() {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 18, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 18, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 19, column: 3, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 7, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 20, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 17, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 17, column: 1, scope: !4)

+ 5 - 0
toolchain/lower/testdata/class/adapt.carbon

@@ -80,6 +80,7 @@ fn DoStuff(a: Int) -> Int {
 // CHECK:STDOUT: define i32 @_CUse.Main() !dbg !15 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %pa.var = alloca { i32, i32 }, align 8, !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %pa.var), !dbg !16
 // CHECK:STDOUT:   call void @_CMake.PairAdapter.Main(ptr %pa.var), !dbg !17
 // CHECK:STDOUT:   %GetB.call = call i32 @_CGetB.PairAdapter.Main(ptr %pa.var), !dbg !18
 // CHECK:STDOUT:   ret i32 %GetB.call, !dbg !19
@@ -88,7 +89,11 @@ fn DoStuff(a: Int) -> Int {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}

+ 15 - 6
toolchain/lower/testdata/class/basic.carbon

@@ -28,11 +28,21 @@ fn Run() {
 // CHECK:STDOUT: define void @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %c.var = alloca { i32, ptr }, align 8, !dbg !7
-// CHECK:STDOUT:   %d.var = alloca { i32, ptr }, align 8, !dbg !8
-// CHECK:STDOUT:   call void @_CF.Main(ptr %d.var, ptr %c.var), !dbg !9
-// CHECK:STDOUT:   ret void, !dbg !10
+// CHECK:STDOUT:   %d.var = alloca { i32, ptr }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %c.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %d.var), !dbg !7
+// CHECK:STDOUT:   call void @_CF.Main(ptr %d.var, ptr %c.var), !dbg !8
+// CHECK:STDOUT:   ret void, !dbg !9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -44,6 +54,5 @@ fn Run() {
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 19, column: 7, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 20, column: 7, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 20, column: 14, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 18, column: 1, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 20, column: 14, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 18, column: 1, scope: !4)

+ 7 - 2
toolchain/lower/testdata/class/convert.carbon

@@ -43,6 +43,7 @@ fn DoIt() {
 // CHECK:STDOUT: define void @_CDoIt.Main() !dbg !11 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %w.var = alloca { i32 }, align 8, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %w.var), !dbg !12
 // CHECK:STDOUT:   %.loc22_31.3.n = getelementptr inbounds nuw { i32 }, ptr %w.var, i32 0, i32 0, !dbg !13
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %w.var, ptr align 4 @IntWrapper.val.loc22_32, i64 4, i1 false), !dbg !14
 // CHECK:STDOUT:   %Convert.call = call i32 @"_CConvert.IntWrapper.Main:ImplicitAs.Core"(ptr %w.var), !dbg !15
@@ -50,10 +51,14 @@ fn DoIt() {
 // CHECK:STDOUT:   ret void, !dbg !17
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}

+ 6 - 0
toolchain/lower/testdata/class/field.carbon

@@ -39,6 +39,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !10 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %c.var = alloca { i32, ptr }, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %c.var), !dbg !11
 // CHECK:STDOUT:   %.loc22_4.a = getelementptr inbounds nuw { i32, ptr }, ptr %c.var, i32 0, i32 0, !dbg !12
 // CHECK:STDOUT:   store i32 1, ptr %.loc22_4.a, align 4, !dbg !12
 // CHECK:STDOUT:   %.loc23.b = getelementptr inbounds nuw { i32, ptr }, ptr %c.var, i32 0, i32 1, !dbg !13
@@ -47,6 +48,11 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   ret i32 %F.call, !dbg !15
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 44 - 26
toolchain/lower/testdata/class/virtual.carbon

@@ -79,19 +79,30 @@ fn Fn() {
 // CHECK:STDOUT: define void @_CCreate.Create() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %b.var = alloca {}, align 8, !dbg !7
-// CHECK:STDOUT:   %i.var = alloca { ptr, {} }, align 8, !dbg !8
-// CHECK:STDOUT:   %d.var = alloca { { ptr, {} } }, align 8, !dbg !9
-// CHECK:STDOUT:   ret void, !dbg !10
+// CHECK:STDOUT:   %i.var = alloca { ptr, {} }, align 8, !dbg !7
+// CHECK:STDOUT:   %d.var = alloca { { ptr, {} } }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %i.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %d.var), !dbg !7
+// CHECK:STDOUT:   ret void, !dbg !8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !11 {
+// CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !9 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   call void @_CFn.Intermediate.Classes(), !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   call void @_CFn.Intermediate.Classes(), !dbg !10
+// CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @_CFn.Intermediate.Classes()
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -103,12 +114,10 @@ fn Fn() {
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 7, column: 7, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 7, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 1, scope: !4)
-// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Create", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2)
-// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !11)
-// CHECK:STDOUT: !13 = !DILocation(line: 12, column: 1, scope: !11)
+// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Create", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 3, scope: !9)
+// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 1, scope: !9)
 // CHECK:STDOUT: ; ModuleID = 'member_init.carbon'
 // CHECK:STDOUT: source_filename = "member_init.carbon"
 // CHECK:STDOUT:
@@ -117,18 +126,28 @@ fn Fn() {
 // CHECK:STDOUT: define void @_CFn.MemberInit() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %i.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %v.var = alloca { ptr, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %i.var), !dbg !7
 // CHECK:STDOUT:   store i32 3, ptr %i.var, align 4, !dbg !8
-// CHECK:STDOUT:   %v.var = alloca { ptr, i32 }, align 8, !dbg !9
-// CHECK:STDOUT:   %.loc12_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   store ptr null, ptr %.loc12_24.2.vptr, align 8, !dbg !10
-// CHECK:STDOUT:   %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !11
-// CHECK:STDOUT:   %.loc12_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc12_23, ptr %.loc12_24.5.m, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !12
-// CHECK:STDOUT:   store i32 5, ptr %.loc15_4.m, align 4, !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %v.var), !dbg !7
+// CHECK:STDOUT:   %.loc12_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !9
+// CHECK:STDOUT:   store ptr null, ptr %.loc12_24.2.vptr, align 8, !dbg !9
+// CHECK:STDOUT:   %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !10
+// CHECK:STDOUT:   %.loc12_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !9
+// CHECK:STDOUT:   store i32 %.loc12_23, ptr %.loc12_24.5.m, align 4, !dbg !9
+// CHECK:STDOUT:   %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !11
+// CHECK:STDOUT:   store i32 5, ptr %.loc15_4.m, align 4, !dbg !11
+// CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -141,8 +160,7 @@ fn Fn() {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 10, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 10, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 12, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 17, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 23, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 9, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 12, column: 17, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 23, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 9, column: 1, scope: !4)

+ 6 - 0
toolchain/lower/testdata/function/call/empty_struct.carbon

@@ -27,10 +27,16 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !8 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %b.var = alloca {}, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %b.var), !dbg !9
 // CHECK:STDOUT:   call void @_CEcho.Main(), !dbg !10
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/function/call/empty_tuple.carbon

@@ -27,10 +27,16 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !8 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %b.var = alloca {}, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %b.var), !dbg !9
 // CHECK:STDOUT:   call void @_CEcho.Main(), !dbg !10
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/function/call/i32.carbon

@@ -27,11 +27,17 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !8 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !9
 // CHECK:STDOUT:   %Echo.call = call i32 @_CEcho.Main(i32 1), !dbg !10
 // CHECK:STDOUT:   store i32 %Echo.call, ptr %b.var, align 4, !dbg !11
 // CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon

@@ -33,12 +33,18 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !10 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca {}, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %x.var), !dbg !11
 // CHECK:STDOUT:   call void @_CFoo.Main(), !dbg !12
 // CHECK:STDOUT:   %.loc17_23.1.temp = alloca {}, align 8, !dbg !12
 // CHECK:STDOUT:   call void @_CBar.Main(), !dbg !13
 // CHECK:STDOUT:   ret void, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/function/call/return_implicit.carbon

@@ -27,10 +27,16 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !8 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %b.var = alloca {}, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %b.var), !dbg !9
 // CHECK:STDOUT:   call void @_CMakeImplicitEmptyTuple.Main(), !dbg !10
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/function/call/var_param.carbon

@@ -26,12 +26,18 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !8 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca i32, align 4, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %a.var), !dbg !9
 // CHECK:STDOUT:   store i32 0, ptr %a.var, align 4, !dbg !10
 // CHECK:STDOUT:   %.loc15 = load i32, ptr %a.var, align 4, !dbg !11
 // CHECK:STDOUT:   call void @_CDoNothing.Main(i32 %.loc15), !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 28 - 22
toolchain/lower/testdata/function/generic/call.carbon

@@ -36,21 +36,27 @@ fn G() {
 // CHECK:STDOUT: define void @_CG.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %c.var = alloca {}, align 8, !dbg !7
+// CHECK:STDOUT:   %d.var = alloca {}, align 8, !dbg !7
+// CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %c.var), !dbg !7
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.loc18_16, i64 0, i1 false), !dbg !8
-// CHECK:STDOUT:   %d.var = alloca {}, align 8, !dbg !9
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %d.var, ptr align 1 @D.val.loc19_16, i64 0, i1 false), !dbg !10
-// CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !11
-// CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !12
-// CHECK:STDOUT:   call void @_CF.Main.40(ptr %c.var), !dbg !13
-// CHECK:STDOUT:   call void @_CF.Main.41(ptr %d.var), !dbg !14
-// CHECK:STDOUT:   %.loc24 = load i32, ptr %n.var, align 4, !dbg !15
-// CHECK:STDOUT:   call void @_CF.Main.42(i32 %.loc24), !dbg !16
-// CHECK:STDOUT:   call void @_CF.Main.43(%type zeroinitializer), !dbg !17
-// CHECK:STDOUT:   ret void, !dbg !18
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %d.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %d.var, ptr align 1 @D.val.loc19_16, i64 0, i1 false), !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %n.var), !dbg !7
+// CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !10
+// CHECK:STDOUT:   call void @_CF.Main.40(ptr %c.var), !dbg !11
+// CHECK:STDOUT:   call void @_CF.Main.41(ptr %d.var), !dbg !12
+// CHECK:STDOUT:   %.loc24 = load i32, ptr %n.var, align 4, !dbg !13
+// CHECK:STDOUT:   call void @_CF.Main.42(i32 %.loc24), !dbg !14
+// CHECK:STDOUT:   call void @_CF.Main.43(%type zeroinitializer), !dbg !15
+// CHECK:STDOUT:   ret void, !dbg !16
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @_CF.Main.40(ptr)
 // CHECK:STDOUT:
@@ -61,9 +67,11 @@ fn G() {
 // CHECK:STDOUT: declare void @_CF.Main.43(%type)
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -77,13 +85,11 @@ fn G() {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 18, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 18, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 19, column: 3, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 7, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 20, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 22, column: 3, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 23, column: 3, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 24, column: 5, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 24, column: 3, scope: !4)
-// CHECK:STDOUT: !17 = !DILocation(line: 25, column: 3, scope: !4)
-// CHECK:STDOUT: !18 = !DILocation(line: 17, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 22, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 23, column: 3, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 24, column: 5, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 24, column: 3, scope: !4)
+// CHECK:STDOUT: !15 = !DILocation(line: 25, column: 3, scope: !4)
+// CHECK:STDOUT: !16 = !DILocation(line: 17, column: 1, scope: !4)

+ 20 - 12
toolchain/lower/testdata/function/generic/call_method.carbon

@@ -28,20 +28,29 @@ fn CallF() -> i32 {
 // CHECK:STDOUT: define i32 @_CCallF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %c.var = alloca {}, align 8, !dbg !7
+// CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %c.var), !dbg !7
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.loc18_16, i64 0, i1 false), !dbg !8
-// CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !9
-// CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc20_14 = load i32, ptr %n.var, align 4, !dbg !11
-// CHECK:STDOUT:   %F.call = call i32 @_CF.C.Main.40(ptr %c.var, i32 %.loc20_14), !dbg !12
-// CHECK:STDOUT:   ret i32 %F.call, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %n.var), !dbg !7
+// CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !9
+// CHECK:STDOUT:   %.loc20_14 = load i32, ptr %n.var, align 4, !dbg !10
+// CHECK:STDOUT:   %F.call = call i32 @_CF.C.Main.40(ptr %c.var, i32 %.loc20_14), !dbg !11
+// CHECK:STDOUT:   ret i32 %F.call, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare i32 @_CF.C.Main.40(ptr, i32)
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -55,8 +64,7 @@ fn CallF() -> i32 {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 18, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 18, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 19, column: 3, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 14, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 20, column: 10, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 20, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 19, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 20, column: 14, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 20, column: 10, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 20, column: 3, scope: !4)

+ 31 - 24
toolchain/lower/testdata/index/array_element_access.carbon

@@ -43,6 +43,10 @@ fn Run() {
 // CHECK:STDOUT: define void @main() !dbg !12 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca [2 x i32], align 4, !dbg !13
+// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !13
+// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !13
+// CHECK:STDOUT:   %d.var = alloca i32, align 4, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %a.var), !dbg !13
 // CHECK:STDOUT:   %.loc15_23.1.temp = alloca { i32, i32 }, align 8, !dbg !14
 // CHECK:STDOUT:   call void @_CA.Main(ptr %.loc15_23.1.temp), !dbg !14
 // CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc15_23.1.temp, i32 0, i32 0, !dbg !14
@@ -53,29 +57,35 @@ fn Run() {
 // CHECK:STDOUT:   %.loc15_23.6 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !14
 // CHECK:STDOUT:   %.loc15_23.7.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i64 1, !dbg !14
 // CHECK:STDOUT:   store i32 %.loc15_23.6, ptr %.loc15_23.7.array.index, align 4, !dbg !14
-// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !15
-// CHECK:STDOUT:   store i32 1, ptr %b.var, align 4, !dbg !16
-// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !17
-// CHECK:STDOUT:   %.loc17_18 = load i32, ptr %b.var, align 4, !dbg !18
-// CHECK:STDOUT:   %.loc17_19.1.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i32 %.loc17_18, !dbg !19
-// CHECK:STDOUT:   %.loc17_19.2 = load i32, ptr %.loc17_19.1.array.index, align 4, !dbg !19
-// CHECK:STDOUT:   store i32 %.loc17_19.2, ptr %c.var, align 4, !dbg !20
-// CHECK:STDOUT:   %d.var = alloca i32, align 4, !dbg !21
-// CHECK:STDOUT:   %.loc18_18.1.temp = alloca [2 x i32], align 4, !dbg !22
-// CHECK:STDOUT:   call void @_CB.Main(ptr %.loc18_18.1.temp), !dbg !22
-// CHECK:STDOUT:   %.loc18_21.1.array.index = getelementptr inbounds [2 x i32], ptr %.loc18_18.1.temp, i32 0, i32 1, !dbg !22
-// CHECK:STDOUT:   %.loc18_21.2 = load i32, ptr %.loc18_21.1.array.index, align 4, !dbg !22
-// CHECK:STDOUT:   store i32 %.loc18_21.2, ptr %d.var, align 4, !dbg !23
-// CHECK:STDOUT:   ret void, !dbg !24
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !13
+// CHECK:STDOUT:   store i32 1, ptr %b.var, align 4, !dbg !15
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %c.var), !dbg !13
+// CHECK:STDOUT:   %.loc17_18 = load i32, ptr %b.var, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc17_19.1.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i32 %.loc17_18, !dbg !17
+// CHECK:STDOUT:   %.loc17_19.2 = load i32, ptr %.loc17_19.1.array.index, align 4, !dbg !17
+// CHECK:STDOUT:   store i32 %.loc17_19.2, ptr %c.var, align 4, !dbg !18
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %d.var), !dbg !13
+// CHECK:STDOUT:   %.loc18_18.1.temp = alloca [2 x i32], align 4, !dbg !19
+// CHECK:STDOUT:   call void @_CB.Main(ptr %.loc18_18.1.temp), !dbg !19
+// CHECK:STDOUT:   %.loc18_21.1.array.index = getelementptr inbounds [2 x i32], ptr %.loc18_18.1.temp, i32 0, i32 1, !dbg !19
+// CHECK:STDOUT:   %.loc18_21.2 = load i32, ptr %.loc18_21.1.array.index, align 4, !dbg !19
+// CHECK:STDOUT:   store i32 %.loc18_21.2, ptr %d.var, align 4, !dbg !20
+// CHECK:STDOUT:   ret void, !dbg !21
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 4, 6, 7, 2, 3, 5, 8, 9, 10 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -95,13 +105,10 @@ fn Run() {
 // CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Run", linkageName: "main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !13 = !DILocation(line: 15, column: 7, scope: !12)
 // CHECK:STDOUT: !14 = !DILocation(line: 15, column: 21, scope: !12)
-// CHECK:STDOUT: !15 = !DILocation(line: 16, column: 7, scope: !12)
-// CHECK:STDOUT: !16 = !DILocation(line: 16, column: 3, scope: !12)
-// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 7, scope: !12)
-// CHECK:STDOUT: !18 = !DILocation(line: 17, column: 18, scope: !12)
-// CHECK:STDOUT: !19 = !DILocation(line: 17, column: 16, scope: !12)
-// CHECK:STDOUT: !20 = !DILocation(line: 17, column: 3, scope: !12)
-// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 7, scope: !12)
-// CHECK:STDOUT: !22 = !DILocation(line: 18, column: 16, scope: !12)
-// CHECK:STDOUT: !23 = !DILocation(line: 18, column: 3, scope: !12)
-// CHECK:STDOUT: !24 = !DILocation(line: 14, column: 1, scope: !12)
+// CHECK:STDOUT: !15 = !DILocation(line: 16, column: 3, scope: !12)
+// CHECK:STDOUT: !16 = !DILocation(line: 17, column: 18, scope: !12)
+// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 16, scope: !12)
+// CHECK:STDOUT: !18 = !DILocation(line: 17, column: 3, scope: !12)
+// CHECK:STDOUT: !19 = !DILocation(line: 18, column: 16, scope: !12)
+// CHECK:STDOUT: !20 = !DILocation(line: 18, column: 3, scope: !12)
+// CHECK:STDOUT: !21 = !DILocation(line: 14, column: 1, scope: !12)

+ 53 - 46
toolchain/lower/testdata/let/tuple.carbon

@@ -24,55 +24,63 @@ fn F() -> i32 {
 // CHECK:STDOUT: define i32 @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca { i32, i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   %tuple.elem0.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %tuple.elem1.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   %tuple.elem2.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple.1.loc12_37, i64 12, i1 false), !dbg !9
-// CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8, !dbg !10
-// CHECK:STDOUT:   %tuple.elem0.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %tuple.elem1.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.2.loc13_29, i64 8, i1 false), !dbg !12
-// CHECK:STDOUT:   %tuple.elem0.loc14_43.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !13
-// CHECK:STDOUT:   %.loc14_43.1 = load i32, ptr %tuple.elem0.loc14_43.tuple.elem, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.elem1.loc14_43.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !13
-// CHECK:STDOUT:   %.loc14_43.2 = load i32, ptr %tuple.elem1.loc14_43.tuple.elem, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.elem2.loc14.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !13
-// CHECK:STDOUT:   %.loc14_43.3 = load i32, ptr %tuple.elem2.loc14.tuple.elem, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.loc14_43 = alloca { i32, i32, i32 }, align 8, !dbg !13
-// CHECK:STDOUT:   %tuple.loc14_431 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 0, !dbg !13
-// CHECK:STDOUT:   store i32 %.loc14_43.1, ptr %tuple.loc14_431, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.loc14_432 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 1, !dbg !13
-// CHECK:STDOUT:   store i32 %.loc14_43.2, ptr %tuple.loc14_432, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.loc14_433 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 2, !dbg !13
-// CHECK:STDOUT:   store i32 %.loc14_43.3, ptr %tuple.loc14_433, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.elem0.loc14_46.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !14
-// CHECK:STDOUT:   %.loc14_46.1 = load i32, ptr %tuple.elem0.loc14_46.tuple.elem, align 4, !dbg !14
-// CHECK:STDOUT:   %tuple.elem1.loc14_46.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !14
-// CHECK:STDOUT:   %.loc14_46.2 = load i32, ptr %tuple.elem1.loc14_46.tuple.elem, align 4, !dbg !14
-// CHECK:STDOUT:   %tuple.loc14_46 = alloca { i32, i32 }, align 8, !dbg !14
-// CHECK:STDOUT:   %tuple.loc14_464 = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.loc14_46, i32 0, i32 0, !dbg !14
-// CHECK:STDOUT:   store i32 %.loc14_46.1, ptr %tuple.loc14_464, align 4, !dbg !14
-// CHECK:STDOUT:   %tuple.loc14_465 = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.loc14_46, i32 0, i32 1, !dbg !14
-// CHECK:STDOUT:   store i32 %.loc14_46.2, ptr %tuple.loc14_465, align 4, !dbg !14
-// CHECK:STDOUT:   %tuple.loc14_47 = alloca { ptr, ptr }, align 8, !dbg !15
-// CHECK:STDOUT:   %tuple.loc14_476 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 0, !dbg !15
-// CHECK:STDOUT:   store ptr %tuple.loc14_43, ptr %tuple.loc14_476, align 8, !dbg !15
-// CHECK:STDOUT:   %tuple.loc14_477 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !15
-// CHECK:STDOUT:   store ptr %tuple.loc14_46, ptr %tuple.loc14_477, align 8, !dbg !15
-// CHECK:STDOUT:   %tuple.elem1.loc15_11.tuple.elem = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !16
-// CHECK:STDOUT:   %tuple.elem1.loc15_11.tuple.elem.load = load ptr, ptr %tuple.elem1.loc15_11.tuple.elem, align 8, !dbg !16
-// CHECK:STDOUT:   %tuple.elem1.loc15_13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.elem1.loc15_11.tuple.elem.load, i32 0, i32 1, !dbg !16
-// CHECK:STDOUT:   %tuple.elem1.loc15_13.tuple.elem.load = load i32, ptr %tuple.elem1.loc15_13.tuple.elem, align 4, !dbg !16
-// CHECK:STDOUT:   ret i32 %tuple.elem1.loc15_13.tuple.elem.load, !dbg !17
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   %tuple.elem1.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.2.loc13_29, i64 8, i1 false), !dbg !11
+// CHECK:STDOUT:   %tuple.elem0.loc14_43.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !12
+// CHECK:STDOUT:   %.loc14_43.1 = load i32, ptr %tuple.elem0.loc14_43.tuple.elem, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.elem1.loc14_43.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !12
+// CHECK:STDOUT:   %.loc14_43.2 = load i32, ptr %tuple.elem1.loc14_43.tuple.elem, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.elem2.loc14.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !12
+// CHECK:STDOUT:   %.loc14_43.3 = load i32, ptr %tuple.elem2.loc14.tuple.elem, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.loc14_43 = alloca { i32, i32, i32 }, align 8, !dbg !12
+// CHECK:STDOUT:   %tuple.loc14_431 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 0, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc14_43.1, ptr %tuple.loc14_431, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.loc14_432 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 1, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc14_43.2, ptr %tuple.loc14_432, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.loc14_433 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc14_43, i32 0, i32 2, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc14_43.3, ptr %tuple.loc14_433, align 4, !dbg !12
+// CHECK:STDOUT:   %tuple.elem0.loc14_46.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !13
+// CHECK:STDOUT:   %.loc14_46.1 = load i32, ptr %tuple.elem0.loc14_46.tuple.elem, align 4, !dbg !13
+// CHECK:STDOUT:   %tuple.elem1.loc14_46.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !13
+// CHECK:STDOUT:   %.loc14_46.2 = load i32, ptr %tuple.elem1.loc14_46.tuple.elem, align 4, !dbg !13
+// CHECK:STDOUT:   %tuple.loc14_46 = alloca { i32, i32 }, align 8, !dbg !13
+// CHECK:STDOUT:   %tuple.loc14_464 = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.loc14_46, i32 0, i32 0, !dbg !13
+// CHECK:STDOUT:   store i32 %.loc14_46.1, ptr %tuple.loc14_464, align 4, !dbg !13
+// CHECK:STDOUT:   %tuple.loc14_465 = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.loc14_46, i32 0, i32 1, !dbg !13
+// CHECK:STDOUT:   store i32 %.loc14_46.2, ptr %tuple.loc14_465, align 4, !dbg !13
+// CHECK:STDOUT:   %tuple.loc14_47 = alloca { ptr, ptr }, align 8, !dbg !14
+// CHECK:STDOUT:   %tuple.loc14_476 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 0, !dbg !14
+// CHECK:STDOUT:   store ptr %tuple.loc14_43, ptr %tuple.loc14_476, align 8, !dbg !14
+// CHECK:STDOUT:   %tuple.loc14_477 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !14
+// CHECK:STDOUT:   store ptr %tuple.loc14_46, ptr %tuple.loc14_477, align 8, !dbg !14
+// CHECK:STDOUT:   %tuple.elem1.loc15_11.tuple.elem = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !15
+// CHECK:STDOUT:   %tuple.elem1.loc15_11.tuple.elem.load = load ptr, ptr %tuple.elem1.loc15_11.tuple.elem, align 8, !dbg !15
+// CHECK:STDOUT:   %tuple.elem1.loc15_13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %tuple.elem1.loc15_11.tuple.elem.load, i32 0, i32 1, !dbg !15
+// CHECK:STDOUT:   %tuple.elem1.loc15_13.tuple.elem.load = load i32, ptr %tuple.elem1.loc15_13.tuple.elem, align 4, !dbg !15
+// CHECK:STDOUT:   ret i32 %tuple.elem1.loc15_13.tuple.elem.load, !dbg !16
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11, 13, 14 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -87,11 +95,10 @@ fn F() -> i32 {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 28, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 23, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 43, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 46, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 42, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 10, scope: !4)
-// CHECK:STDOUT: !17 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 23, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 43, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 46, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 42, scope: !4)
+// CHECK:STDOUT: !15 = !DILocation(line: 15, column: 10, scope: !4)
+// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 3, scope: !4)

+ 19 - 11
toolchain/lower/testdata/operators/assignment.carbon

@@ -23,19 +23,28 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   store i32 12, ptr %a.var, align 4, !dbg !8
 // CHECK:STDOUT:   store i32 9, ptr %a.var, align 4, !dbg !9
-// CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8, !dbg !10
-// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.loc15_5, i64 8, i1 false), !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.loc15_5, i64 8, i1 false), !dbg !11
+// CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -50,7 +59,6 @@ fn Main() {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 3, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 15, column: 7, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 1, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 15, column: 7, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 11, column: 1, scope: !4)

+ 7 - 2
toolchain/lower/testdata/pointer/address_of_field.carbon

@@ -25,6 +25,7 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %s.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %s.var), !dbg !7
 // CHECK:STDOUT:   %.loc14_46.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %s.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %.loc14_46.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %s.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %s.var, ptr align 4 @struct.loc14_47, i64 8, i1 false), !dbg !9
@@ -33,10 +34,14 @@ fn F() {
 // CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}

+ 6 - 0
toolchain/lower/testdata/pointer/address_of_unused.carbon

@@ -19,10 +19,16 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %n.var), !dbg !7
 // CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !8
 // CHECK:STDOUT:   ret void, !dbg !9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/pointer/basic.carbon

@@ -29,11 +29,17 @@ fn F() -> i32 {
 // CHECK:STDOUT: define i32 @_CF.Main() !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %n.var = alloca i32, align 4, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %n.var), !dbg !10
 // CHECK:STDOUT:   store i32 0, ptr %n.var, align 4, !dbg !11
 // CHECK:STDOUT:   %G.call = call i32 @_CG.Main(ptr %n.var), !dbg !12
 // CHECK:STDOUT:   ret i32 %G.call, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 27 - 18
toolchain/lower/testdata/pointer/pointer_to_pointer.carbon

@@ -21,18 +21,29 @@ fn F(p: i32**) -> i32 {
 // CHECK:STDOUT: define i32 @_CF.Main(ptr %p) !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca ptr, align 8, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca ptr, align 8, !dbg !7
+// CHECK:STDOUT:   %c.var = alloca ptr, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   store ptr %p, ptr %a.var, align 8, !dbg !8
-// CHECK:STDOUT:   %b.var = alloca ptr, align 8, !dbg !9
-// CHECK:STDOUT:   %.loc13_17.2 = load ptr, ptr %p, align 8, !dbg !10
-// CHECK:STDOUT:   store ptr %.loc13_17.2, ptr %b.var, align 8, !dbg !11
-// CHECK:STDOUT:   %c.var = alloca ptr, align 8, !dbg !12
-// CHECK:STDOUT:   store ptr %b.var, ptr %c.var, align 8, !dbg !13
-// CHECK:STDOUT:   %.loc15_12 = load ptr, ptr %c.var, align 8, !dbg !14
-// CHECK:STDOUT:   %.loc15_11.2 = load ptr, ptr %.loc15_12, align 8, !dbg !15
-// CHECK:STDOUT:   %.loc15_10.2 = load i32, ptr %.loc15_11.2, align 4, !dbg !16
-// CHECK:STDOUT:   ret i32 %.loc15_10.2, !dbg !17
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %.loc13_17.2 = load ptr, ptr %p, align 8, !dbg !9
+// CHECK:STDOUT:   store ptr %.loc13_17.2, ptr %b.var, align 8, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %c.var), !dbg !7
+// CHECK:STDOUT:   store ptr %b.var, ptr %c.var, align 8, !dbg !11
+// CHECK:STDOUT:   %.loc15_12 = load ptr, ptr %c.var, align 8, !dbg !12
+// CHECK:STDOUT:   %.loc15_11.2 = load ptr, ptr %.loc15_12, align 8, !dbg !13
+// CHECK:STDOUT:   %.loc15_10.2 = load i32, ptr %.loc15_11.2, align 4, !dbg !14
+// CHECK:STDOUT:   ret i32 %.loc15_10.2, !dbg !15
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -45,12 +56,10 @@ fn F(p: i32**) -> i32 {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 17, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 3, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 12, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 15, column: 11, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 10, scope: !4)
-// CHECK:STDOUT: !17 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 17, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 12, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 15, column: 11, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 10, scope: !4)
+// CHECK:STDOUT: !15 = !DILocation(line: 15, column: 3, scope: !4)

+ 6 - 0
toolchain/lower/testdata/return/return_var_byval.carbon

@@ -19,11 +19,17 @@ fn Main() -> i32 {
 // CHECK:STDOUT: define i32 @_CMain.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   store i32 0, ptr %x.var, align 4, !dbg !8
 // CHECK:STDOUT:   %.loc12_16 = load i32, ptr %x.var, align 4, !dbg !7
 // CHECK:STDOUT:   ret i32 %.loc12_16, !dbg !9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 6 - 0
toolchain/lower/testdata/return/var.carbon

@@ -19,11 +19,17 @@ fn Main() -> i32 {
 // CHECK:STDOUT: define i32 @_CMain.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   store i32 0, ptr %x.var, align 4, !dbg !8
 // CHECK:STDOUT:   %.loc13 = load i32, ptr %x.var, align 4, !dbg !9
 // CHECK:STDOUT:   ret i32 %.loc13, !dbg !10
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 13 - 4
toolchain/lower/testdata/struct/empty.carbon

@@ -20,10 +20,20 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca {}, align 8, !dbg !7
-// CHECK:STDOUT:   %y.var = alloca {}, align 8, !dbg !8
-// CHECK:STDOUT:   ret i32 0, !dbg !9
+// CHECK:STDOUT:   %y.var = alloca {}, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %x.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   ret i32 0, !dbg !8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -35,5 +45,4 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 3, scope: !4)

+ 26 - 17
toolchain/lower/testdata/struct/member_access.carbon

@@ -23,23 +23,34 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { double, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %y.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %z.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   %.loc12_48.2.a = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %.loc12_48.5.b = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x.var, ptr align 8 @struct.loc12_49, i64 16, i1 false), !dbg !9
-// CHECK:STDOUT:   %y.var = alloca i32, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc13_17.1.b = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   %.loc13_17.2 = load i32, ptr %.loc13_17.1.b, align 4, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13_17.2, ptr %y.var, align 4, !dbg !12
-// CHECK:STDOUT:   %z.var = alloca i32, align 4, !dbg !13
-// CHECK:STDOUT:   %.loc14 = load i32, ptr %y.var, align 4, !dbg !14
-// CHECK:STDOUT:   store i32 %.loc14, ptr %z.var, align 4, !dbg !15
-// CHECK:STDOUT:   ret i32 0, !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   %.loc13_17.1.b = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   %.loc13_17.2 = load i32, ptr %.loc13_17.1.b, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13_17.2, ptr %y.var, align 4, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %z.var), !dbg !7
+// CHECK:STDOUT:   %.loc14 = load i32, ptr %y.var, align 4, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc14, ptr %z.var, align 4, !dbg !13
+// CHECK:STDOUT:   ret i32 0, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 1, 3, 0, 2, 4, 5 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -54,10 +65,8 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 31, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 16, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 16, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 3, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 16, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 16, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 3, scope: !4)

+ 6 - 0
toolchain/lower/testdata/struct/nested_struct_in_place.carbon

@@ -22,6 +22,7 @@ fn G() {
 // CHECK:STDOUT: define void @_CG.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %v.var = alloca { { i32, i32, i32 }, { i32, i32, i32 } }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 24, ptr %v.var), !dbg !7
 // CHECK:STDOUT:   %.loc14_74.1.a = getelementptr inbounds nuw { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc14_74.1.a), !dbg !9
 // CHECK:STDOUT:   %.loc14_74.2.b = getelementptr inbounds nuw { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v.var, i32 0, i32 1, !dbg !8
@@ -29,6 +30,11 @@ fn G() {
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 19 - 10
toolchain/lower/testdata/struct/one_entry.carbon

@@ -20,15 +20,25 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %y.var = alloca { i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   store { i32 } { i32 4 }, ptr %x.var, align 4, !dbg !8
-// CHECK:STDOUT:   %y.var = alloca { i32 }, align 8, !dbg !9
-// CHECK:STDOUT:   %.loc13_22.1.a = getelementptr inbounds nuw { i32 }, ptr %x.var, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   %.loc13_22.2 = load i32, ptr %.loc13_22.1.a, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc13_22.3.struct.init = insertvalue { i32 } poison, i32 %.loc13_22.2, 0, !dbg !10
-// CHECK:STDOUT:   store { i32 } %.loc13_22.3.struct.init, ptr %y.var, align 4, !dbg !11
-// CHECK:STDOUT:   ret i32 0, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   %.loc13_22.1.a = getelementptr inbounds nuw { i32 }, ptr %x.var, i32 0, i32 0, !dbg !9
+// CHECK:STDOUT:   %.loc13_22.2 = load i32, ptr %.loc13_22.1.a, align 4, !dbg !9
+// CHECK:STDOUT:   %.loc13_22.3.struct.init = insertvalue { i32 } poison, i32 %.loc13_22.2, 0, !dbg !9
+// CHECK:STDOUT:   store { i32 } %.loc13_22.3.struct.init, ptr %y.var, align 4, !dbg !10
+// CHECK:STDOUT:   ret i32 0, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -41,7 +51,6 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 22, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 22, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4)

+ 24 - 15
toolchain/lower/testdata/struct/two_entries.carbon

@@ -22,25 +22,35 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   %.loc12_46.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %.loc12_46.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @struct.loc12_47, i64 8, i1 false), !dbg !9
-// CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8, !dbg !10
-// CHECK:STDOUT:   %.loc13_31.1.a = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %.loc13_31.2 = load i32, ptr %.loc13_31.1.a, align 4, !dbg !11
-// CHECK:STDOUT:   %.loc13_31.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13_31.2, ptr %.loc13_31.3.a, align 4, !dbg !11
-// CHECK:STDOUT:   %.loc13_31.5.b = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   %.loc13_31.6 = load i32, ptr %.loc13_31.5.b, align 4, !dbg !11
-// CHECK:STDOUT:   %.loc13_31.7.b = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13_31.6, ptr %.loc13_31.7.b, align 4, !dbg !11
-// CHECK:STDOUT:   ret i32 0, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   %.loc13_31.1.a = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   %.loc13_31.2 = load i32, ptr %.loc13_31.1.a, align 4, !dbg !10
+// CHECK:STDOUT:   %.loc13_31.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13_31.2, ptr %.loc13_31.3.a, align 4, !dbg !10
+// CHECK:STDOUT:   %.loc13_31.5.b = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   %.loc13_31.6 = load i32, ptr %.loc13_31.5.b, align 4, !dbg !10
+// CHECK:STDOUT:   %.loc13_31.7.b = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13_31.6, ptr %.loc13_31.7.b, align 4, !dbg !10
+// CHECK:STDOUT:   ret i32 0, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 2, 4, 5 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -55,6 +65,5 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 31, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 31, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 31, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4)

+ 27 - 18
toolchain/lower/testdata/tuple/access/element_access.carbon

@@ -23,25 +23,36 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca { i32, i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %a.var), !dbg !7
 // CHECK:STDOUT:   %tuple.elem0.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   %tuple.elem2.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple.loc12_37, i64 12, i1 false), !dbg !9
-// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.elem0.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %.loc13 = load i32, ptr %tuple.elem0.loc13.tuple.elem, align 4, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13, ptr %b.var, align 4, !dbg !12
-// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !13
-// CHECK:STDOUT:   %tuple.elem2.loc14.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !14
-// CHECK:STDOUT:   %.loc14 = load i32, ptr %tuple.elem2.loc14.tuple.elem, align 4, !dbg !14
-// CHECK:STDOUT:   store i32 %.loc14, ptr %c.var, align 4, !dbg !15
-// CHECK:STDOUT:   ret i32 0, !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc13.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   %.loc13 = load i32, ptr %tuple.elem0.loc13.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13, ptr %b.var, align 4, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %c.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem2.loc14.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !12
+// CHECK:STDOUT:   %.loc14 = load i32, ptr %tuple.elem2.loc14.tuple.elem, align 4, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc14, ptr %c.var, align 4, !dbg !13
+// CHECK:STDOUT:   ret i32 0, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1, 3, 4 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -56,10 +67,8 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 28, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 16, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 16, scope: !4)
-// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 3, scope: !4)
-// CHECK:STDOUT: !16 = !DILocation(line: 15, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 16, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 16, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 15, column: 3, scope: !4)

+ 5 - 0
toolchain/lower/testdata/tuple/access/return_value_access.carbon

@@ -30,6 +30,7 @@ fn Run() {
 // CHECK:STDOUT: define void @main() !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %t.var = alloca i32, align 4, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %t.var), !dbg !10
 // CHECK:STDOUT:   %.loc14_18.1.temp = alloca { i32, i32 }, align 8, !dbg !11
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc14_18.1.temp), !dbg !11
 // CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc14_18.1.temp, i32 0, i32 1, !dbg !11
@@ -41,7 +42,11 @@ fn Run() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}

+ 13 - 4
toolchain/lower/testdata/tuple/empty.carbon

@@ -20,10 +20,20 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca {}, align 8, !dbg !7
-// CHECK:STDOUT:   %y.var = alloca {}, align 8, !dbg !8
-// CHECK:STDOUT:   ret i32 0, !dbg !9
+// CHECK:STDOUT:   %y.var = alloca {}, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %x.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   ret i32 0, !dbg !8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -35,5 +45,4 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 3, scope: !4)

+ 6 - 0
toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon

@@ -22,6 +22,7 @@ fn G() {
 // CHECK:STDOUT: define void @_CG.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %v.var = alloca { { i32, i32, i32 }, { i32, i32, i32 } }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 24, ptr %v.var), !dbg !7
 // CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   call void @_CF.Main(ptr %tuple.elem0.tuple.elem), !dbg !9
 // CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { { i32, i32, i32 }, { i32, i32, i32 } }, ptr %v.var, i32 0, i32 1, !dbg !8
@@ -29,6 +30,11 @@ fn G() {
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 18 - 11
toolchain/lower/testdata/tuple/one_entry.carbon

@@ -20,17 +20,25 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %y.var = alloca { i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   store { i32 } { i32 1 }, ptr %x.var, align 4, !dbg !8
-// CHECK:STDOUT:   %y.var = alloca { i32 }, align 8, !dbg !9
-// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32 }, ptr %x.var, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   %.loc13_20.1 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc13_20.2.tuple.init = insertvalue { i32 } poison, i32 %.loc13_20.1, 0, !dbg !10
-// CHECK:STDOUT:   store { i32 } %.loc13_20.2.tuple.init, ptr %y.var, align 4, !dbg !11
-// CHECK:STDOUT:   ret i32 0, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32 }, ptr %x.var, i32 0, i32 0, !dbg !9
+// CHECK:STDOUT:   %.loc13_20.1 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !9
+// CHECK:STDOUT:   %.loc13_20.2.tuple.init = insertvalue { i32 } poison, i32 %.loc13_20.1, 0, !dbg !9
+// CHECK:STDOUT:   store { i32 } %.loc13_20.2.tuple.init, ptr %y.var, align 4, !dbg !10
+// CHECK:STDOUT:   ret i32 0, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 2, 1 }
+// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -44,7 +52,6 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 20, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 3, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 20, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4)

+ 24 - 15
toolchain/lower/testdata/tuple/two_entries.carbon

@@ -22,25 +22,35 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   %tuple.elem0.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !8
 // CHECK:STDOUT:   %tuple.elem1.loc12.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @tuple.loc12_30, i64 8, i1 false), !dbg !9
-// CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8, !dbg !10
-// CHECK:STDOUT:   %tuple.elem0.loc13_23.1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %.loc13_23.1 = load i32, ptr %tuple.elem0.loc13_23.1.tuple.elem, align 4, !dbg !11
-// CHECK:STDOUT:   %tuple.elem0.loc13_23.2.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13_23.1, ptr %tuple.elem0.loc13_23.2.tuple.elem, align 4, !dbg !11
-// CHECK:STDOUT:   %tuple.elem1.loc13_23.1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   %.loc13_23.3 = load i32, ptr %tuple.elem1.loc13_23.1.tuple.elem, align 4, !dbg !11
-// CHECK:STDOUT:   %tuple.elem1.loc13_23.2.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   store i32 %.loc13_23.3, ptr %tuple.elem1.loc13_23.2.tuple.elem, align 4, !dbg !11
-// CHECK:STDOUT:   ret i32 0, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %y.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc13_23.1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   %.loc13_23.1 = load i32, ptr %tuple.elem0.loc13_23.1.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   %tuple.elem0.loc13_23.2.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13_23.1, ptr %tuple.elem0.loc13_23.2.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   %tuple.elem1.loc13_23.1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %x.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   %.loc13_23.3 = load i32, ptr %tuple.elem1.loc13_23.1.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   %tuple.elem1.loc13_23.2.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %y.var, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc13_23.3, ptr %tuple.elem1.loc13_23.2.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   ret i32 0, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 2, 4 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
@@ -55,6 +65,5 @@ fn Run() -> i32 {
 // CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
 // CHECK:STDOUT: !8 = !DILocation(line: 12, column: 23, scope: !4)
 // CHECK:STDOUT: !9 = !DILocation(line: 12, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 7, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 23, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 14, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 13, column: 23, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 3, scope: !4)

+ 49 - 40
toolchain/lower/testdata/tuple/value_formation.carbon

@@ -24,42 +24,52 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca { i32, i32, i32 }, align 8, !dbg !7
-// CHECK:STDOUT:   %b.var = alloca { i32, i32, i32 }, align 8, !dbg !8
-// CHECK:STDOUT:   %tuple.elem0.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !9
-// CHECK:STDOUT:   %.loc16_6.1 = load i32, ptr %tuple.elem0.loc16_6.tuple.elem, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.elem1.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !9
-// CHECK:STDOUT:   %.loc16_6.2 = load i32, ptr %tuple.elem1.loc16_6.tuple.elem, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.elem2.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !9
-// CHECK:STDOUT:   %.loc16_6.3 = load i32, ptr %tuple.elem2.loc16_6.tuple.elem, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.loc16_6 = alloca { i32, i32, i32 }, align 8, !dbg !9
-// CHECK:STDOUT:   %tuple.loc16_61 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 0, !dbg !9
-// CHECK:STDOUT:   store i32 %.loc16_6.1, ptr %tuple.loc16_61, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.loc16_62 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 1, !dbg !9
-// CHECK:STDOUT:   store i32 %.loc16_6.2, ptr %tuple.loc16_62, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.loc16_63 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 2, !dbg !9
-// CHECK:STDOUT:   store i32 %.loc16_6.3, ptr %tuple.loc16_63, align 4, !dbg !9
-// CHECK:STDOUT:   %tuple.elem0.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   %.loc16_9.1 = load i32, ptr %tuple.elem0.loc16_9.tuple.elem, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.elem1.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !10
-// CHECK:STDOUT:   %.loc16_9.2 = load i32, ptr %tuple.elem1.loc16_9.tuple.elem, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.elem2.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 2, !dbg !10
-// CHECK:STDOUT:   %.loc16_9.3 = load i32, ptr %tuple.elem2.loc16_9.tuple.elem, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.loc16_9 = alloca { i32, i32, i32 }, align 8, !dbg !10
-// CHECK:STDOUT:   %tuple.loc16_94 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc16_9.1, ptr %tuple.loc16_94, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.loc16_95 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 1, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc16_9.2, ptr %tuple.loc16_95, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.loc16_96 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 2, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc16_9.3, ptr %tuple.loc16_96, align 4, !dbg !10
-// CHECK:STDOUT:   %tuple.loc16_10 = alloca { ptr, ptr }, align 8, !dbg !11
-// CHECK:STDOUT:   %tuple.loc16_107 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc16_10, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   store ptr %tuple.loc16_6, ptr %tuple.loc16_107, align 8, !dbg !11
-// CHECK:STDOUT:   %tuple.loc16_108 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc16_10, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   store ptr %tuple.loc16_9, ptr %tuple.loc16_108, align 8, !dbg !11
-// CHECK:STDOUT:   call void @_CG.Main(ptr %tuple.loc16_10), !dbg !12
-// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT:   %b.var = alloca { i32, i32, i32 }, align 8, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %a.var), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %tuple.elem0.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !8
+// CHECK:STDOUT:   %.loc16_6.1 = load i32, ptr %tuple.elem0.loc16_6.tuple.elem, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.elem1.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 1, !dbg !8
+// CHECK:STDOUT:   %.loc16_6.2 = load i32, ptr %tuple.elem1.loc16_6.tuple.elem, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.elem2.loc16_6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !8
+// CHECK:STDOUT:   %.loc16_6.3 = load i32, ptr %tuple.elem2.loc16_6.tuple.elem, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.loc16_6 = alloca { i32, i32, i32 }, align 8, !dbg !8
+// CHECK:STDOUT:   %tuple.loc16_61 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 0, !dbg !8
+// CHECK:STDOUT:   store i32 %.loc16_6.1, ptr %tuple.loc16_61, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.loc16_62 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 1, !dbg !8
+// CHECK:STDOUT:   store i32 %.loc16_6.2, ptr %tuple.loc16_62, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.loc16_63 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_6, i32 0, i32 2, !dbg !8
+// CHECK:STDOUT:   store i32 %.loc16_6.3, ptr %tuple.loc16_63, align 4, !dbg !8
+// CHECK:STDOUT:   %tuple.elem0.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 0, !dbg !9
+// CHECK:STDOUT:   %.loc16_9.1 = load i32, ptr %tuple.elem0.loc16_9.tuple.elem, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.elem1.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 1, !dbg !9
+// CHECK:STDOUT:   %.loc16_9.2 = load i32, ptr %tuple.elem1.loc16_9.tuple.elem, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.elem2.loc16_9.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %b.var, i32 0, i32 2, !dbg !9
+// CHECK:STDOUT:   %.loc16_9.3 = load i32, ptr %tuple.elem2.loc16_9.tuple.elem, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.loc16_9 = alloca { i32, i32, i32 }, align 8, !dbg !9
+// CHECK:STDOUT:   %tuple.loc16_94 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 0, !dbg !9
+// CHECK:STDOUT:   store i32 %.loc16_9.1, ptr %tuple.loc16_94, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.loc16_95 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 1, !dbg !9
+// CHECK:STDOUT:   store i32 %.loc16_9.2, ptr %tuple.loc16_95, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.loc16_96 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple.loc16_9, i32 0, i32 2, !dbg !9
+// CHECK:STDOUT:   store i32 %.loc16_9.3, ptr %tuple.loc16_96, align 4, !dbg !9
+// CHECK:STDOUT:   %tuple.loc16_10 = alloca { ptr, ptr }, align 8, !dbg !10
+// CHECK:STDOUT:   %tuple.loc16_107 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc16_10, i32 0, i32 0, !dbg !10
+// CHECK:STDOUT:   store ptr %tuple.loc16_6, ptr %tuple.loc16_107, align 8, !dbg !10
+// CHECK:STDOUT:   %tuple.loc16_108 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc16_10, i32 0, i32 1, !dbg !10
+// CHECK:STDOUT:   store ptr %tuple.loc16_9, ptr %tuple.loc16_108, align 8, !dbg !10
+// CHECK:STDOUT:   call void @_CG.Main(ptr %tuple.loc16_10), !dbg !11
+// CHECK:STDOUT:   ret void, !dbg !12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:
@@ -71,9 +81,8 @@ fn F() {
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
 // CHECK:STDOUT: !7 = !DILocation(line: 14, column: 7, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 15, column: 7, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 16, column: 6, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 16, column: 9, scope: !4)
-// CHECK:STDOUT: !11 = !DILocation(line: 16, column: 5, scope: !4)
-// CHECK:STDOUT: !12 = !DILocation(line: 16, column: 3, scope: !4)
-// CHECK:STDOUT: !13 = !DILocation(line: 13, column: 1, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 16, column: 6, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 16, column: 9, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 16, column: 5, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 16, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 1, scope: !4)

+ 6 - 0
toolchain/lower/testdata/var/local.carbon

@@ -19,11 +19,17 @@ fn Run() -> i32 {
 // CHECK:STDOUT: define i32 @main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %x.var), !dbg !7
 // CHECK:STDOUT:   store i32 1, ptr %x.var, align 4, !dbg !8
 // CHECK:STDOUT:   %.loc13 = load i32, ptr %x.var, align 4, !dbg !9
 // CHECK:STDOUT:   ret i32 %.loc13, !dbg !10
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!2}
 // CHECK:STDOUT:

+ 75 - 0
toolchain/lower/testdata/var/nested.carbon

@@ -0,0 +1,75 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/var/nested.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/var/nested.carbon
+
+fn Run() -> i32 {
+  var a: i32 = 1;
+  while (true) {
+    var b: i32 = a;
+    a = b;
+  }
+  return a;
+}
+
+// CHECK:STDOUT: ; ModuleID = 'nested.carbon'
+// CHECK:STDOUT: source_filename = "nested.carbon"
+// CHECK:STDOUT:
+// CHECK:STDOUT: define i32 @main() !dbg !4 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %a.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %a.var), !dbg !7
+// CHECK:STDOUT:   store i32 1, ptr %a.var, align 4, !dbg !8
+// CHECK:STDOUT:   br label %while.cond, !dbg !9
+// CHECK:STDOUT:
+// CHECK:STDOUT: while.cond:                                       ; preds = %while.body, %entry
+// CHECK:STDOUT:   br i1 true, label %while.body, label %while.done, !dbg !9
+// CHECK:STDOUT:
+// CHECK:STDOUT: while.body:                                       ; preds = %while.cond
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !7
+// CHECK:STDOUT:   %.loc14 = load i32, ptr %a.var, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc14, ptr %b.var, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc15 = load i32, ptr %b.var, align 4, !dbg !12
+// CHECK:STDOUT:   store i32 %.loc15, ptr %a.var, align 4, !dbg !13
+// CHECK:STDOUT:   br label %while.cond, !dbg !14
+// CHECK:STDOUT:
+// CHECK:STDOUT: while.done:                                       ; preds = %while.cond
+// CHECK:STDOUT:   %.loc17 = load i32, ptr %a.var, align 4, !dbg !15
+// CHECK:STDOUT:   ret i32 %.loc17, !dbg !16
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 1, 0, 2 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !3 = !DIFile(filename: "nested.carbon", directory: "")
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Run", linkageName: "main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
+// CHECK:STDOUT: !6 = !{}
+// CHECK:STDOUT: !7 = !DILocation(line: 12, column: 7, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 12, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 13, column: 9, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 14, column: 18, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 5, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 9, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 15, column: 5, scope: !4)
+// CHECK:STDOUT: !14 = !DILocation(line: 13, column: 3, scope: !4)
+// CHECK:STDOUT: !15 = !DILocation(line: 17, column: 10, scope: !4)
+// CHECK:STDOUT: !16 = !DILocation(line: 17, column: 3, scope: !4)