Selaa lähdekoodia

Use static allocas for temporaries. (#5734)

Previously we created allocas for temporaries at whatever point in the
output LLVM function we'd reached. This would result in these being
dynamic allocas (performing a dynamic stack allocation), which is
inefficent and can lead to a stack overflow if it happens in a loop.

Switch to putting the allocas in the entry block instead, and instead
generate a lifetime start marker when we reach the point where the
temporary is introduced. We already did this for local variables; this
is just factoring out and reusing that code.
Richard Smith 10 kuukautta sitten
vanhempi
sitoutus
a556cf41fc

+ 44 - 0
toolchain/lower/function_context.cpp

@@ -198,6 +198,50 @@ auto FunctionContext::MakeSyntheticBlock() -> llvm::BasicBlock* {
   return synthetic_block_;
 }
 
+auto FunctionContext::CreateAlloca(llvm::Type* type, const llvm::Twine& name)
+    -> llvm::AllocaInst* {
+  // Position the first alloca right before the start of the executable code in
+  // the function.
+  llvm::AllocaInst* alloca;
+  {
+    llvm::IRBuilderBase::InsertPointGuard guard(builder());
+
+    auto debug_loc = builder().getCurrentDebugLocation();
+    if (after_allocas_) {
+      builder().SetInsertPoint(after_allocas_);
+    } else {
+      builder().SetInsertPointPastAllocas(&llvm_function());
+    }
+
+    // IRBuilder tramples over our debug location when setting the insert point,
+    // so undo that.
+    builder().SetCurrentDebugLocation(debug_loc);
+
+    // Create an alloca for this variable in the entry block.
+    alloca = builder().CreateAlloca(type, /*ArraySize=*/nullptr, name);
+  }
+
+  // Create a lifetime start intrinsic here to indicate where its scope really
+  // begins.
+  auto size = llvm_module().getDataLayout().getTypeAllocSize(type);
+  builder().CreateLifetimeStart(
+      alloca, llvm::ConstantInt::get(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 (!after_allocas_) {
+    auto loc = alloca->getIterator();
+    ++loc;
+    after_allocas_ = &*loc;
+  }
+
+  // TODO: Create a matching `@llvm.lifetime.end` intrinsic call when the
+  // variable goes out of scope.
+
+  return alloca;
+}
+
 auto FunctionContext::GetDebugLoc(SemIR::InstId inst_id) -> llvm::DebugLoc {
   if (!di_subprogram_) {
     return llvm::DebugLoc();

+ 5 - 13
toolchain/lower/function_context.h

@@ -175,19 +175,6 @@ class FunctionContext {
     return file_context_->GetIntLiteralAsValue();
   }
 
-  // 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) -> void {
-    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
@@ -199,6 +186,11 @@ class FunctionContext {
     return synthetic_block_ == block;
   }
 
+  // Creates an alloca instruction of the given type, adds it to the entry
+  // block, and starts the lifetime of the corresponding storage.
+  auto CreateAlloca(llvm::Type* type, const llvm::Twine& name = llvm::Twine())
+      -> llvm::AllocaInst*;
+
   // Returns the debug location to associate with the specified instruction.
   auto GetDebugLoc(SemIR::InstId inst_id) -> llvm::DebugLoc;
 

+ 2 - 43
toolchain/lower/handle.cpp

@@ -296,49 +296,8 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
                 SemIR::VarStorage /* inst */) -> void {
-  auto* type = context.GetTypeOfInst(inst_id);
-
-  // Position the first alloca right before the start of the executable code in
-  // the function.
-  llvm::AllocaInst* alloca;
-  {
-    llvm::IRBuilderBase::InsertPointGuard guard(context.builder());
-
-    auto debug_loc = context.builder().getCurrentDebugLocation();
-    if (auto* after_allocas = context.GetInstructionAfterAllocas()) {
-      context.builder().SetInsertPoint(after_allocas);
-    } else {
-      context.builder().SetInsertPointPastAllocas(&context.llvm_function());
-    }
-
-    // IRBuilder tramples over our debug location when setting the insert point,
-    // so undo that.
-    context.builder().SetCurrentDebugLocation(debug_loc);
-
-    // Create an alloca for this variable in the entry block.
-    alloca = context.builder().CreateAlloca(type);
-  }
-
-  // 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);
+  context.SetLocal(inst_id,
+                   context.CreateAlloca(context.GetTypeOfInst(inst_id)));
 }
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,

+ 2 - 3
toolchain/lower/handle_expr_category.cpp

@@ -44,9 +44,8 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
                 SemIR::TemporaryStorage /*inst*/) -> void {
-  auto* type = context.GetTypeOfInst(inst_id);
-  context.SetLocal(inst_id,
-                   context.builder().CreateAlloca(type, nullptr, "temp"));
+  context.SetLocal(
+      inst_id, context.CreateAlloca(context.GetTypeOfInst(inst_id), "temp"));
 }
 
 auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,

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

@@ -32,8 +32,9 @@ 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:   %.loc16_28.1.temp = alloca { i32, i32 }, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %t.var), !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %.loc16_28.1.temp), !dbg !11
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc16_28.1.temp), !dbg !11
 // CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc16_28.1.temp, i32 0, i32 0, !dbg !11
 // CHECK:STDOUT:   %.loc16_28.3 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !11
@@ -52,6 +53,9 @@ fn Run() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #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 #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:

+ 7 - 2
toolchain/lower/testdata/array/function_param.carbon

