From ccf87f0a380e3dbbf3b3ca055a122814b0587b1c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 23 Apr 2024 09:23:24 -0700 Subject: [PATCH] Use computed constants in lowering rather than lowering instructions (#3905) First steps towards using constant values in lowering. For now, we reuse the regular instruction lowering to lower constants. This mostly works, because we don't actually need an `llvm::Function` or a current basic block when lowering a constant most of the time. However, a special case is needed for lowering aggregate value constants because they would otherwise create a stack alloca to store the constant. Separate constant lowering code will be added in a future change to clean this up. When lowering a constant initializing expression, the result is a value of the destination type, rather than code to initialize the destination, so a separate copy step is required when finishing initialization from a constant for a type that uses in-place initialization. Handling this required extending `ReturnExpr` to track its destination location. We currently often create non-constant `*_access` SemIR instructions that are only used by constant `*_init` instructions. These cause lowering to leave behind `getelementptr` instructions in the lowered IR that are now unused. It should be possible to detect this case and avoid producing these instructions, or to produce them lazily, but for now we're just leaving them around for LLVM to clean up. --- toolchain/check/return.cpp | 9 +++- .../check/testdata/alias/in_namespace.carbon | 2 +- .../testdata/as/adapter_conversion.carbon | 2 +- .../testdata/basics/raw_and_textual_ir.carbon | 4 +- toolchain/check/testdata/basics/raw_ir.carbon | 2 +- .../check/testdata/basics/textual_ir.carbon | 2 +- toolchain/check/testdata/class/base.carbon | 4 +- .../check/testdata/class/fail_abstract.carbon | 4 +- .../class/fail_convert_to_invalid.carbon | 2 +- .../check/testdata/class/fail_self.carbon | 2 +- toolchain/check/testdata/class/init.carbon | 4 +- .../check/testdata/class/init_nested.carbon | 2 +- .../check/testdata/class/raw_self.carbon | 2 +- .../check/testdata/class/self_type.carbon | 2 +- .../expr_category/in_place_tuple_init.carbon | 2 +- .../testdata/global/class_with_fun.carbon | 2 +- .../testdata/impl/self_in_signature.carbon | 4 +- .../testdata/operators/overloaded/add.carbon | 4 +- .../operators/overloaded/bit_and.carbon | 4 +- .../overloaded/bit_complement.carbon | 4 +- .../operators/overloaded/bit_or.carbon | 4 +- .../operators/overloaded/bit_xor.carbon | 4 +- .../testdata/operators/overloaded/div.carbon | 4 +- .../operators/overloaded/fail_no_impl.carbon | 4 +- .../overloaded/fail_no_impl_for_arg.carbon | 2 +- .../operators/overloaded/left_shift.carbon | 4 +- .../testdata/operators/overloaded/mod.carbon | 4 +- .../testdata/operators/overloaded/mul.carbon | 4 +- .../operators/overloaded/negate.carbon | 4 +- .../operators/overloaded/right_shift.carbon | 4 +- .../testdata/operators/overloaded/sub.carbon | 4 +- .../check/testdata/return/returned_var.carbon | 2 +- toolchain/check/testdata/return/tuple.carbon | 2 +- .../testdata/struct/reorder_fields.carbon | 2 +- toolchain/lower/file_context.cpp | 18 +++++++ toolchain/lower/function_context.cpp | 54 ++++++++++++------- toolchain/lower/function_context.h | 9 ++++ toolchain/lower/handle.cpp | 11 ++-- toolchain/lower/handle_aggregates.cpp | 51 ++++++++++++++---- .../testdata/array/assign_return_value.carbon | 10 +++- toolchain/lower/testdata/array/base.carbon | 26 ++++++--- .../testdata/array/function_param.carbon | 11 ++-- .../testdata/basics/numeric_literals.carbon | 23 ++++---- toolchain/lower/testdata/class/adapt.carbon | 13 ++++- toolchain/lower/testdata/class/base.carbon | 11 +++- .../function/call/struct_param.carbon | 12 ++--- .../testdata/function/call/tuple_param.carbon | 12 ++--- .../call/tuple_param_with_return_slot.carbon | 11 ++-- .../index/array_element_access.carbon | 18 +++++-- .../index/tuple_element_access.carbon | 16 ++++-- .../index/tuple_return_value_access.carbon | 10 +++- toolchain/lower/testdata/let/tuple.carbon | 20 +++++-- .../testdata/operators/assignment.carbon | 13 ++++- .../testdata/pointer/address_of_field.carbon | 13 ++++- .../lower/testdata/return/return_var.carbon | 1 - .../testdata/struct/member_access.carbon | 13 ++++- .../lower/testdata/struct/two_entries.carbon | 13 ++++- .../lower/testdata/tuple/two_entries.carbon | 10 +++- toolchain/sem_ir/formatter.cpp | 7 +++ toolchain/sem_ir/typed_insts.h | 2 + 60 files changed, 357 insertions(+), 162 deletions(-) diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 4b27d77f7969..4abfc08b46b6 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -118,6 +118,7 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id, SemIR::InstId expr_id) -> void { const auto& function = GetCurrentFunction(context); auto returned_var_id = GetCurrentReturnedVar(context); + auto return_slot_id = SemIR::InstId::Invalid; if (!function.return_type_id.is_valid()) { CARBON_DIAGNOSTIC( @@ -137,6 +138,7 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id, expr_id = SemIR::InstId::BuiltinError; } else if (function.has_return_slot()) { expr_id = Initialize(context, node_id, function.return_storage_id, expr_id); + return_slot_id = function.return_storage_id; } else if (function.return_slot == SemIR::Function::ReturnSlot::Error) { // Don't produce a second error complaining the return type is incomplete. expr_id = SemIR::InstId::BuiltinError; @@ -145,7 +147,7 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id, function.return_type_id); } - context.AddInst({node_id, SemIR::ReturnExpr{expr_id}}); + context.AddInst({node_id, SemIR::ReturnExpr{expr_id, return_slot_id}}); } auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) @@ -160,13 +162,16 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) returned_var_id = SemIR::InstId::BuiltinError; } + auto return_slot_id = function.return_storage_id; if (!function.has_return_slot()) { // If we don't have a return slot, we're returning by value. Convert to a // value expression. returned_var_id = ConvertToValueExpr(context, returned_var_id); + return_slot_id = SemIR::InstId::Invalid; } - context.AddInst({node_id, SemIR::ReturnExpr{returned_var_id}}); + context.AddInst( + {node_id, SemIR::ReturnExpr{returned_var_id, return_slot_id}}); } } // namespace Carbon::Check diff --git a/toolchain/check/testdata/alias/in_namespace.carbon b/toolchain/check/testdata/alias/in_namespace.carbon index b88c4efebe04..2a1e350db328 100644 --- a/toolchain/check/testdata/alias/in_namespace.carbon +++ b/toolchain/check/testdata/alias/in_namespace.carbon @@ -75,6 +75,6 @@ fn F() -> NS.a { // CHECK:STDOUT: %.loc15_17.3: init i32 = initialize_from %.loc15_16 to %.loc15_17.2 [template = constants.%.4] // CHECK:STDOUT: %.loc15_17.4: init C = class_init (%.loc15_17.3), %return [template = constants.%.5] // CHECK:STDOUT: %.loc15_18: init C = converted %.loc15_17.1, %.loc15_17.4 [template = constants.%.5] -// CHECK:STDOUT: return %.loc15_18 +// CHECK:STDOUT: return %.loc15_18 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/as/adapter_conversion.carbon b/toolchain/check/testdata/as/adapter_conversion.carbon index f95a47bdbdd7..724ed1d4d3b1 100644 --- a/toolchain/check/testdata/as/adapter_conversion.carbon +++ b/toolchain/check/testdata/as/adapter_conversion.carbon @@ -183,7 +183,7 @@ var b: B = {.x = 1} as B; // CHECK:STDOUT: %.loc9_27.5: init i32 = initialize_from %.loc9_26 to %.loc9_27.4 [template = constants.%.5] // CHECK:STDOUT: %.loc9_27.6: init A = class_init (%.loc9_27.3, %.loc9_27.5), @A.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc9_28: init A = converted %.loc9_27.1, %.loc9_27.6 [template = constants.%.6] -// CHECK:STDOUT: return %.loc9_28 +// CHECK:STDOUT: return %.loc9_28 to @A.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { diff --git a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon index 6dd682af35b5..0d8f6280f38a 100644 --- a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon @@ -77,7 +77,7 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: inst+26: {kind: TupleInit, arg0: block10, arg1: inst+13, type: type2} // CHECK:STDOUT: inst+27: {kind: TupleValue, arg0: block12, type: type2} // CHECK:STDOUT: inst+28: {kind: Converted, arg0: inst+18, arg1: inst+26, type: type2} -// CHECK:STDOUT: inst+29: {kind: ReturnExpr, arg0: inst+28} +// CHECK:STDOUT: inst+29: {kind: ReturnExpr, arg0: inst+28, arg1: inst+13} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: inst+0: template inst+0 // CHECK:STDOUT: inst+1: template inst+1 @@ -190,6 +190,6 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: %.loc12_16.5: init () = converted %.loc12_15.1, %.loc12_15.2 [template = constants.%.4] // CHECK:STDOUT: %.loc12_16.6: init ((), ()) = tuple_init (%.loc12_16.3, %.loc12_16.5) to %return [template = constants.%.5] // CHECK:STDOUT: %.loc12_17: init ((), ()) = converted %.loc12_16.1, %.loc12_16.6 [template = constants.%.5] -// CHECK:STDOUT: return %.loc12_17 +// CHECK:STDOUT: return %.loc12_17 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/basics/raw_ir.carbon b/toolchain/check/testdata/basics/raw_ir.carbon index ed13a4587ecf..f76f12795fbd 100644 --- a/toolchain/check/testdata/basics/raw_ir.carbon +++ b/toolchain/check/testdata/basics/raw_ir.carbon @@ -77,7 +77,7 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: inst+26: {kind: TupleInit, arg0: block10, arg1: inst+13, type: type2} // CHECK:STDOUT: inst+27: {kind: TupleValue, arg0: block12, type: type2} // CHECK:STDOUT: inst+28: {kind: Converted, arg0: inst+18, arg1: inst+26, type: type2} -// CHECK:STDOUT: inst+29: {kind: ReturnExpr, arg0: inst+28} +// CHECK:STDOUT: inst+29: {kind: ReturnExpr, arg0: inst+28, arg1: inst+13} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: inst+0: template inst+0 // CHECK:STDOUT: inst+1: template inst+1 diff --git a/toolchain/check/testdata/basics/textual_ir.carbon b/toolchain/check/testdata/basics/textual_ir.carbon index 43689ad546ef..119ccdffeb25 100644 --- a/toolchain/check/testdata/basics/textual_ir.carbon +++ b/toolchain/check/testdata/basics/textual_ir.carbon @@ -54,6 +54,6 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: %.loc12_16.5: init () = converted %.loc12_15.1, %.loc12_15.2 [template = constants.%.4] // CHECK:STDOUT: %.loc12_16.6: init ((), ()) = tuple_init (%.loc12_16.3, %.loc12_16.5) to %return [template = constants.%.5] // CHECK:STDOUT: %.loc12_17: init ((), ()) = converted %.loc12_16.1, %.loc12_16.6 [template = constants.%.5] -// CHECK:STDOUT: return %.loc12_17 +// CHECK:STDOUT: return %.loc12_17 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/base.carbon b/toolchain/check/testdata/class/base.carbon index 5c9276a09015..923fa130efdf 100644 --- a/toolchain/check/testdata/class/base.carbon +++ b/toolchain/check/testdata/class/base.carbon @@ -106,7 +106,7 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: %.loc18_35.5: init i32 = initialize_from %.loc18_34 to %.loc18_35.4 [template = constants.%.11] // CHECK:STDOUT: %.loc18_35.6: init Derived = class_init (%.loc18_35.3, %.loc18_35.5), %return [template = constants.%.14] // CHECK:STDOUT: %.loc18_36: init Derived = converted %.loc18_35.1, %.loc18_35.6 [template = constants.%.14] -// CHECK:STDOUT: return %.loc18_36 +// CHECK:STDOUT: return %.loc18_36 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d: Derived) -> %return: (i32, i32) { @@ -129,6 +129,6 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: %.loc22_24.5: init i32 = initialize_from %.loc22_22.2 to %.loc22_24.4 // CHECK:STDOUT: %.loc22_24.6: init (i32, i32) = tuple_init (%.loc22_24.3, %.loc22_24.5) to %return // CHECK:STDOUT: %.loc22_25: init (i32, i32) = converted %.loc22_24.1, %.loc22_24.6 -// CHECK:STDOUT: return %.loc22_25 +// CHECK:STDOUT: return %.loc22_25 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_abstract.carbon b/toolchain/check/testdata/class/fail_abstract.carbon index 98f13d26802c..e9206d9995bc 100644 --- a/toolchain/check/testdata/class/fail_abstract.carbon +++ b/toolchain/check/testdata/class/fail_abstract.carbon @@ -99,7 +99,7 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: %.loc22_26: {.a: i32} = struct_literal (%.loc22_25) // CHECK:STDOUT: %.loc22_34: i32 = int_literal 7 [template = constants.%.11] // CHECK:STDOUT: %.loc22_35: {.base: {.a: i32}, .d: i32} = struct_literal (%.loc22_26, %.loc22_34) -// CHECK:STDOUT: return +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d: Derived) -> %return: (i32, i32) { @@ -122,6 +122,6 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: %.loc26_24.5: init i32 = initialize_from %.loc26_22.2 to %.loc26_24.4 // CHECK:STDOUT: %.loc26_24.6: init (i32, i32) = tuple_init (%.loc26_24.3, %.loc26_24.5) to %return // CHECK:STDOUT: %.loc26_25: init (i32, i32) = converted %.loc26_24.1, %.loc26_24.6 -// CHECK:STDOUT: return %.loc26_25 +// CHECK:STDOUT: return %.loc26_25 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_convert_to_invalid.carbon b/toolchain/check/testdata/class/fail_convert_to_invalid.carbon index a46e87c9e39c..76a6e0a996e7 100644 --- a/toolchain/check/testdata/class/fail_convert_to_invalid.carbon +++ b/toolchain/check/testdata/class/fail_convert_to_invalid.carbon @@ -50,6 +50,6 @@ fn Make() -> C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc15_16: i32 = int_literal 123 [template = constants.%.1] // CHECK:STDOUT: %.loc15_19: {.a: i32} = struct_literal (%.loc15_16) -// CHECK:STDOUT: return +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_self.carbon b/toolchain/check/testdata/class/fail_self.carbon index c29cf3242fe5..8970b6836db0 100644 --- a/toolchain/check/testdata/class/fail_self.carbon +++ b/toolchain/check/testdata/class/fail_self.carbon @@ -125,7 +125,7 @@ fn CallWrongSelf(ws: WrongSelf) { // CHECK:STDOUT: %self: ref Class = bind_name self, %self.var // CHECK:STDOUT: %self.ref: ref Class = name_ref self, %self // CHECK:STDOUT: %.loc34: Class = bind_value %self.ref -// CHECK:STDOUT: return +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.2[@WrongSelf.%self.loc38_8.2: Class](); diff --git a/toolchain/check/testdata/class/init.carbon b/toolchain/check/testdata/class/init.carbon index 05b6dbb30280..5abd61c83a73 100644 --- a/toolchain/check/testdata/class/init.carbon +++ b/toolchain/check/testdata/class/init.carbon @@ -83,7 +83,7 @@ fn MakeReorder(n: i32, next: Class*) -> Class { // CHECK:STDOUT: %.loc13_31.5: init Class* = initialize_from %next.ref to %.loc13_31.4 // CHECK:STDOUT: %.loc13_31.6: init Class = class_init (%.loc13_31.3, %.loc13_31.5), %return // CHECK:STDOUT: %.loc13_32: init Class = converted %.loc13_31.1, %.loc13_31.6 -// CHECK:STDOUT: return %.loc13_32 +// CHECK:STDOUT: return %.loc13_32 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeReorder(%n: i32, %next: Class*) -> %return: Class { @@ -97,6 +97,6 @@ fn MakeReorder(n: i32, next: Class*) -> Class { // CHECK:STDOUT: %.loc17_31.5: init Class* = initialize_from %next.ref to %.loc17_31.4 // CHECK:STDOUT: %.loc17_31.6: init Class = class_init (%.loc17_31.3, %.loc17_31.5), %return // CHECK:STDOUT: %.loc17_32: init Class = converted %.loc17_31.1, %.loc17_31.6 -// CHECK:STDOUT: return %.loc17_32 +// CHECK:STDOUT: return %.loc17_32 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/init_nested.carbon b/toolchain/check/testdata/class/init_nested.carbon index c04f817cb618..118a8e7dddd7 100644 --- a/toolchain/check/testdata/class/init_nested.carbon +++ b/toolchain/check/testdata/class/init_nested.carbon @@ -91,6 +91,6 @@ fn MakeOuter() -> Outer { // CHECK:STDOUT: %.loc20_45.3: {.c: Inner, .d: Inner} = struct_literal (%MakeInner.call.loc20_25, %MakeInner.call.loc20_43) // CHECK:STDOUT: %.loc20_45.4: init Outer = class_init (%MakeInner.call.loc20_25, %MakeInner.call.loc20_43), %return // CHECK:STDOUT: %.loc20_46: init Outer = converted %.loc20_45.3, %.loc20_45.4 -// CHECK:STDOUT: return %.loc20_46 +// CHECK:STDOUT: return %.loc20_46 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/raw_self.carbon b/toolchain/check/testdata/class/raw_self.carbon index c1246f5730e4..2c2e78519dd2 100644 --- a/toolchain/check/testdata/class/raw_self.carbon +++ b/toolchain/check/testdata/class/raw_self.carbon @@ -113,6 +113,6 @@ fn Class.G[self: Self](r#self: i32) -> (i32, i32) { // CHECK:STDOUT: %.loc18_25.5: init i32 = initialize_from %self.ref.loc18_19 to %.loc18_25.4 // CHECK:STDOUT: %.loc18_25.6: init (i32, i32) = tuple_init (%.loc18_25.3, %.loc18_25.5) to %return // CHECK:STDOUT: %.loc18_26: init (i32, i32) = converted %.loc18_25.1, %.loc18_25.6 -// CHECK:STDOUT: return %.loc18_26 +// CHECK:STDOUT: return %.loc18_26 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/self_type.carbon b/toolchain/check/testdata/class/self_type.carbon index 7d37831d1e29..1e821d83a984 100644 --- a/toolchain/check/testdata/class/self_type.carbon +++ b/toolchain/check/testdata/class/self_type.carbon @@ -94,6 +94,6 @@ fn Class.F[self: Self]() -> i32 { // CHECK:STDOUT: %.loc11_17.4: init Class = class_init (%.loc11_17.3), %s.ref.loc11_5 // CHECK:STDOUT: %.loc11_7: init Class = converted %.loc11_17.1, %.loc11_17.4 // CHECK:STDOUT: assign %s.ref.loc11_5, %.loc11_7 -// CHECK:STDOUT: return %s +// CHECK:STDOUT: return %s to @Class.%return.var.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/expr_category/in_place_tuple_init.carbon b/toolchain/check/testdata/expr_category/in_place_tuple_init.carbon index 5b13e685f7d7..cec0e9cc246a 100644 --- a/toolchain/check/testdata/expr_category/in_place_tuple_init.carbon +++ b/toolchain/check/testdata/expr_category/in_place_tuple_init.carbon @@ -68,7 +68,7 @@ fn H() -> i32 { // CHECK:STDOUT: %F.ref.loc12: = name_ref F, file.%F [template = file.%F] // CHECK:STDOUT: %.loc9: ref (i32, i32) = splice_block %return {} // CHECK:STDOUT: %F.call.loc12: init (i32, i32) = call %F.ref.loc12() to %.loc9 -// CHECK:STDOUT: return %F.call.loc12 +// CHECK:STDOUT: return %F.call.loc12 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() -> i32 { diff --git a/toolchain/check/testdata/global/class_with_fun.carbon b/toolchain/check/testdata/global/class_with_fun.carbon index c9a5c872bbd5..fbf3c62bb9c9 100644 --- a/toolchain/check/testdata/global/class_with_fun.carbon +++ b/toolchain/check/testdata/global/class_with_fun.carbon @@ -49,7 +49,7 @@ var a: A = {}; // CHECK:STDOUT: %.loc9_11.1: {} = struct_literal () // CHECK:STDOUT: %.loc9_11.2: init A = class_init (), %return [template = constants.%.4] // CHECK:STDOUT: %.loc9_12: init A = converted %.loc9_11.1, %.loc9_11.2 [template = constants.%.4] -// CHECK:STDOUT: return %.loc9_12 +// CHECK:STDOUT: return %.loc9_12 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { diff --git a/toolchain/check/testdata/impl/self_in_signature.carbon b/toolchain/check/testdata/impl/self_in_signature.carbon index b1bfb4e01e38..6726ac998943 100644 --- a/toolchain/check/testdata/impl/self_in_signature.carbon +++ b/toolchain/check/testdata/impl/self_in_signature.carbon @@ -236,7 +236,7 @@ impl D as SelfNested { // CHECK:STDOUT: %.loc16_38.1: {} = struct_literal () // CHECK:STDOUT: %.loc16_38.2: init C = class_init (), @impl.1.%return.var [template = constants.%.8] // CHECK:STDOUT: %.loc16_39: init C = converted %.loc16_38.1, %.loc16_38.2 [template = constants.%.8] -// CHECK:STDOUT: return %.loc16_39 +// CHECK:STDOUT: return %.loc16_39 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.3[@impl.2.%self.loc20_8.2: D](@impl.2.%x.loc20_20.2: D) -> @impl.2.%return.var: D { @@ -244,7 +244,7 @@ impl D as SelfNested { // CHECK:STDOUT: %.loc20_47.1: {} = struct_literal () // CHECK:STDOUT: %.loc20_47.2: init D = class_init (), @impl.2.%return.var [template = constants.%.10] // CHECK:STDOUT: %.loc20_48: init D = converted %.loc20_47.1, %.loc20_47.2 [template = constants.%.10] -// CHECK:STDOUT: return %.loc20_48 +// CHECK:STDOUT: return %.loc20_48 to @impl.2.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.4(@SelfNested.%x.loc24_8.2: (Self*, {.x: Self, .y: i32})); diff --git a/toolchain/check/testdata/operators/overloaded/add.carbon b/toolchain/check/testdata/operators/overloaded/add.carbon index a13cb9282899..16302c22c585 100644 --- a/toolchain/check/testdata/operators/overloaded/add.carbon +++ b/toolchain/check/testdata/operators/overloaded/add.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/bit_and.carbon b/toolchain/check/testdata/operators/overloaded/bit_and.carbon index 1eaca5043de7..ff70f251b2bd 100644 --- a/toolchain/check/testdata/operators/overloaded/bit_and.carbon +++ b/toolchain/check/testdata/operators/overloaded/bit_and.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/bit_complement.carbon b/toolchain/check/testdata/operators/overloaded/bit_complement.carbon index 3ade70b3f2aa..dfe989f65cfa 100644 --- a/toolchain/check/testdata/operators/overloaded/bit_complement.carbon +++ b/toolchain/check/testdata/operators/overloaded/bit_complement.carbon @@ -147,7 +147,7 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self]() -> Self; @@ -159,6 +159,6 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: %.loc15_10.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc14: ref C = splice_block %return {} // CHECK:STDOUT: %.loc15_10.2: init C = call %.loc15_10.1(%a.ref) to %.loc14 -// CHECK:STDOUT: return %.loc15_10.2 +// CHECK:STDOUT: return %.loc15_10.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/bit_or.carbon b/toolchain/check/testdata/operators/overloaded/bit_or.carbon index 59c1db1641d6..66372452e24e 100644 --- a/toolchain/check/testdata/operators/overloaded/bit_or.carbon +++ b/toolchain/check/testdata/operators/overloaded/bit_or.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/bit_xor.carbon b/toolchain/check/testdata/operators/overloaded/bit_xor.carbon index 3d70b9aed3ae..6edc89c91be5 100644 --- a/toolchain/check/testdata/operators/overloaded/bit_xor.carbon +++ b/toolchain/check/testdata/operators/overloaded/bit_xor.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/div.carbon b/toolchain/check/testdata/operators/overloaded/div.carbon index d66e8b37a2e8..59b73428d434 100644 --- a/toolchain/check/testdata/operators/overloaded/div.carbon +++ b/toolchain/check/testdata/operators/overloaded/div.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon b/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon index 9aa3ef778562..e605ae8902fc 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon @@ -305,7 +305,7 @@ fn TestRef(b: C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: C = name_ref a, %a // CHECK:STDOUT: %Negate.decl: type = interface_decl @Negate [template = constants.%.4] {} -// CHECK:STDOUT: return +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestBinary(%a: C, %b: C) -> %return: C { @@ -313,7 +313,7 @@ fn TestRef(b: C) { // CHECK:STDOUT: %a.ref: C = name_ref a, %a // CHECK:STDOUT: %b.ref: C = name_ref b, %b // CHECK:STDOUT: %Add.decl: type = interface_decl @Add [template = constants.%.7] {} -// CHECK:STDOUT: return +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestRef(%b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon index 993295461543..d7cdde0837df 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon @@ -281,7 +281,7 @@ fn TestAssign(b: D) { // CHECK:STDOUT: %.loc24_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc24_12.2: ref C = temporary_storage // CHECK:STDOUT: %.loc24_12.3: init C = call %.loc24_12.1() [template = ] -// CHECK:STDOUT: return %.loc24_12.3 +// CHECK:STDOUT: return %.loc24_12.3 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%b: D) { diff --git a/toolchain/check/testdata/operators/overloaded/left_shift.carbon b/toolchain/check/testdata/operators/overloaded/left_shift.carbon index c8c1f9de537a..e06c48b91562 100644 --- a/toolchain/check/testdata/operators/overloaded/left_shift.carbon +++ b/toolchain/check/testdata/operators/overloaded/left_shift.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/mod.carbon b/toolchain/check/testdata/operators/overloaded/mod.carbon index 5452b04e4941..ec138ba9c91b 100644 --- a/toolchain/check/testdata/operators/overloaded/mod.carbon +++ b/toolchain/check/testdata/operators/overloaded/mod.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/mul.carbon b/toolchain/check/testdata/operators/overloaded/mul.carbon index ac7f66fd7208..01a42253a74d 100644 --- a/toolchain/check/testdata/operators/overloaded/mul.carbon +++ b/toolchain/check/testdata/operators/overloaded/mul.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/negate.carbon b/toolchain/check/testdata/operators/overloaded/negate.carbon index bda4d29fc03c..6b41fc008fd4 100644 --- a/toolchain/check/testdata/operators/overloaded/negate.carbon +++ b/toolchain/check/testdata/operators/overloaded/negate.carbon @@ -147,7 +147,7 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self]() -> Self; @@ -159,6 +159,6 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: %.loc15_10.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc14: ref C = splice_block %return {} // CHECK:STDOUT: %.loc15_10.2: init C = call %.loc15_10.1(%a.ref) to %.loc14 -// CHECK:STDOUT: return %.loc15_10.2 +// CHECK:STDOUT: return %.loc15_10.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/right_shift.carbon b/toolchain/check/testdata/operators/overloaded/right_shift.carbon index 278985a8110f..d4bafd0dd737 100644 --- a/toolchain/check/testdata/operators/overloaded/right_shift.carbon +++ b/toolchain/check/testdata/operators/overloaded/right_shift.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/operators/overloaded/sub.carbon b/toolchain/check/testdata/operators/overloaded/sub.carbon index c15b43fcb6cb..dcd56c9fa364 100644 --- a/toolchain/check/testdata/operators/overloaded/sub.carbon +++ b/toolchain/check/testdata/operators/overloaded/sub.carbon @@ -255,7 +255,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc10_13.1: {} = struct_literal () // CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6] // CHECK:STDOUT: %.loc10_14: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6] -// CHECK:STDOUT: return %.loc10_14 +// CHECK:STDOUT: return %.loc10_14 to @impl.1.%return.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; @@ -275,7 +275,7 @@ fn TestAssign(a: C*, b: C) { // CHECK:STDOUT: %.loc18_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc17: ref C = splice_block %return {} // CHECK:STDOUT: %.loc18_12.2: init C = call %.loc18_12.1(%a.ref, %b.ref) to %.loc17 -// CHECK:STDOUT: return %.loc18_12.2 +// CHECK:STDOUT: return %.loc18_12.2 to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAssign(%a: C*, %b: C) { diff --git a/toolchain/check/testdata/return/returned_var.carbon b/toolchain/check/testdata/return/returned_var.carbon index 95369ef4d452..574b328638fd 100644 --- a/toolchain/check/testdata/return/returned_var.carbon +++ b/toolchain/check/testdata/return/returned_var.carbon @@ -74,7 +74,7 @@ fn G() -> i32 { // CHECK:STDOUT: %.loc13_43.6: init C = class_init (%.loc13_43.3, %.loc13_43.5), %return [template = constants.%.6] // CHECK:STDOUT: %.loc13_44: init C = converted %.loc13_43.1, %.loc13_43.6 [template = constants.%.6] // CHECK:STDOUT: assign %return, %.loc13_44 -// CHECK:STDOUT: return %result +// CHECK:STDOUT: return %result to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> i32 { diff --git a/toolchain/check/testdata/return/tuple.carbon b/toolchain/check/testdata/return/tuple.carbon index 6730e580d5a3..758f2edd5e71 100644 --- a/toolchain/check/testdata/return/tuple.carbon +++ b/toolchain/check/testdata/return/tuple.carbon @@ -44,6 +44,6 @@ fn Main() -> (i32, i32) { // CHECK:STDOUT: %.loc9_17.5: init i32 = initialize_from %.loc9_15 to %.loc9_17.4 [template = constants.%.5] // CHECK:STDOUT: %.loc9_17.6: init (i32, i32) = tuple_init (%.loc9_17.3, %.loc9_17.5) to %return [template = constants.%.6] // CHECK:STDOUT: %.loc9_18: init (i32, i32) = converted %.loc9_17.1, %.loc9_17.6 [template = constants.%.6] -// CHECK:STDOUT: return %.loc9_18 +// CHECK:STDOUT: return %.loc9_18 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/struct/reorder_fields.carbon b/toolchain/check/testdata/struct/reorder_fields.carbon index e024bb785e29..945c41bc2515 100644 --- a/toolchain/check/testdata/struct/reorder_fields.carbon +++ b/toolchain/check/testdata/struct/reorder_fields.carbon @@ -77,6 +77,6 @@ fn F() -> {.a: i32, .b: f64} { // CHECK:STDOUT: %.loc13_10.6: init f64 = initialize_from %.loc13_10.4 to %.loc13_10.5 // CHECK:STDOUT: %.loc13_10.7: init {.a: i32, .b: f64} = struct_init (%.loc13_10.3, %.loc13_10.6) to %return // CHECK:STDOUT: %.loc13_11: init {.a: i32, .b: f64} = converted %y.ref, %.loc13_10.7 -// CHECK:STDOUT: return %.loc13_11 +// CHECK:STDOUT: return %.loc13_11 to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index dc1e826379d5..0a4ce9375ec8 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -70,6 +70,7 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { return GetTypeAsValue(); } + // TODO: Add a FunctionValue that FunctionDecl evaluates to, and remove this. auto target = sem_ir().insts().Get(inst_id); if (auto function_decl = target.TryAs()) { return GetFunction(function_decl->function_id); @@ -84,6 +85,23 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { return GetTypeAsValue(); } + auto constant_id = sem_ir().constant_values().Get(inst_id); + if (constant_id.is_constant()) { + if (auto function_decl = sem_ir().insts().TryGetAs( + constant_id.inst_id())) { + return GetFunction(function_decl->function_id); + } + auto* value = globals_.lookup(constant_id.inst_id()); + if (!value) { + // TODO: Less-hacky constant lowering. + FunctionContext ctx(*this, nullptr, vlog_stream_); + ctx.LowerInst(constant_id.inst_id()); + value = ctx.GetValue(constant_id.inst_id()); + globals_.insert({constant_id.inst_id(), value}); + } + return value; + } + CARBON_FATAL() << "Missing value: " << inst_id << " " << target; } diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 3ee422060378..a34136c025f4 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -46,17 +46,25 @@ auto FunctionContext::TryToReuseBlock(SemIR::InstBlockId block_id, } auto FunctionContext::LowerBlock(SemIR::InstBlockId block_id) -> void { - for (const auto& inst_id : sem_ir().inst_blocks().Get(block_id)) { - auto inst = sem_ir().insts().Get(inst_id); - CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n"; - builder_.getInserter().SetCurrentInstId(inst_id); - switch (inst.kind()) { + for (auto inst_id : sem_ir().inst_blocks().Get(block_id)) { + // Skip over constants. `FileContext::GetGlobal` lowers them as needed. + if (sem_ir().constant_values().Get(inst_id).is_constant()) { + continue; + } + LowerInst(inst_id); + } +} + +auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void { + auto inst = sem_ir().insts().Get(inst_id); + CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n"; + builder_.getInserter().SetCurrentInstId(inst_id); + switch (inst.kind()) { #define CARBON_SEM_IR_INST_KIND(Name) \ case SemIR::Name::Kind: \ Handle##Name(*this, inst_id, inst.As()); \ break; #include "toolchain/sem_ir/inst_kind.def" - } } builder_.getInserter().SetCurrentInstId(SemIR::InstId::Invalid); } @@ -90,7 +98,13 @@ auto FunctionContext::FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id, SemIR::InstId source_id) -> void { switch (SemIR::GetInitRepr(sem_ir(), type_id).kind) { case SemIR::InitRepr::None: + break; case SemIR::InitRepr::InPlace: + if (sem_ir().constant_values().Get(source_id).is_constant()) { + // When initializing from a constant, emission of the source doesn't + // initialize the destination. Copy the constant value instead. + CopyValue(type_id, source_id, dest_id); + } break; case SemIR::InitRepr::ByCopy: CopyValue(type_id, source_id, dest_id); @@ -108,24 +122,28 @@ auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, case SemIR::ValueRepr::Copy: builder().CreateStore(GetValue(source_id), GetValue(dest_id)); break; - case SemIR::ValueRepr::Pointer: { - const auto& layout = llvm_module().getDataLayout(); - auto* type = GetType(type_id); - // TODO: Compute known alignment of the source and destination, which may - // be greater than the alignment computed by LLVM. - auto align = layout.getABITypeAlign(type); - - // TODO: Attach !tbaa.struct metadata indicating which portions of the - // type we actually need to copy and which are padding. - builder().CreateMemCpy(GetValue(dest_id), align, GetValue(source_id), - align, layout.getTypeAllocSize(type)); + case SemIR::ValueRepr::Pointer: + CopyObject(type_id, source_id, dest_id); break; - } case SemIR::ValueRepr::Custom: CARBON_FATAL() << "TODO: Add support for CopyValue with custom value rep"; } } +auto FunctionContext::CopyObject(SemIR::TypeId type_id, SemIR::InstId source_id, + SemIR::InstId dest_id) -> void { + const auto& layout = llvm_module().getDataLayout(); + auto* type = GetType(type_id); + // TODO: Compute known alignment of the source and destination, which may + // be greater than the alignment computed by LLVM. + auto align = layout.getABITypeAlign(type); + + // TODO: Attach !tbaa.struct metadata indicating which portions of the + // type we actually need to copy and which are padding. + builder().CreateMemCpy(GetValue(dest_id), align, GetValue(source_id), align, + layout.getTypeAllocSize(type)); +} + auto FunctionContext::Inserter::InsertHelper( llvm::Instruction* inst, const llvm::Twine& name, llvm::BasicBlock* block, llvm::BasicBlock::iterator insert_pt) const -> void { diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index b7824a2e37ff..0f6793a85ff8 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -33,6 +33,9 @@ class FunctionContext { // Builds LLVM IR for the sequence of instructions in `block_id`. auto LowerBlock(SemIR::InstBlockId block_id) -> void; + // Builds LLVM IR for the specified instruction. + auto LowerInst(SemIR::InstId inst_id) -> void; + // Returns a phi node corresponding to the block argument of the given basic // block. auto GetBlockArg(SemIR::InstBlockId block_id, SemIR::TypeId type_id) @@ -129,6 +132,12 @@ class FunctionContext { auto CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, SemIR::InstId dest_id) -> void; + // Emits an object representation copy for type `type_id` from `source_id` to + // `dest_id`. `source_id` and `dest_id` must produce pointers to `type_id` + // objects. + auto CopyObject(SemIR::TypeId type_id, SemIR::InstId source_id, + SemIR::InstId dest_id) -> void; + // Context for the overall lowering process. FileContext* file_context_; diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 345b391a4e8c..1854b21df006 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -595,15 +595,16 @@ auto HandleReturn(FunctionContext& context, SemIR::InstId /*inst_id*/, auto HandleReturnExpr(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::ReturnExpr inst) -> void { - switch ( - SemIR::GetInitRepr(context.sem_ir(), - context.sem_ir().insts().Get(inst.expr_id).type_id()) - .kind) { + auto result_type_id = context.sem_ir().insts().Get(inst.expr_id).type_id(); + switch (SemIR::GetInitRepr(context.sem_ir(), result_type_id).kind) { case SemIR::InitRepr::None: - case SemIR::InitRepr::InPlace: // Nothing to return. context.builder().CreateRetVoid(); return; + case SemIR::InitRepr::InPlace: + context.FinishInit(result_type_id, inst.dest_id, inst.expr_id); + context.builder().CreateRetVoid(); + return; case SemIR::InitRepr::ByCopy: // The expression produces the value representation for the type. context.builder().CreateRet(context.GetValue(inst.expr_id)); diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index 32c11e52459c..d81b4e435609 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -208,18 +208,47 @@ auto EmitAggregateValueRepr(FunctionContext& context, SemIR::TypeId type_id, auto pointee_type_id = context.sem_ir().GetPointeeType(value_rep.type_id); auto* llvm_value_rep_type = context.GetType(pointee_type_id); - // Write the value representation to a local alloca so we can produce a - // pointer to it as the value representation of the struct or tuple. - auto* alloca = - context.builder().CreateAlloca(llvm_value_rep_type, - /*ArraySize=*/nullptr, name); - for (auto [i, ref] : - llvm::enumerate(context.sem_ir().inst_blocks().Get(refs_id))) { - context.builder().CreateStore( - context.GetValue(ref), - context.builder().CreateStructGEP(llvm_value_rep_type, alloca, i)); + auto refs = context.sem_ir().inst_blocks().Get(refs_id); + if (context.builder().GetInsertBlock()) { + // Write the value representation to a local alloca so we can produce a + // pointer to it as the value representation of the struct or tuple. + auto* alloca = + context.builder().CreateAlloca(llvm_value_rep_type, + /*ArraySize=*/nullptr, name); + for (auto [i, ref] : llvm::enumerate(refs)) { + context.builder().CreateStore(context.GetValue(ref), + context.builder().CreateStructGEP( + llvm_value_rep_type, alloca, i)); + } + return alloca; + } else { + // TODO: Move this out to a separate constant lowering file. + llvm::SmallVector elements; + elements.reserve(refs.size()); + for (auto ref : refs) { + auto ref_value_rep = SemIR::GetValueRepr( + context.sem_ir(), context.sem_ir().insts().Get(ref).type_id()); + auto* inner_value = llvm::cast(context.GetValue(ref)); + if (ref_value_rep.kind == SemIR::ValueRepr::Pointer) { + inner_value = + llvm::cast(inner_value)->getInitializer(); + } + elements.push_back(inner_value); + } + llvm::Constant* value; + if (auto* struct_type = + llvm::dyn_cast(llvm_value_rep_type)) { + value = llvm::ConstantStruct::get(struct_type, elements); + } else if (auto* array_type = + llvm::dyn_cast(llvm_value_rep_type)) { + value = llvm::ConstantArray::get(array_type, elements); + } else { + CARBON_FATAL() << "Unknown aggregate value representation"; + } + return new llvm::GlobalVariable( + context.llvm_module(), llvm_value_rep_type, /*isConstant=*/true, + llvm::GlobalVariable::InternalLinkage, value, name); } - return alloca; } case SemIR::ValueRepr::Custom: diff --git a/toolchain/lower/testdata/array/assign_return_value.carbon b/toolchain/lower/testdata/array/assign_return_value.carbon index 2b6b0c8b7d98..376bdd97587e 100644 --- a/toolchain/lower/testdata/array/assign_return_value.carbon +++ b/toolchain/lower/testdata/array/assign_return_value.carbon @@ -13,12 +13,13 @@ fn Run() { // CHECK:STDOUT: ; ModuleID = 'assign_return_value.carbon' // CHECK:STDOUT: source_filename = "assign_return_value.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 24 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc7_38.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %.loc7_38.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc7_38.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 24, ptr %.loc7_38.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -37,3 +38,8 @@ fn Run() { // CHECK:STDOUT: store i32 %.loc10_22.9, ptr %.loc10_22.11.array.index, align 4 // CHECK:STDOUT: ret void // 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: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/array/base.carbon b/toolchain/lower/testdata/array/base.carbon index 009faf0cfd21..94bdaf627a9b 100644 --- a/toolchain/lower/testdata/array/base.carbon +++ b/toolchain/lower/testdata/array/base.carbon @@ -15,29 +15,32 @@ fn Run() { // CHECK:STDOUT: ; ModuleID = 'base.carbon' // CHECK:STDOUT: source_filename = "base.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant [1 x i32] [i32 1] +// CHECK:STDOUT: @tuple.1 = internal constant [2 x double] [double 0x4026333333333334, double 2.200000e+00] +// CHECK:STDOUT: @tuple.2 = internal constant [5 x {}] poison +// CHECK:STDOUT: @tuple.3 = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %a.var = alloca [1 x i32], align 4 // CHECK:STDOUT: %.loc8_24.3.array.index = getelementptr inbounds [1 x i32], ptr %a.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc8_24.3.array.index, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 4, i1 false) // CHECK:STDOUT: %b.var = alloca [2 x double], align 8 // CHECK:STDOUT: %.loc9_32.3.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i32 0 -// CHECK:STDOUT: store double 0x4026333333333334, ptr %.loc9_32.3.array.index, align 8 // CHECK:STDOUT: %.loc9_32.6.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i32 1 -// CHECK:STDOUT: store double 2.200000e+00, ptr %.loc9_32.6.array.index, align 8 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %b.var, ptr align 8 @tuple.1, i64 16, i1 false) // CHECK:STDOUT: %c.var = alloca [5 x {}], align 8 // CHECK:STDOUT: %.loc10_40.3.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 0 // CHECK:STDOUT: %.loc10_40.6.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 1 // CHECK:STDOUT: %.loc10_40.9.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 2 // CHECK:STDOUT: %.loc10_40.12.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 3 // CHECK:STDOUT: %.loc10_40.15.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @tuple.2, i64 0, i1 false) // CHECK:STDOUT: %d.var = alloca { i32, i32, i32 }, align 8 // CHECK:STDOUT: %.loc11_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc11_36.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc11_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc11_36.4.tuple.elem, align 4 // CHECK:STDOUT: %.loc11_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %.loc11_36.6.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %d.var, ptr align 4 @tuple.3, i64 12, i1 false) // CHECK:STDOUT: %e.var = alloca [3 x i32], align 4 // CHECK:STDOUT: %.loc12_21.1.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 0 // CHECK:STDOUT: %.loc12_21.2 = load i32, ptr %.loc12_21.1.tuple.elem, align 4 @@ -53,3 +56,14 @@ fn Run() { // CHECK:STDOUT: store i32 %.loc12_21.12, ptr %.loc12_21.14.array.index, align 4 // CHECK:STDOUT: ret void // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 3 } +// CHECK:STDOUT: uselistorder i32 2, { 0, 1, 3, 4, 2 } +// CHECK:STDOUT: uselistorder i32 3, { 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) } diff --git a/toolchain/lower/testdata/array/function_param.carbon b/toolchain/lower/testdata/array/function_param.carbon index c3642545ba87..d341ca83b6e0 100644 --- a/toolchain/lower/testdata/array/function_param.carbon +++ b/toolchain/lower/testdata/array/function_param.carbon @@ -15,6 +15,8 @@ fn G() -> i32 { // CHECK:STDOUT: ; ModuleID = 'function_param.carbon' // CHECK:STDOUT: source_filename = "function_param.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant [3 x i32] [i32 1, i32 2, i32 3] +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @F(ptr %arr, i32 %i) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc8_15.2.array.index = getelementptr inbounds [3 x i32], ptr %arr, i32 0, i32 %i @@ -26,11 +28,14 @@ fn G() -> i32 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc12_20.2.temp = alloca [3 x i32], align 4 // CHECK:STDOUT: %.loc12_20.4.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc12_20.4.array.index, align 4 // CHECK:STDOUT: %.loc12_20.7.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc12_20.7.array.index, align 4 // CHECK:STDOUT: %.loc12_20.10.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %.loc12_20.10.array.index, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %.loc12_20.2.temp, ptr align 4 @tuple, i64 12, i1 false) // CHECK:STDOUT: %F.call = call i32 @F(ptr %.loc12_20.2.temp, i32 1) // CHECK:STDOUT: ret i32 %F.call // 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: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/basics/numeric_literals.carbon b/toolchain/lower/testdata/basics/numeric_literals.carbon index 8bcd65e8f827..62fa54771fe8 100644 --- a/toolchain/lower/testdata/basics/numeric_literals.carbon +++ b/toolchain/lower/testdata/basics/numeric_literals.carbon @@ -26,29 +26,32 @@ fn F() { // CHECK:STDOUT: ; ModuleID = 'numeric_literals.carbon' // CHECK:STDOUT: source_filename = "numeric_literals.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant [4 x i32] [i32 8, i32 9, i32 8, i32 8] +// CHECK:STDOUT: @tuple.1 = internal constant [6 x double] [double 9.000000e-01, double 8.000000e+00, double 8.000000e+01, double 1.000000e+07, double 1.000000e+08, double 1.000000e-08] +// CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %ints.var = alloca [4 x i32], align 4 // CHECK:STDOUT: %.loc15_3.3.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 8, ptr %.loc15_3.3.array.index, align 4 // CHECK:STDOUT: %.loc15_3.6.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 9, ptr %.loc15_3.6.array.index, align 4 // CHECK:STDOUT: %.loc15_3.9.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 2 -// CHECK:STDOUT: store i32 8, ptr %.loc15_3.9.array.index, align 4 // CHECK:STDOUT: %.loc15_3.12.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 3 -// CHECK:STDOUT: store i32 8, ptr %.loc15_3.12.array.index, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %ints.var, ptr align 4 @tuple, i64 16, i1 false) // CHECK:STDOUT: %floats.var = alloca [6 x double], align 8 // CHECK:STDOUT: %.loc23_3.3.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 0 -// CHECK:STDOUT: store double 9.000000e-01, ptr %.loc23_3.3.array.index, align 8 // CHECK:STDOUT: %.loc23_3.6.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 1 -// CHECK:STDOUT: store double 8.000000e+00, ptr %.loc23_3.6.array.index, align 8 // CHECK:STDOUT: %.loc23_3.9.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 2 -// CHECK:STDOUT: store double 8.000000e+01, ptr %.loc23_3.9.array.index, align 8 // CHECK:STDOUT: %.loc23_3.12.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 3 -// CHECK:STDOUT: store double 1.000000e+07, ptr %.loc23_3.12.array.index, align 8 // CHECK:STDOUT: %.loc23_3.15.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 4 -// CHECK:STDOUT: store double 1.000000e+08, ptr %.loc23_3.15.array.index, align 8 // CHECK:STDOUT: %.loc23_3.18.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 5 -// CHECK:STDOUT: store double 1.000000e-08, ptr %.loc23_3.18.array.index, align 8 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %floats.var, ptr align 8 @tuple.1, i64 48, i1 false) // CHECK:STDOUT: ret void // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/class/adapt.carbon b/toolchain/lower/testdata/class/adapt.carbon index 136b3f2870ba..5ada92ceb281 100644 --- a/toolchain/lower/testdata/class/adapt.carbon +++ b/toolchain/lower/testdata/class/adapt.carbon @@ -50,12 +50,13 @@ fn DoStuff(a: Int) -> Int { // CHECK:STDOUT: ; ModuleID = 'adapt_class.carbon' // CHECK:STDOUT: source_filename = "adapt_class.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @Make(ptr sret({ i32, i32 }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc9_27.2.a = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc9_27.2.a, align 4 // CHECK:STDOUT: %.loc9_27.4.b = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc9_27.4.b, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @struct, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -79,6 +80,14 @@ fn DoStuff(a: Int) -> Int { // CHECK:STDOUT: %.loc28_17 = call i32 @GetB(ptr %pa.var) // CHECK:STDOUT: ret i32 %.loc28_17 // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 2 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } // CHECK:STDOUT: ; ModuleID = 'adapt_int.carbon' // CHECK:STDOUT: source_filename = "adapt_int.carbon" // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/class/base.carbon b/toolchain/lower/testdata/class/base.carbon index 758ea1361037..2186058cd015 100644 --- a/toolchain/lower/testdata/class/base.carbon +++ b/toolchain/lower/testdata/class/base.carbon @@ -29,13 +29,15 @@ fn Convert(p: Derived*) -> Base* { // CHECK:STDOUT: ; ModuleID = 'base.carbon' // CHECK:STDOUT: source_filename = "base.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { i32 } { i32 4 } +// CHECK:STDOUT: @struct.1 = internal constant { { i32 }, i32 } { { i32 } { i32 4 }, i32 7 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @Make(ptr sret({ { i32 }, i32 }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc18_35.2.base = getelementptr inbounds { { i32 }, i32 }, ptr %return, i32 0, i32 0 // CHECK:STDOUT: %.loc18_26.2.b = getelementptr inbounds { i32 }, ptr %.loc18_35.2.base, i32 0, i32 0 -// CHECK:STDOUT: store i32 4, ptr %.loc18_26.2.b, align 4 // CHECK:STDOUT: %.loc18_35.4.d = getelementptr inbounds { { i32 }, i32 }, ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 7, ptr %.loc18_35.4.d, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @struct.1, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -58,3 +60,8 @@ fn Convert(p: Derived*) -> Base* { // CHECK:STDOUT: %.loc26_11.2.base = getelementptr inbounds { { i32 }, i32 }, ptr %p, i32 0, i32 0 // CHECK:STDOUT: ret ptr %.loc26_11.2.base // 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: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/function/call/struct_param.carbon b/toolchain/lower/testdata/function/call/struct_param.carbon index 708730560455..6ec53bc8edc6 100644 --- a/toolchain/lower/testdata/function/call/struct_param.carbon +++ b/toolchain/lower/testdata/function/call/struct_param.carbon @@ -13,6 +13,8 @@ fn Main() { // CHECK:STDOUT: ; ModuleID = 'struct_param.carbon' // CHECK:STDOUT: source_filename = "struct_param.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 2, i32 3 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @F({ i32 } %b, ptr %c) { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void @@ -20,14 +22,6 @@ fn Main() { // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc10_34.2.struct = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %.loc10_34.2 = getelementptr inbounds { i32, i32 }, ptr %.loc10_34.2.struct, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %.loc10_34.2, align 4 -// CHECK:STDOUT: %.loc10_34.21 = getelementptr inbounds { i32, i32 }, ptr %.loc10_34.2.struct, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %.loc10_34.21, align 4 -// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr %.loc10_34.2.struct) +// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr @struct) // CHECK:STDOUT: ret void // CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; uselistorder directives -// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1 } diff --git a/toolchain/lower/testdata/function/call/tuple_param.carbon b/toolchain/lower/testdata/function/call/tuple_param.carbon index 7fc65c0f3e6d..9e00545a3f38 100644 --- a/toolchain/lower/testdata/function/call/tuple_param.carbon +++ b/toolchain/lower/testdata/function/call/tuple_param.carbon @@ -13,6 +13,8 @@ fn Main() { // CHECK:STDOUT: ; ModuleID = 'tuple_param.carbon' // CHECK:STDOUT: source_filename = "tuple_param.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 2, i32 3 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @F({ i32 } %b, ptr %c) { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void @@ -20,14 +22,6 @@ fn Main() { // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc10_20.2.tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %.loc10_20.2 = getelementptr inbounds { i32, i32 }, ptr %.loc10_20.2.tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %.loc10_20.2, align 4 -// CHECK:STDOUT: %.loc10_20.21 = getelementptr inbounds { i32, i32 }, ptr %.loc10_20.2.tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %.loc10_20.21, align 4 -// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr %.loc10_20.2.tuple) +// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr @tuple) // CHECK:STDOUT: ret void // CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; uselistorder directives -// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1 } diff --git a/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon b/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon index eb13a0dfb0cd..8dcfa28e5983 100644 --- a/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon +++ b/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon @@ -15,6 +15,8 @@ fn Main() { // CHECK:STDOUT: ; ModuleID = 'tuple_param_with_return_slot.carbon' // CHECK:STDOUT: source_filename = "tuple_param_with_return_slot.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 2, i32 3 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @F(ptr sret({ i32, i32, i32 }) %return, { i32 } %b, ptr %c) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc8_14.tuple.index = extractvalue { i32 } %b, 0 @@ -34,14 +36,9 @@ fn Main() { // CHECK:STDOUT: define void @Main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc12_4.1.temp = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %.loc12_20.2.tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %.loc12_20.2 = getelementptr inbounds { i32, i32 }, ptr %.loc12_20.2.tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %.loc12_20.2, align 4 -// CHECK:STDOUT: %.loc12_20.21 = getelementptr inbounds { i32, i32 }, ptr %.loc12_20.2.tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %.loc12_20.21, align 4 -// CHECK:STDOUT: call void @F(ptr %.loc12_4.1.temp, { i32 } { i32 1 }, ptr %.loc12_20.2.tuple) +// CHECK:STDOUT: call void @F(ptr %.loc12_4.1.temp, { i32 } { i32 1 }, ptr @tuple) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives -// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1, 3, 4, 5 } +// CHECK:STDOUT: uselistorder i32 2, { 1, 0 } diff --git a/toolchain/lower/testdata/index/array_element_access.carbon b/toolchain/lower/testdata/index/array_element_access.carbon index 9d0b940c7b3a..c05358b3f855 100644 --- a/toolchain/lower/testdata/index/array_element_access.carbon +++ b/toolchain/lower/testdata/index/array_element_access.carbon @@ -17,21 +17,22 @@ fn Run() { // CHECK:STDOUT: ; ModuleID = 'array_element_access.carbon' // CHECK:STDOUT: source_filename = "array_element_access.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: @tuple.1 = internal constant [2 x i32] [i32 1, i32 2] +// CHECK:STDOUT: // CHECK:STDOUT: define void @A(ptr sret({ i32, i32 }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc6_36.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc6_36.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc6_36.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc6_36.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @B(ptr sret([2 x i32]) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc8_34.3.array.index = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc8_34.3.array.index, align 4 // CHECK:STDOUT: %.loc8_34.6.array.index = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc8_34.6.array.index, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple.1, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -67,3 +68,12 @@ fn Run() { // CHECK:STDOUT: store i32 %.loc14_21.2, ptr %d.var, align 4 // CHECK:STDOUT: ret void // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/index/tuple_element_access.carbon b/toolchain/lower/testdata/index/tuple_element_access.carbon index 873755e6ad0a..d303730bff85 100644 --- a/toolchain/lower/testdata/index/tuple_element_access.carbon +++ b/toolchain/lower/testdata/index/tuple_element_access.carbon @@ -14,15 +14,15 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'tuple_element_access.carbon' // CHECK:STDOUT: source_filename = "tuple_element_access.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32, i32 } { i32 0, i32 1, i32 2 } +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %a.var = alloca { i32, i32, i32 }, align 8 // CHECK:STDOUT: %.loc8_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 0, ptr %.loc8_36.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc8_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 1, ptr %.loc8_36.4.tuple.elem, align 4 // CHECK:STDOUT: %.loc8_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 2 -// CHECK:STDOUT: store i32 2, ptr %.loc8_36.6.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 12, i1 false) // CHECK:STDOUT: %b.var = alloca i32, align 4 // CHECK:STDOUT: %.loc9_19.1.tuple.index = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0 // CHECK:STDOUT: %.loc9_19.2 = load i32, ptr %.loc9_19.1.tuple.index, align 4 @@ -33,3 +33,13 @@ fn Run() -> i32 { // CHECK:STDOUT: store i32 %.loc10_19.2, ptr %c.var, align 4 // CHECK:STDOUT: ret i32 0 // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 0, { 0, 1, 2, 3, 5, 6, 7, 8, 4 } +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 4, 2 } +// CHECK:STDOUT: uselistorder i32 2, { 0, 2, 1 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/index/tuple_return_value_access.carbon b/toolchain/lower/testdata/index/tuple_return_value_access.carbon index ad0c10cf6ffb..a957ad7da30f 100644 --- a/toolchain/lower/testdata/index/tuple_return_value_access.carbon +++ b/toolchain/lower/testdata/index/tuple_return_value_access.carbon @@ -13,12 +13,13 @@ fn Run() { // CHECK:STDOUT: ; ModuleID = 'tuple_return_value_access.carbon' // CHECK:STDOUT: source_filename = "tuple_return_value_access.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 24 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc7_38.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %.loc7_38.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc7_38.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 -// CHECK:STDOUT: store i32 24, ptr %.loc7_38.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -32,3 +33,8 @@ fn Run() { // CHECK:STDOUT: store i32 %.loc10_21.2, ptr %t.var, align 4 // CHECK:STDOUT: ret void // 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: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/let/tuple.carbon b/toolchain/lower/testdata/let/tuple.carbon index bb7d347c498e..e39ac3f2b3d2 100644 --- a/toolchain/lower/testdata/let/tuple.carbon +++ b/toolchain/lower/testdata/let/tuple.carbon @@ -14,20 +14,20 @@ fn F() -> i32 { // CHECK:STDOUT: ; ModuleID = 'tuple.carbon' // CHECK:STDOUT: source_filename = "tuple.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 } +// CHECK:STDOUT: @tuple.1 = internal constant { i32, i32 } { i32 4, i32 5 } +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @F() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %a.var = alloca { i32, i32, i32 }, align 8 // CHECK:STDOUT: %.loc8_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc8_36.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc8_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc8_36.4.tuple.elem, align 4 // CHECK:STDOUT: %.loc8_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %.loc8_36.6.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 12, i1 false) // CHECK:STDOUT: %b.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc9_28.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 4, ptr %.loc9_28.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc9_28.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 5, ptr %.loc9_28.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.1, i64 8, i1 false) // CHECK:STDOUT: %.loc10_43.1.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0 // CHECK:STDOUT: %.loc10_43.2 = load i32, ptr %.loc10_43.1.tuple.elem, align 4 // CHECK:STDOUT: %.loc10_43.3.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1 @@ -61,3 +61,13 @@ fn F() -> i32 { // CHECK:STDOUT: %.loc11_16.tuple.index.load = load i32, ptr %.loc11_16.tuple.index, align 4 // CHECK:STDOUT: ret i32 %.loc11_16.tuple.index.load // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 12 } +// CHECK:STDOUT: uselistorder i32 2, { 0, 1, 3, 2 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/operators/assignment.carbon b/toolchain/lower/testdata/operators/assignment.carbon index 7c8129282ac0..00ef8a52b6a6 100644 --- a/toolchain/lower/testdata/operators/assignment.carbon +++ b/toolchain/lower/testdata/operators/assignment.carbon @@ -14,6 +14,8 @@ fn Main() { // CHECK:STDOUT: ; ModuleID = 'assignment.carbon' // CHECK:STDOUT: source_filename = "assignment.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %a.var = alloca i32, align 4 @@ -21,8 +23,15 @@ fn Main() { // CHECK:STDOUT: store i32 9, ptr %a.var, align 4 // CHECK:STDOUT: %b.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc11_12.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc11_12.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc11_12.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc11_12.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple, i64 8, i1 false) // CHECK:STDOUT: ret void // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 1, 2, 3, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/pointer/address_of_field.carbon b/toolchain/lower/testdata/pointer/address_of_field.carbon index 8926f21603e9..56a3d6a0a5f7 100644 --- a/toolchain/lower/testdata/pointer/address_of_field.carbon +++ b/toolchain/lower/testdata/pointer/address_of_field.carbon @@ -14,16 +14,25 @@ fn F() { // CHECK:STDOUT: ; ModuleID = 'address_of_field.carbon' // CHECK:STDOUT: source_filename = "address_of_field.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: // CHECK:STDOUT: declare void @G(ptr) // CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %s.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc10_46.2.a = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc10_46.2.a, align 4 // CHECK:STDOUT: %.loc10_46.4.b = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc10_46.4.b, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %s.var, ptr align 4 @struct, i64 8, i1 false) // CHECK:STDOUT: %.loc11_7.b = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 1 // CHECK:STDOUT: call void @G(ptr %.loc11_7.b) // CHECK:STDOUT: ret void // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 2, 3, 1 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/return/return_var.carbon b/toolchain/lower/testdata/return/return_var.carbon index 4e2b03eeb122..cd87e964810d 100644 --- a/toolchain/lower/testdata/return/return_var.carbon +++ b/toolchain/lower/testdata/return/return_var.carbon @@ -21,7 +21,6 @@ fn Make() -> C { // CHECK:STDOUT: define void @Make(ptr sret({ i32, ptr }) %return) { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc14_30.2.data = getelementptr inbounds { i32, ptr }, ptr %return, i32 0, i32 0 -// CHECK:STDOUT: store i32 42, ptr %.loc14_30.2.data, align 4 // CHECK:STDOUT: %.loc14_30.4.next = getelementptr inbounds { i32, ptr }, ptr %return, i32 0, i32 1 // CHECK:STDOUT: store ptr %return, ptr %.loc14_30.4.next, align 8 // CHECK:STDOUT: ret void diff --git a/toolchain/lower/testdata/struct/member_access.carbon b/toolchain/lower/testdata/struct/member_access.carbon index 29ade7a6f9f3..ce10f5f734d8 100644 --- a/toolchain/lower/testdata/struct/member_access.carbon +++ b/toolchain/lower/testdata/struct/member_access.carbon @@ -14,13 +14,14 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'member_access.carbon' // CHECK:STDOUT: source_filename = "member_access.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { double, i32 } { double 0.000000e+00, i32 1 } +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %x.var = alloca { double, i32 }, align 8 // CHECK:STDOUT: %.loc8_48.2.a = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 0 -// CHECK:STDOUT: store double 0.000000e+00, ptr %.loc8_48.2.a, align 8 // CHECK:STDOUT: %.loc8_48.4.b = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 1, ptr %.loc8_48.4.b, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x.var, ptr align 8 @struct, i64 16, i1 false) // CHECK:STDOUT: %y.var = alloca i32, align 4 // CHECK:STDOUT: %.loc9_17.1.b = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 1 // CHECK:STDOUT: %.loc9_17.2 = load i32, ptr %.loc9_17.1.b, align 4 @@ -30,3 +31,11 @@ fn Run() -> i32 { // CHECK:STDOUT: store i32 %.loc10, ptr %z.var, align 4 // CHECK:STDOUT: ret i32 0 // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 3 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/struct/two_entries.carbon b/toolchain/lower/testdata/struct/two_entries.carbon index cf930967b56b..b2c3b934a4cb 100644 --- a/toolchain/lower/testdata/struct/two_entries.carbon +++ b/toolchain/lower/testdata/struct/two_entries.carbon @@ -13,13 +13,14 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon' // CHECK:STDOUT: source_filename = "two_entries.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %x.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc8_46.2.a = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %.loc8_46.2.a, align 4 // CHECK:STDOUT: %.loc8_46.4.b = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %.loc8_46.4.b, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @struct, i64 8, i1 false) // CHECK:STDOUT: %y.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc9_31.1.a = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0 // CHECK:STDOUT: %.loc9_31.2 = load i32, ptr %.loc9_31.1.a, align 4 @@ -31,3 +32,11 @@ fn Run() -> i32 { // CHECK:STDOUT: store i32 %.loc9_31.6, ptr %.loc9_31.7.b, align 4 // CHECK:STDOUT: ret i32 0 // 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: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 3 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/tuple/two_entries.carbon b/toolchain/lower/testdata/tuple/two_entries.carbon index e8c89c2bdc17..0e60ee96f491 100644 --- a/toolchain/lower/testdata/tuple/two_entries.carbon +++ b/toolchain/lower/testdata/tuple/two_entries.carbon @@ -13,13 +13,14 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon' // CHECK:STDOUT: source_filename = "two_entries.carbon" // CHECK:STDOUT: +// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 7 } +// CHECK:STDOUT: // CHECK:STDOUT: define i32 @main() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %x.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc8_29.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %.loc8_29.2.tuple.elem, align 4 // CHECK:STDOUT: %.loc8_29.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 1 -// CHECK:STDOUT: store i32 7, ptr %.loc8_29.4.tuple.elem, align 4 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @tuple, i64 8, i1 false) // CHECK:STDOUT: %y.var = alloca { i32, i32 }, align 8 // CHECK:STDOUT: %.loc9_23.1.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0 // CHECK:STDOUT: %.loc9_23.2 = load i32, ptr %.loc9_23.1.tuple.elem, align 4 @@ -31,3 +32,8 @@ fn Run() -> i32 { // CHECK:STDOUT: store i32 %.loc9_23.6, ptr %.loc9_23.7.tuple.elem, align 4 // CHECK:STDOUT: ret i32 0 // 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: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 8498c86d5efb..8dc28ed34a32 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -577,6 +577,13 @@ class Formatter { FormatReturnSlot(inst.dest_id); } + auto FormatInstructionRHS(ReturnExpr ret) -> void { + FormatArgs(ret.expr_id); + if (ret.dest_id.is_valid()) { + FormatReturnSlot(ret.dest_id); + } + } + auto FormatInstructionRHS(StructInit init) -> void { FormatArgs(init.elements_id); FormatReturnSlot(init.dest_id); diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 15a31df9f4a1..c5a2b8d89a24 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -690,6 +690,8 @@ struct ReturnExpr { // This is a statement, so has no type. InstId expr_id; + // The return slot, if any. Invalid if we're not returning through memory. + InstId dest_id; }; struct SpliceBlock {