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 {