@@ -33,6 +33,7 @@ fn G() -> i32 {
 // CHECK:STDOUT: define i32 @_CG.Main() !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc18_20.3.temp = alloca [3 x i32], align 4, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %.loc18_20.3.temp), !dbg !10
 // CHECK:STDOUT:   %.loc18_20.4.array.index = getelementptr inbounds [3 x i32], ptr %.loc18_20.3.temp, i32 0, i64 0, !dbg !10
 // CHECK:STDOUT:   %.loc18_20.7.array.index = getelementptr inbounds [3 x i32], ptr %.loc18_20.3.temp, i32 0, i64 1, !dbg !10
 // CHECK:STDOUT:   %.loc18_20.10.array.index = getelementptr inbounds [3 x i32], ptr %.loc18_20.3.temp, i32 0, i64 2, !dbg !10
@@ -41,10 +42,14 @@ fn G() -> i32 {
 // 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 captures(none)) #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 writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #0
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), 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 - 1
toolchain/lower/testdata/builtins/no_op.carbon

@@ -51,6 +51,7 @@ fn J() {
 // CHECK:STDOUT: define void @_CH.Main() !dbg !11 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc27_18.1.temp = alloca {}, align 8, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc27_18.1.temp), !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -58,14 +59,18 @@ fn J() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CJ.Main() !dbg !14 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   call void @_CI.Main(), !dbg !15
 // CHECK:STDOUT:   %.loc33_11.1.temp = alloca {}, align 8, !dbg !15
+// CHECK:STDOUT:   call void @_CI.Main(), !dbg !15
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc33_11.1.temp), !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 captures(none)) #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}

+ 8 - 7
toolchain/lower/testdata/class/generic.carbon

@@ -268,12 +268,13 @@ fn AccessTuple() -> (i32, i32, i32) {
 // CHECK:STDOUT: define void @_CAccessEmpty.Main() !dbg !16 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %c.var = alloca { i1, {} }, align 8, !dbg !17
+// CHECK:STDOUT:   %.loc30_17.1.temp = alloca {}, align 8, !dbg !18
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %c.var), !dbg !17
-// CHECK:STDOUT:   %.loc29_37.2.v = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 0, !dbg !18
-// CHECK:STDOUT:   %.loc29_37.4.w = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 1, !dbg !18
+// CHECK:STDOUT:   %.loc29_37.2.v = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 0, !dbg !19
+// CHECK:STDOUT:   %.loc29_37.4.w = getelementptr inbounds nuw { i1, {} }, ptr %c.var, i32 0, i32 1, !dbg !19
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @C.val.dcc.loc29_3, i64 1, i1 false), !dbg !17
-// CHECK:STDOUT:   call void @_CGetT.C.Main.e43630e9a6c38c3f(ptr %c.var), !dbg !19
-// CHECK:STDOUT:   %.loc30_17.1.temp = alloca {}, align 8, !dbg !19
+// CHECK:STDOUT:   call void @_CGetT.C.Main.e43630e9a6c38c3f(ptr %c.var), !dbg !18
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc30_17.1.temp), !dbg !18
 // CHECK:STDOUT:   ret void, !dbg !20
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -324,7 +325,7 @@ fn AccessTuple() -> (i32, i32, i32) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 }
+// 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 nosync nounwind willreturn memory(argmem: readwrite) }
@@ -351,8 +352,8 @@ fn AccessTuple() -> (i32, i32, i32) {
 // CHECK:STDOUT: !15 = !DILocation(line: 25, column: 3, scope: !11)
 // CHECK:STDOUT: !16 = distinct !DISubprogram(name: "AccessEmpty", linkageName: "_CAccessEmpty.Main", scope: null, file: !3, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !17 = !DILocation(line: 29, column: 3, scope: !16)
-// CHECK:STDOUT: !18 = !DILocation(line: 29, column: 18, scope: !16)
-// CHECK:STDOUT: !19 = !DILocation(line: 30, column: 10, scope: !16)
+// CHECK:STDOUT: !18 = !DILocation(line: 30, column: 10, scope: !16)
+// CHECK:STDOUT: !19 = !DILocation(line: 29, column: 18, scope: !16)
 // CHECK:STDOUT: !20 = !DILocation(line: 30, column: 3, scope: !16)
 // CHECK:STDOUT: !21 = distinct !DISubprogram(name: "AccessTuple", linkageName: "_CAccessTuple.Main", scope: null, file: !3, line: 33, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !22 = !DILocation(line: 34, column: 3, scope: !21)

+ 5 - 1
toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon

@@ -35,9 +35,10 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !10 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca {}, align 8, !dbg !11
+// CHECK:STDOUT:   %.loc19_23.1.temp = alloca {}, align 8, !dbg !12
 // 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:   %.loc19_23.1.temp = alloca {}, align 8, !dbg !12
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc19_23.1.temp), !dbg !12
 // CHECK:STDOUT:   call void @_CBar.Main(), !dbg !13
 // CHECK:STDOUT:   ret void, !dbg !14
 // CHECK:STDOUT: }
@@ -45,6 +46,9 @@ fn Main() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #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}

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

@@ -42,10 +42,16 @@ fn Main() {
 // CHECK:STDOUT: define void @_CMain.Main() !dbg !12 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc18_21.1.temp = alloca { i32, i32, i32 }, align 8, !dbg !13
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 12, ptr %.loc18_21.1.temp), !dbg !13
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc18_21.1.temp, { i32 } { i32 1 }, ptr @tuple.11a.loc18_20.6), !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 captures(none)) #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:

+ 61 - 59
toolchain/lower/testdata/function/generic/call_basic.carbon

@@ -99,26 +99,27 @@ fn M() {
 // CHECK:STDOUT:   %ptr_f64.var = alloca ptr, align 8, !dbg !27
 // CHECK:STDOUT:   %ptr_i8.var = alloca ptr, align 8, !dbg !28
 // CHECK:STDOUT:   %c.var = alloca {}, align 8, !dbg !29
-// CHECK:STDOUT:   %H.call.loc25 = call i32 @_CH.Main.b88d1103f417c6d4(i32 %x), !dbg !30
-// CHECK:STDOUT:   %H.call.loc26 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !31
-// CHECK:STDOUT:   %H.call.loc27 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !32
-// CHECK:STDOUT:   %G.call = call i32 @_CG.Main.b88d1103f417c6d4(i32 %x), !dbg !33
-// CHECK:STDOUT:   %H.call.loc28 = call i32 @_CH.Main.b88d1103f417c6d4(i32 %G.call), !dbg !34
+// CHECK:STDOUT:   %.loc40_6.1.temp = alloca {}, align 8, !dbg !30
+// CHECK:STDOUT:   %H.call.loc25 = call i32 @_CH.Main.b88d1103f417c6d4(i32 %x), !dbg !31
+// CHECK:STDOUT:   %H.call.loc26 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !32
+// CHECK:STDOUT:   %H.call.loc27 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !33
+// CHECK:STDOUT:   %G.call = call i32 @_CG.Main.b88d1103f417c6d4(i32 %x), !dbg !34
+// CHECK:STDOUT:   %H.call.loc28 = call i32 @_CH.Main.b88d1103f417c6d4(i32 %G.call), !dbg !35
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %var_f64.var), !dbg !25
-// CHECK:STDOUT:   %.loc32 = load double, ptr %var_f64.var, align 8, !dbg !35
-// CHECK:STDOUT:   %H.call.loc32 = call double @_CH.Main.66be507887ceee78(double %.loc32), !dbg !36
+// CHECK:STDOUT:   %.loc32 = load double, ptr %var_f64.var, align 8, !dbg !36
+// CHECK:STDOUT:   %H.call.loc32 = call double @_CH.Main.66be507887ceee78(double %.loc32), !dbg !37
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_i32.var), !dbg !26
-// CHECK:STDOUT:   %.loc34 = load ptr, ptr %ptr_i32.var, align 8, !dbg !37
-// CHECK:STDOUT:   %H.call.loc34 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc34), !dbg !38
+// CHECK:STDOUT:   %.loc34 = load ptr, ptr %ptr_i32.var, align 8, !dbg !38
+// CHECK:STDOUT:   %H.call.loc34 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc34), !dbg !39
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_f64.var), !dbg !27
-// CHECK:STDOUT:   %.loc36 = load ptr, ptr %ptr_f64.var, align 8, !dbg !39
-// CHECK:STDOUT:   %H.call.loc36 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc36), !dbg !40
+// CHECK:STDOUT:   %.loc36 = load ptr, ptr %ptr_f64.var, align 8, !dbg !40
+// CHECK:STDOUT:   %H.call.loc36 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc36), !dbg !41
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_i8.var), !dbg !28
-// CHECK:STDOUT:   %.loc38 = load ptr, ptr %ptr_i8.var, align 8, !dbg !41
-// CHECK:STDOUT:   %H.call.loc38 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc38), !dbg !42
+// CHECK:STDOUT:   %.loc38 = load ptr, ptr %ptr_i8.var, align 8, !dbg !42
+// CHECK:STDOUT:   %H.call.loc38 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc38), !dbg !43
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %c.var), !dbg !29
-// CHECK:STDOUT:   %.loc40_6.1.temp = alloca {}, align 8, !dbg !43
-// CHECK:STDOUT:   call void @_CH.Main.15b1f98bd9cc0c5b(ptr %.loc40_6.1.temp, ptr %c.var), !dbg !43
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc40_6.1.temp), !dbg !30
+// CHECK:STDOUT:   call void @_CH.Main.15b1f98bd9cc0c5b(ptr %.loc40_6.1.temp, ptr %c.var), !dbg !30
 // CHECK:STDOUT:   ret i32 %x, !dbg !44
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -134,26 +135,27 @@ fn M() {
 // CHECK:STDOUT:   %ptr_f64.var = alloca ptr, align 8, !dbg !50
 // CHECK:STDOUT:   %ptr_i8.var = alloca ptr, align 8, !dbg !51
 // CHECK:STDOUT:   %c.var = alloca {}, align 8, !dbg !52
-// CHECK:STDOUT:   %H.call.loc25 = call double @_CH.Main.66be507887ceee78(double %x), !dbg !53
-// CHECK:STDOUT:   %H.call.loc26 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !54
-// CHECK:STDOUT:   %H.call.loc27 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !55
-// CHECK:STDOUT:   %G.call = call double @_CG.Main.66be507887ceee78(double %x), !dbg !56
-// CHECK:STDOUT:   %H.call.loc28 = call double @_CH.Main.66be507887ceee78(double %G.call), !dbg !57
+// CHECK:STDOUT:   %.loc40_6.1.temp = alloca {}, align 8, !dbg !53
+// CHECK:STDOUT:   %H.call.loc25 = call double @_CH.Main.66be507887ceee78(double %x), !dbg !54
+// CHECK:STDOUT:   %H.call.loc26 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !55
+// CHECK:STDOUT:   %H.call.loc27 = call %type @_CH.Main.5754c7a55c7cbe4a(%type zeroinitializer), !dbg !56
+// CHECK:STDOUT:   %G.call = call double @_CG.Main.66be507887ceee78(double %x), !dbg !57
+// CHECK:STDOUT:   %H.call.loc28 = call double @_CH.Main.66be507887ceee78(double %G.call), !dbg !58
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %var_f64.var), !dbg !48
-// CHECK:STDOUT:   %.loc32 = load double, ptr %var_f64.var, align 8, !dbg !58
-// CHECK:STDOUT:   %H.call.loc32 = call double @_CH.Main.66be507887ceee78(double %.loc32), !dbg !59
+// CHECK:STDOUT:   %.loc32 = load double, ptr %var_f64.var, align 8, !dbg !59
+// CHECK:STDOUT:   %H.call.loc32 = call double @_CH.Main.66be507887ceee78(double %.loc32), !dbg !60
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_i32.var), !dbg !49
-// CHECK:STDOUT:   %.loc34 = load ptr, ptr %ptr_i32.var, align 8, !dbg !60
-// CHECK:STDOUT:   %H.call.loc34 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc34), !dbg !61
+// CHECK:STDOUT:   %.loc34 = load ptr, ptr %ptr_i32.var, align 8, !dbg !61
+// CHECK:STDOUT:   %H.call.loc34 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc34), !dbg !62
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_f64.var), !dbg !50
-// CHECK:STDOUT:   %.loc36 = load ptr, ptr %ptr_f64.var, align 8, !dbg !62
-// CHECK:STDOUT:   %H.call.loc36 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc36), !dbg !63
+// CHECK:STDOUT:   %.loc36 = load ptr, ptr %ptr_f64.var, align 8, !dbg !63
+// CHECK:STDOUT:   %H.call.loc36 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc36), !dbg !64
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %ptr_i8.var), !dbg !51
-// CHECK:STDOUT:   %.loc38 = load ptr, ptr %ptr_i8.var, align 8, !dbg !64
-// CHECK:STDOUT:   %H.call.loc38 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc38), !dbg !65
+// CHECK:STDOUT:   %.loc38 = load ptr, ptr %ptr_i8.var, align 8, !dbg !65
+// CHECK:STDOUT:   %H.call.loc38 = call ptr @_CH.Main.e8193710fd35b608(ptr %.loc38), !dbg !66
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %c.var), !dbg !52
-// CHECK:STDOUT:   %.loc40_6.1.temp = alloca {}, align 8, !dbg !66
-// CHECK:STDOUT:   call void @_CH.Main.15b1f98bd9cc0c5b(ptr %.loc40_6.1.temp, ptr %c.var), !dbg !66
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc40_6.1.temp), !dbg !53
+// CHECK:STDOUT:   call void @_CH.Main.15b1f98bd9cc0c5b(ptr %.loc40_6.1.temp, ptr %c.var), !dbg !53
 // CHECK:STDOUT:   ret double %x, !dbg !67
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -183,7 +185,7 @@ fn M() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 12, 11, 10 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 14, 13, 12 }
 // CHECK:STDOUT: uselistorder ptr @_CH.Main.b88d1103f417c6d4, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @_CH.Main.5754c7a55c7cbe4a, { 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @_CH.Main.66be507887ceee78, { 3, 2, 1, 0 }
@@ -225,20 +227,20 @@ fn M() {
 // CHECK:STDOUT: !27 = !DILocation(line: 35, column: 3, scope: !24)
 // CHECK:STDOUT: !28 = !DILocation(line: 37, column: 3, scope: !24)
 // CHECK:STDOUT: !29 = !DILocation(line: 39, column: 3, scope: !24)
-// CHECK:STDOUT: !30 = !DILocation(line: 25, column: 3, scope: !24)
-// CHECK:STDOUT: !31 = !DILocation(line: 26, column: 3, scope: !24)
-// CHECK:STDOUT: !32 = !DILocation(line: 27, column: 3, scope: !24)
-// CHECK:STDOUT: !33 = !DILocation(line: 28, column: 5, scope: !24)
-// CHECK:STDOUT: !34 = !DILocation(line: 28, column: 3, scope: !24)
-// CHECK:STDOUT: !35 = !DILocation(line: 32, column: 5, scope: !24)
-// CHECK:STDOUT: !36 = !DILocation(line: 32, column: 3, scope: !24)
-// CHECK:STDOUT: !37 = !DILocation(line: 34, column: 5, scope: !24)
-// CHECK:STDOUT: !38 = !DILocation(line: 34, column: 3, scope: !24)
-// CHECK:STDOUT: !39 = !DILocation(line: 36, column: 5, scope: !24)
-// CHECK:STDOUT: !40 = !DILocation(line: 36, column: 3, scope: !24)
-// CHECK:STDOUT: !41 = !DILocation(line: 38, column: 5, scope: !24)
-// CHECK:STDOUT: !42 = !DILocation(line: 38, column: 3, scope: !24)
-// CHECK:STDOUT: !43 = !DILocation(line: 40, column: 3, scope: !24)
+// CHECK:STDOUT: !30 = !DILocation(line: 40, column: 3, scope: !24)
+// CHECK:STDOUT: !31 = !DILocation(line: 25, column: 3, scope: !24)
+// CHECK:STDOUT: !32 = !DILocation(line: 26, column: 3, scope: !24)
+// CHECK:STDOUT: !33 = !DILocation(line: 27, column: 3, scope: !24)
+// CHECK:STDOUT: !34 = !DILocation(line: 28, column: 5, scope: !24)
+// CHECK:STDOUT: !35 = !DILocation(line: 28, column: 3, scope: !24)
+// CHECK:STDOUT: !36 = !DILocation(line: 32, column: 5, scope: !24)
+// CHECK:STDOUT: !37 = !DILocation(line: 32, column: 3, scope: !24)
+// CHECK:STDOUT: !38 = !DILocation(line: 34, column: 5, scope: !24)
+// CHECK:STDOUT: !39 = !DILocation(line: 34, column: 3, scope: !24)
+// CHECK:STDOUT: !40 = !DILocation(line: 36, column: 5, scope: !24)
+// CHECK:STDOUT: !41 = !DILocation(line: 36, column: 3, scope: !24)
+// CHECK:STDOUT: !42 = !DILocation(line: 38, column: 5, scope: !24)
+// CHECK:STDOUT: !43 = !DILocation(line: 38, column: 3, scope: !24)
 // CHECK:STDOUT: !44 = !DILocation(line: 42, column: 3, scope: !24)
 // CHECK:STDOUT: !45 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main.66be507887ceee78", scope: null, file: !3, line: 15, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !46 = !DILocation(line: 15, column: 1, scope: !45)
@@ -248,20 +250,20 @@ fn M() {
 // CHECK:STDOUT: !50 = !DILocation(line: 35, column: 3, scope: !47)
 // CHECK:STDOUT: !51 = !DILocation(line: 37, column: 3, scope: !47)
 // CHECK:STDOUT: !52 = !DILocation(line: 39, column: 3, scope: !47)
-// CHECK:STDOUT: !53 = !DILocation(line: 25, column: 3, scope: !47)
-// CHECK:STDOUT: !54 = !DILocation(line: 26, column: 3, scope: !47)
-// CHECK:STDOUT: !55 = !DILocation(line: 27, column: 3, scope: !47)
-// CHECK:STDOUT: !56 = !DILocation(line: 28, column: 5, scope: !47)
-// CHECK:STDOUT: !57 = !DILocation(line: 28, column: 3, scope: !47)
-// CHECK:STDOUT: !58 = !DILocation(line: 32, column: 5, scope: !47)
-// CHECK:STDOUT: !59 = !DILocation(line: 32, column: 3, scope: !47)
-// CHECK:STDOUT: !60 = !DILocation(line: 34, column: 5, scope: !47)
-// CHECK:STDOUT: !61 = !DILocation(line: 34, column: 3, scope: !47)
-// CHECK:STDOUT: !62 = !DILocation(line: 36, column: 5, scope: !47)
-// CHECK:STDOUT: !63 = !DILocation(line: 36, column: 3, scope: !47)
-// CHECK:STDOUT: !64 = !DILocation(line: 38, column: 5, scope: !47)
-// CHECK:STDOUT: !65 = !DILocation(line: 38, column: 3, scope: !47)
-// CHECK:STDOUT: !66 = !DILocation(line: 40, column: 3, scope: !47)
+// CHECK:STDOUT: !53 = !DILocation(line: 40, column: 3, scope: !47)
+// CHECK:STDOUT: !54 = !DILocation(line: 25, column: 3, scope: !47)
+// CHECK:STDOUT: !55 = !DILocation(line: 26, column: 3, scope: !47)
+// CHECK:STDOUT: !56 = !DILocation(line: 27, column: 3, scope: !47)
+// CHECK:STDOUT: !57 = !DILocation(line: 28, column: 5, scope: !47)
+// CHECK:STDOUT: !58 = !DILocation(line: 28, column: 3, scope: !47)
+// CHECK:STDOUT: !59 = !DILocation(line: 32, column: 5, scope: !47)
+// CHECK:STDOUT: !60 = !DILocation(line: 32, column: 3, scope: !47)
+// CHECK:STDOUT: !61 = !DILocation(line: 34, column: 5, scope: !47)
+// CHECK:STDOUT: !62 = !DILocation(line: 34, column: 3, scope: !47)
+// CHECK:STDOUT: !63 = !DILocation(line: 36, column: 5, scope: !47)
+// CHECK:STDOUT: !64 = !DILocation(line: 36, column: 3, scope: !47)
+// CHECK:STDOUT: !65 = !DILocation(line: 38, column: 5, scope: !47)
+// CHECK:STDOUT: !66 = !DILocation(line: 38, column: 3, scope: !47)
 // CHECK:STDOUT: !67 = !DILocation(line: 42, column: 3, scope: !47)
 // CHECK:STDOUT: !68 = distinct !DISubprogram(name: "H", linkageName: "_CH.Main.b88d1103f417c6d4", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !69 = !DILocation(line: 19, column: 3, scope: !68)

+ 50 - 4
toolchain/lower/testdata/impl/import_thunk.carbon

@@ -100,13 +100,24 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc20_19.1.temp = alloca { i32 }, align 8, !dbg !19
 // CHECK:STDOUT:   %.loc16_9.1.temp = alloca { i32 }, align 8, !dbg !20
+// CHECK:STDOUT:   %.loc20_19.2.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc20_19.1.temp), !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc16_9.1.temp), !dbg !20
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc16_9.1.temp, ptr %a), !dbg !20
 // CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc20_19.1.temp, ptr %.loc16_9.1.temp), !dbg !19
-// CHECK:STDOUT:   %.loc20_19.2.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc20_19.2.temp), !dbg !19
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc20_19.2.temp, ptr %.loc20_19.1.temp), !dbg !19
 // CHECK:STDOUT:   ret void, !dbg !19
 // 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 captures(none)) #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:
@@ -139,9 +150,13 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT:   %.loc7_20.1.temp = alloca { i32 }, align 8, !dbg !7
 // CHECK:STDOUT:   %.loc7_20.2.temp = alloca { i32 }, align 8, !dbg !7
 // CHECK:STDOUT:   %.loc7_19.1.temp = alloca { i32 }, align 8, !dbg !8
+// CHECK:STDOUT:   %.loc7_21.1.temp = alloca { i32 }, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc7_20.1.temp), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc7_20.2.temp), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc7_19.1.temp), !dbg !8
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc7_19.1.temp, ptr %a), !dbg !8
 // CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc7_20.2.temp, ptr %.loc7_19.1.temp), !dbg !7
-// CHECK:STDOUT:   %.loc7_21.1.temp = alloca { i32 }, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc7_21.1.temp), !dbg !9
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc7_21.1.temp, ptr %.loc7_20.2.temp), !dbg !9
 // CHECK:STDOUT:   ret void, !dbg !9
 // CHECK:STDOUT: }
@@ -154,6 +169,14 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @"_CConvert.B.Main:ImplicitAs.Core"(ptr sret({ i32 }), ptr)
 // 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 captures(none)) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 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:
@@ -183,9 +206,12 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc9_19.1.temp = alloca { i32 }, align 8, !dbg !11
 // CHECK:STDOUT:   %.2.temp = alloca { i32 }, align 8
+// CHECK:STDOUT:   %.loc9_19.2.temp = alloca { i32 }, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc9_19.1.temp), !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.2.temp)
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.2.temp, ptr %a)
 // CHECK:STDOUT:   call void @"_CF.X.Main:I.Main"(ptr %.loc9_19.1.temp, ptr %.2.temp), !dbg !11
-// CHECK:STDOUT:   %.loc9_19.2.temp = alloca { i32 }, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc9_19.2.temp), !dbg !11
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc9_19.2.temp, ptr %.loc9_19.1.temp), !dbg !11
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
@@ -194,6 +220,14 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @"_CConvert.B.Main:ImplicitAs.Core"(ptr sret({ i32 }), ptr)
 // 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 captures(none)) #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:
@@ -217,9 +251,13 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT:   %.loc8_19.1.temp = alloca { i32 }, align 8, !dbg !7
 // CHECK:STDOUT:   %.loc8_19.2.temp = alloca { i32 }, align 8, !dbg !7
 // CHECK:STDOUT:   %.loc8_18.1.temp = alloca { i32 }, align 8, !dbg !8
+// CHECK:STDOUT:   %.loc8_20.1.temp = alloca { i32 }, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc8_19.1.temp), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc8_19.2.temp), !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc8_18.1.temp), !dbg !8
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc8_18.1.temp, ptr %a), !dbg !8
 // CHECK:STDOUT:   call void @"_CF.X.Main:I.Main"(ptr %.loc8_19.2.temp, ptr %.loc8_18.1.temp), !dbg !7
-// CHECK:STDOUT:   %.loc8_20.1.temp = alloca { i32 }, align 8, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc8_20.1.temp), !dbg !9
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc8_20.1.temp, ptr %.loc8_19.2.temp), !dbg !9
 // CHECK:STDOUT:   ret void, !dbg !9
 // CHECK:STDOUT: }
@@ -232,6 +270,14 @@ fn Test(a: A) -> C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @"_CConvert.B.Main:ImplicitAs.Core"(ptr sret({ i32 }), ptr)
 // 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 captures(none)) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 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:

+ 37 - 6
toolchain/lower/testdata/impl/thunk.carbon

@@ -104,9 +104,12 @@ fn CallCallGeneric(c: C(()), b: B) -> A {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc20_19.1.temp = alloca { i32 }, align 8, !dbg !19
 // CHECK:STDOUT:   %.loc16_9.1.temp = alloca { i32 }, align 8, !dbg !20
+// CHECK:STDOUT:   %.loc20_19.2.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc20_19.1.temp), !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc16_9.1.temp), !dbg !20
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc16_9.1.temp, ptr %a), !dbg !20
 // CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc20_19.1.temp, ptr %.loc16_9.1.temp), !dbg !19
-// CHECK:STDOUT:   %.loc20_19.2.temp = alloca { i32 }, align 8, !dbg !19
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc20_19.2.temp), !dbg !19
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc20_19.2.temp, ptr %.loc20_19.1.temp), !dbg !19
 // CHECK:STDOUT:   ret void, !dbg !19
 // CHECK:STDOUT: }
@@ -116,13 +119,25 @@ fn CallCallGeneric(c: C(()), b: B) -> A {
 // CHECK:STDOUT:   %.loc24_20.1.temp = alloca { i32 }, align 8, !dbg !22
 // CHECK:STDOUT:   %.loc24_20.2.temp = alloca { i32 }, align 8, !dbg !22
 // CHECK:STDOUT:   %.loc24_19.1.temp = alloca { i32 }, align 8, !dbg !23
+// CHECK:STDOUT:   %.loc24_21.1.temp = alloca { i32 }, align 8, !dbg !24
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc24_20.1.temp), !dbg !22
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc24_20.2.temp), !dbg !22
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc24_19.1.temp), !dbg !23
 // CHECK:STDOUT:   call void @"_CConvert.A.Main:ImplicitAs.Core"(ptr %.loc24_19.1.temp, ptr %a), !dbg !23
 // CHECK:STDOUT:   call void @"_CF.61ea2aba74ab3bf1:I.Main"(ptr %.loc24_20.2.temp, ptr %.loc24_19.1.temp), !dbg !22
-// CHECK:STDOUT:   %.loc24_21.1.temp = alloca { i32 }, align 8, !dbg !24
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %.loc24_21.1.temp), !dbg !24
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc24_21.1.temp, ptr %.loc24_20.2.temp), !dbg !24
 // CHECK:STDOUT:   ret void, !dbg !24
 // 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 captures(none)) #0
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 6, 5, 4, 3, 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:
@@ -167,12 +182,17 @@ fn CallCallGeneric(c: C(()), b: B) -> A {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc22_23.1.temp = alloca {}, align 8, !dbg !9
 // CHECK:STDOUT:   %.loc22_23.3.temp = alloca {}, align 8, !dbg !9
-// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc22_23.3.temp, ptr %b), !dbg !9
 // CHECK:STDOUT:   %.loc22_23.7.temp = alloca {}, align 8, !dbg !9
 // CHECK:STDOUT:   %.loc22_22.1.temp = alloca {}, align 8, !dbg !10
+// CHECK:STDOUT:   %.loc22_24.1.temp = alloca {}, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc22_23.1.temp), !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc22_23.3.temp), !dbg !9
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc22_23.3.temp, ptr %b), !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc22_23.7.temp), !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc22_22.1.temp), !dbg !10
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc22_22.1.temp, ptr %b), !dbg !10
 // CHECK:STDOUT:   call void @"_CF.C.Main:I.Main.e43630e9a6c38c3f"(ptr %.loc22_23.7.temp, ptr %c, ptr %.loc22_22.1.temp), !dbg !9
-// CHECK:STDOUT:   %.loc22_24.1.temp = alloca {}, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc22_24.1.temp), !dbg !11
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc22_24.1.temp, ptr %.loc22_23.7.temp), !dbg !11
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
@@ -186,6 +206,9 @@ fn CallCallGeneric(c: C(()), b: B) -> A {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), 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 captures(none)) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: define linkonce_odr void @"_CF.C.Main:I.Main.e43630e9a6c38c3f"(ptr sret({}) %return, ptr %self, ptr %x) !dbg !15 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @B.val.loc18_42, i64 0, i1 false), !dbg !16
@@ -201,17 +224,25 @@ fn CallCallGeneric(c: C(()), b: B) -> A {
 // CHECK:STDOUT: define linkonce_odr void @"_CF:thunk.C.Main:I.Main.e43630e9a6c38c3f"(ptr sret({}) %return, ptr %self, ptr %x) !dbg !20 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc18_31.2.temp = alloca {}, align 8, !dbg !21
-// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc18_31.2.temp, ptr %x), !dbg !21
 // CHECK:STDOUT:   %.loc18_31.6.temp = alloca {}, align 8, !dbg !21
 // CHECK:STDOUT:   %.loc12_21.1.temp = alloca {}, align 8, !dbg !22
+// CHECK:STDOUT:   %.loc18_31.7.temp = alloca {}, align 8, !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc18_31.2.temp), !dbg !21
+// CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc18_31.2.temp, ptr %x), !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc18_31.6.temp), !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc12_21.1.temp), !dbg !22
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc12_21.1.temp, ptr %x), !dbg !22
 // CHECK:STDOUT:   call void @"_CF.C.Main:I.Main.e43630e9a6c38c3f"(ptr %.loc18_31.6.temp, ptr %self, ptr %.loc12_21.1.temp), !dbg !21
-// CHECK:STDOUT:   %.loc18_31.7.temp = alloca {}, align 8, !dbg !21
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 0, ptr %.loc18_31.7.temp), !dbg !21
 // CHECK:STDOUT:   call void @"_CConvert.B.Main:ImplicitAs.Core"(ptr %.loc18_31.7.temp, ptr %.loc18_31.6.temp), !dbg !21
 // CHECK:STDOUT:   ret void, !dbg !21
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 0, 1, 2, 3, 8, 7, 6, 5, 4 }
+// 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}

+ 36 - 34
toolchain/lower/testdata/index/array_element_access.carbon

@@ -45,33 +45,35 @@ 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 !14
-// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !15
-// CHECK:STDOUT:   %d.var = alloca i32, align 4, !dbg !16
+// CHECK:STDOUT:   %.loc17_28.1.temp = alloca { i32, i32 }, align 8, !dbg !14
+// CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !15
+// CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !16
+// CHECK:STDOUT:   %d.var = alloca i32, align 4, !dbg !17
+// CHECK:STDOUT:   %.loc20_18.1.temp = alloca [2 x i32], align 4, !dbg !18
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %a.var), !dbg !13
-// CHECK:STDOUT:   %.loc17_28.1.temp = alloca { i32, i32 }, align 8, !dbg !17
-// CHECK:STDOUT:   call void @_CA.Main(ptr %.loc17_28.1.temp), !dbg !17
-// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc17_28.1.temp, i32 0, i32 0, !dbg !17
-// CHECK:STDOUT:   %.loc17_28.3 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !17
-// CHECK:STDOUT:   %.loc17_28.4.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i64 0, !dbg !17
-// CHECK:STDOUT:   store i32 %.loc17_28.3, ptr %.loc17_28.4.array.index, align 4, !dbg !17
-// CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc17_28.1.temp, i32 0, i32 1, !dbg !17
-// CHECK:STDOUT:   %.loc17_28.6 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !17
-// CHECK:STDOUT:   %.loc17_28.7.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i64 1, !dbg !17
-// CHECK:STDOUT:   store i32 %.loc17_28.6, ptr %.loc17_28.7.array.index, align 4, !dbg !17
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !14
-// CHECK:STDOUT:   store i32 1, ptr %b.var, align 4, !dbg !14
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %c.var), !dbg !15
-// CHECK:STDOUT:   %.loc19_18 = load i32, ptr %b.var, align 4, !dbg !18
-// CHECK:STDOUT:   %.loc19_19.1.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i32 %.loc19_18, !dbg !19
-// CHECK:STDOUT:   %.loc19_19.2 = load i32, ptr %.loc19_19.1.array.index, align 4, !dbg !19
-// CHECK:STDOUT:   store i32 %.loc19_19.2, ptr %c.var, align 4, !dbg !15
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %d.var), !dbg !16
-// CHECK:STDOUT:   %.loc20_18.1.temp = alloca [2 x i32], align 4, !dbg !20
-// CHECK:STDOUT:   call void @_CB.Main(ptr %.loc20_18.1.temp), !dbg !20
-// CHECK:STDOUT:   %.loc20_21.1.array.index = getelementptr inbounds [2 x i32], ptr %.loc20_18.1.temp, i32 0, i32 1, !dbg !20
-// CHECK:STDOUT:   %.loc20_21.2 = load i32, ptr %.loc20_21.1.array.index, align 4, !dbg !20
-// CHECK:STDOUT:   store i32 %.loc20_21.2, ptr %d.var, align 4, !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %.loc17_28.1.temp), !dbg !14
+// CHECK:STDOUT:   call void @_CA.Main(ptr %.loc17_28.1.temp), !dbg !14
+// CHECK:STDOUT:   %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc17_28.1.temp, i32 0, i32 0, !dbg !14
+// CHECK:STDOUT:   %.loc17_28.3 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !14
+// CHECK:STDOUT:   %.loc17_28.4.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i64 0, !dbg !14
+// CHECK:STDOUT:   store i32 %.loc17_28.3, ptr %.loc17_28.4.array.index, align 4, !dbg !14
+// CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc17_28.1.temp, i32 0, i32 1, !dbg !14
+// CHECK:STDOUT:   %.loc17_28.6 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !14
+// CHECK:STDOUT:   %.loc17_28.7.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i64 1, !dbg !14
+// CHECK:STDOUT:   store i32 %.loc17_28.6, ptr %.loc17_28.7.array.index, align 4, !dbg !14
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %b.var), !dbg !15
+// 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 !16
+// CHECK:STDOUT:   %.loc19_18 = load i32, ptr %b.var, align 4, !dbg !19
+// CHECK:STDOUT:   %.loc19_19.1.array.index = getelementptr inbounds [2 x i32], ptr %a.var, i32 0, i32 %.loc19_18, !dbg !20
+// CHECK:STDOUT:   %.loc19_19.2 = load i32, ptr %.loc19_19.1.array.index, align 4, !dbg !20
+// CHECK:STDOUT:   store i32 %.loc19_19.2, ptr %c.var, align 4, !dbg !16
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %d.var), !dbg !17
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %.loc20_18.1.temp), !dbg !18
+// CHECK:STDOUT:   call void @_CB.Main(ptr %.loc20_18.1.temp), !dbg !18
+// CHECK:STDOUT:   %.loc20_21.1.array.index = getelementptr inbounds [2 x i32], ptr %.loc20_18.1.temp, i32 0, i32 1, !dbg !18
+// CHECK:STDOUT:   %.loc20_21.2 = load i32, ptr %.loc20_21.1.array.index, align 4, !dbg !18
+// CHECK:STDOUT:   store i32 %.loc20_21.2, ptr %d.var, align 4, !dbg !17
 // CHECK:STDOUT:   ret void, !dbg !21
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -83,7 +85,7 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // 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: uselistorder ptr @llvm.lifetime.start.p0, { 5, 4, 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) }
@@ -105,11 +107,11 @@ fn Run() {
 // CHECK:STDOUT: !11 = !DILocation(line: 14, column: 27, scope: !9)
 // CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Run", linkageName: "main", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !13 = !DILocation(line: 17, column: 3, scope: !12)
-// CHECK:STDOUT: !14 = !DILocation(line: 18, column: 3, scope: !12)
-// CHECK:STDOUT: !15 = !DILocation(line: 19, column: 3, scope: !12)
-// CHECK:STDOUT: !16 = !DILocation(line: 20, column: 3, scope: !12)
-// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 26, scope: !12)
-// CHECK:STDOUT: !18 = !DILocation(line: 19, column: 18, scope: !12)
-// CHECK:STDOUT: !19 = !DILocation(line: 19, column: 16, scope: !12)
-// CHECK:STDOUT: !20 = !DILocation(line: 20, column: 16, scope: !12)
+// CHECK:STDOUT: !14 = !DILocation(line: 17, column: 26, scope: !12)
+// CHECK:STDOUT: !15 = !DILocation(line: 18, column: 3, scope: !12)
+// CHECK:STDOUT: !16 = !DILocation(line: 19, column: 3, scope: !12)
+// CHECK:STDOUT: !17 = !DILocation(line: 20, column: 3, scope: !12)
+// CHECK:STDOUT: !18 = !DILocation(line: 20, column: 16, scope: !12)
+// CHECK:STDOUT: !19 = !DILocation(line: 19, column: 18, scope: !12)
+// CHECK:STDOUT: !20 = !DILocation(line: 19, column: 16, scope: !12)
 // CHECK:STDOUT: !21 = !DILocation(line: 16, column: 1, scope: !12)

+ 6 - 0
toolchain/lower/testdata/operators/overloaded.carbon

@@ -83,11 +83,17 @@ fn Calculate(a: Number, b: Number) -> Number {
 // CHECK:STDOUT: define void @_CCalculate.Main(ptr sret({ i1 }) %return, ptr %a, ptr %b) !dbg !21 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc28_10.1.temp = alloca { i1 }, align 8, !dbg !22
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %.loc28_10.1.temp), !dbg !22
 // CHECK:STDOUT:   call void @"_COp.Number.Main:Negate.Core"(ptr %.loc28_10.1.temp, ptr %a), !dbg !22
 // CHECK:STDOUT:   call void @"_COp.Number.Main:MulWith.Core"(ptr %return, ptr %.loc28_10.1.temp, ptr %b), !dbg !22
 // CHECK:STDOUT:   ret void, !dbg !23
 // 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 captures(none)) #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 - 1
toolchain/lower/testdata/tuple/access/return_value_access.carbon

@@ -32,8 +32,9 @@ 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:   %.loc16_18.1.temp = alloca { i32, i32 }, align 8, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %t.var), !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %.loc16_18.1.temp), !dbg !11
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc16_18.1.temp), !dbg !11
 // CHECK:STDOUT:   %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc16_18.1.temp, i32 0, i32 1, !dbg !11
 // CHECK:STDOUT:   %.loc16_19 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !11
@@ -47,6 +48,9 @@ fn Run() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #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 #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: