From bb2f4d3cbcc52ecf4e6fc6b63af2db56548a362c Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 2 Oct 2026 14:43:26 +0800 Subject: [PATCH 1/2] ssa: emit static itabs and box constant iface data Known concrete-to-interface conversions emit a read-only _llgo_itab$ global (Inter, Type, Hash, Fun) with weak_odr linkage, matching cmd/compile's go:itab symbols. Ordinary builds use that global as the runtime vtable. The T2I site is the only reference, so --gc-sections/-dead_strip can drop itabs that belong to dead functions. LTO and deadcode-drop keep calling NewItab so unused interface methods can be dropped; Fun[] would pin them. LTO still emits the static template for de-virt and erases unused templates after the plugin runs. Non-direct interface values (integers, bools, small aggregates) still use IfaceIndir, matching Go: data is a pointer to a copy. Compile-time constants go in a WeakODR _llgo_ifacebox$ global instead of AllocU, like cmd/compile's static temps. Box names hash the constant's bits, not LLVM's printed form, so same-length strings in different packages do not collide under Windows COMDAT. Repeated boxing of the same LLVM value reuses a per-package pointer cache. Zero-sized constants stay AllocU so boxing does not mutate the shared sentinel. Together, a constant T2I is {itab, box} with no runtime call or heap allocation. Hash is copied from the type descriptor when present. interequal compares the (inter, _type) pair so a static itab and a dynamically allocated itab for the same conversion compare equal. --- cl/_testdata/vargs/in.go | 4 +- cl/_testgo/abimethod/in.go | 56 +-- cl/_testgo/cgocfiles/cgocfiles.go | 3 +- cl/_testgo/closureall/in.go | 4 +- cl/_testgo/defer3/in.go | 4 +- cl/_testgo/defer4/in.go | 4 +- cl/_testgo/defer5/in.go | 15 +- cl/_testgo/deferiface/in.go | 4 +- cl/_testgo/genericembediface/in.go | 9 +- cl/_testgo/ifaceconv/in.go | 13 +- cl/_testgo/ifaceprom/in.go | 5 +- cl/_testgo/interface1370/in.go | 4 +- cl/_testgo/invoke/in.go | 28 +- cl/_testgo/reflectconv/in.go | 5 +- cl/_testgo/struczero/in.go | 27 +- cl/_testgo/tpinst/main.go | 8 +- cl/_testgo/tprecur/in.go | 5 +- .../globaldce_static_itab_devirt/in.go | 7 +- .../globaldce_static_itab_partial_root/in.go | 1 + .../interface_anyonmous/meta-expect.txt | 9 +- .../interface_exported_var/meta-expect.txt | 13 +- .../interface_generic/meta-expect.txt | 8 +- .../meta-expect.txt | 12 +- .../interface_imported/meta-expect.txt | 8 +- cl/_testmeta/interface_named/meta-expect.txt | 8 +- .../interface_unexported/meta-expect.txt | 8 +- .../methodinfo_imported/meta-expect.txt | 8 +- .../typechildren_basic/meta-expect.txt | 2 +- cl/_testrt/abinamed/in.go | 8 +- cl/_testrt/closureiface/in.go | 5 +- cl/_testrt/eface/in.go | 62 +-- cl/_testrt/float2any/in.go | 20 +- cl/_testrt/funcdecl/in.go | 12 +- cl/_testrt/mapclosure/in.go | 4 +- cl/_testrt/methodthunk/in.go | 61 ++- cl/_testrt/reflectclosureenv/in.go | 2 +- cl/_testrt/staticifacebox/in.go | 42 ++ cl/_testrt/staticitab/in.go | 35 ++ cl/_testrt/tpabi/in.go | 4 +- cl/_testrt/tpmap/in.go | 70 ++-- cl/_testrt/typed/in.go | 21 +- cl/cgo_test.go | 10 +- runtime/internal/runtime/alg.go | 10 +- runtime/internal/runtime/z_face.go | 12 + ssa/interface.go | 213 ++++++++-- ssa/package.go | 7 + ssa/ssa_test.go | 385 +++++++++++++++++- 47 files changed, 880 insertions(+), 385 deletions(-) create mode 100644 cl/_testrt/staticifacebox/in.go create mode 100644 cl/_testrt/staticitab/in.go diff --git a/cl/_testdata/vargs/in.go b/cl/_testdata/vargs/in.go index a806dc9a77..bb003061b0 100644 --- a/cl/_testdata/vargs/in.go +++ b/cl/_testdata/vargs/in.go @@ -24,9 +24,7 @@ func test(a ...any) { // assertion-to-C-varargs data flow. Bounds-loop details are owned elsewhere. // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK: [[ARGS:%[0-9]+]] = call ptr @"{{.*}}AllocZ"(i64 48) -// CHECK: [[BOX:%[0-9]+]] = call ptr @"{{.*}}AllocU"(i64 8) -// CHECK-NEXT: store i64 1, ptr [[BOX]] -// CHECK-NEXT: [[EFACE:%[0-9]+]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int, ptr undef }, ptr [[BOX]], 1 +// CHECK: store %"{{.*}}eface" { ptr @_llgo_int, ptr @"_llgo_ifacebox${{.*}}" }, ptr {{%[0-9]+}} // CHECK: [[SLICE_LEN:%[0-9]+]] = insertvalue %"{{.*}}Slice" {{.*}}, i64 3, 1 // CHECK-NEXT: [[SLICE:%[0-9]+]] = insertvalue %"{{.*}}Slice" [[SLICE_LEN]], i64 3, 2 // CHECK-NEXT: call void @main.test(%"{{.*}}Slice" [[SLICE]]) diff --git a/cl/_testgo/abimethod/in.go b/cl/_testgo/abimethod/in.go index 76b42d6eaa..0723378c5b 100644 --- a/cl/_testgo/abimethod/in.go +++ b/cl/_testgo/abimethod/in.go @@ -232,9 +232,7 @@ type I2 interface { // Pointer-to-anonymous-struct embedding *T: form I, dispatch Demo1, and test its result. // CHECK-LABEL: define void @main.testAnonymous1(){{.*}} { -// CHECK: [[A1_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK: [[A1_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A1_ITAB]], 0 -// CHECK: [[A1_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A1_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A1_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK: [[A1_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A1_IFACE]]) // CHECK: [[A1_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A1_IFACE]], 0 // CHECK-NEXT: [[A1_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A1_TABLE]], i64 3 @@ -249,9 +247,7 @@ type I2 interface { // Value anonymous struct embedding *T uses the value descriptor, but the same promoted method. // CHECK-LABEL: define void @main.testAnonymous2(){{.*}} { -// CHECK: [[A2_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A2_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A2_ITAB]], 0 -// CHECK-NEXT: [[A2_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A2_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A2_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A2_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A2_IFACE]]) // CHECK-NEXT: [[A2_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A2_IFACE]], 0 // CHECK-NEXT: [[A2_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A2_TABLE]], i64 3 @@ -266,9 +262,7 @@ type I2 interface { // Value and pointer anonymous structs embedding T select distinct concrete descriptors. // CHECK-LABEL: define void @main.testAnonymous3(){{.*}} { -// CHECK: [[A3_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A3_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A3_ITAB]], 0 -// CHECK-NEXT: [[A3_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A3_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A3_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A3_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A3_IFACE]]) // CHECK-NEXT: [[A3_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A3_IFACE]], 0 // CHECK-NEXT: [[A3_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A3_TABLE]], i64 3 @@ -282,9 +276,7 @@ type I2 interface { // CHECK-NEXT: br i1 [[A3_BAD]], // CHECK-LABEL: define void @main.testAnonymous4(){{.*}} { -// CHECK: [[A4_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A4_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A4_ITAB]], 0 -// CHECK-NEXT: [[A4_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A4_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A4_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A4_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A4_IFACE]]) // CHECK-NEXT: [[A4_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A4_IFACE]], 0 // CHECK-NEXT: [[A4_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A4_TABLE]], i64 3 @@ -299,9 +291,7 @@ type I2 interface { // Demo2 requires the pointer method set, for both embedding T and embedding *T. // CHECK-LABEL: define void @main.testAnonymous5(){{.*}} { -// CHECK: [[A5_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A5_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A5_ITAB]], 0 -// CHECK-NEXT: [[A5_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A5_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A5_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A5_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A5_IFACE]]) // CHECK-NEXT: [[A5_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A5_IFACE]], 0 // CHECK-NEXT: [[A5_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A5_TABLE]], i64 3 @@ -315,9 +305,7 @@ type I2 interface { // CHECK-NEXT: br i1 [[A5_BAD]], // CHECK-LABEL: define void @main.testAnonymous6(){{.*}} { -// CHECK: [[A6_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A6_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A6_ITAB]], 0 -// CHECK-NEXT: [[A6_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A6_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A6_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A6_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A6_IFACE]]) // CHECK-NEXT: [[A6_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A6_IFACE]], 0 // CHECK-NEXT: [[A6_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[A6_TABLE]], i64 3 @@ -332,9 +320,7 @@ type I2 interface { // A two-method anonymous interface must dispatch two different itab slots on one interface value. // CHECK-LABEL: define void @main.testAnonymous7(){{.*}} { -// CHECK: [[A7_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A7_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A7_ITAB]], 0 -// CHECK-NEXT: [[A7_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A7_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A7_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A7_DATA1:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A7_IFACE]]) // CHECK-NEXT: [[A7_TAB1:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A7_IFACE]], 0 // CHECK-NEXT: [[A7_SLOT1:%[0-9]+]] = getelementptr ptr, ptr [[A7_TAB1]], i64 3 @@ -360,9 +346,7 @@ type I2 interface { // The package-local interface adds the unexported demo3 slot after Demo1 and Demo2. // CHECK-LABEL: define void @main.testAnonymous8(){{.*}} { -// CHECK: [[A8_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/abimethod.iface${{[-A-Za-z0-9_]+}}", ptr @"{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[A8_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[A8_ITAB]], 0 -// CHECK-NEXT: [[A8_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[A8_I0]], ptr %{{[0-9]+}}, 1 +// CHECK: [[A8_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK-NEXT: [[A8_DATA1:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[A8_IFACE]]) // CHECK-NEXT: [[A8_TAB1:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[A8_IFACE]], 0 // CHECK-NEXT: [[A8_SLOT1:%[0-9]+]] = getelementptr ptr, ptr [[A8_TAB1]], i64 3 @@ -403,9 +387,7 @@ type I2 interface { // CHECK: [[BUF_FIELD:%[0-9]+]] = getelementptr inbounds nuw { i64, ptr }, ptr [[BUF_OWNER]], i32 0, i32 1 // CHECK-NEXT: [[BUF:%[0-9]+]] = call ptr @bytes.NewBufferString( // CHECK: store ptr [[BUF]], ptr [[BUF_FIELD]] -// CHECK-NEXT: [[BUF_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*{{.*}}/abimethod.struct${{[-A-Za-z0-9_]+}}") -// CHECK-NEXT: [[BUF_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[BUF_ITAB]], 0 -// CHECK-NEXT: [[BUF_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[BUF_I0]], ptr [[BUF_OWNER]], 1 +// CHECK-NEXT: [[BUF_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[BUF_OWNER]], 1 // CHECK-NEXT: [[BUF_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[BUF_IFACE]]) // CHECK-NEXT: [[BUF_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[BUF_IFACE]], 0 // CHECK-NEXT: [[BUF_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[BUF_TABLE]], i64 3 @@ -422,9 +404,7 @@ type I2 interface { // IP.Store and IP.Load must operate on the same Pointer[any] interface value. // CHECK-LABEL: define void @main.testGeneric(){{.*}} { // CHECK: [[P_OBJECT:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: [[P_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.Pointer{{\[.*\]}}") -// CHECK: [[P_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[P_ITAB]], 0 -// CHECK-NEXT: [[P_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[P_I0]], ptr [[P_OBJECT]], 1 +// CHECK-NEXT: [[P_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[P_OBJECT]], 1 // CHECK: [[P_VALUE:%[0-9]+]] = call ptr @"main.testGeneric$1"() // CHECK-NEXT: [[P_SDATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[P_IFACE]]) // CHECK-NEXT: [[P_STAB:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[P_IFACE]], 0 @@ -453,9 +433,7 @@ type I2 interface { // CHECK: [[P_BAD:%[0-9]+]] = icmp ne i64 [[P_INT]], 100 // CHECK-LABEL: define ptr @"main.testGeneric$1"(){{.*}} { -// CHECK: store i64 100, ptr [[P_BOX:%[0-9]+]] -// CHECK: [[P_EFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr undef }, ptr [[P_BOX]], 1 -// CHECK: store %"{{.*}}/runtime/internal/runtime.eface" [[P_EFACE]], ptr [[P_RESULT:%[0-9]+]] +// CHECK: store %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr @"_llgo_ifacebox${{.*}}" }, ptr [[P_RESULT:%[0-9]+]] // CHECK: ret ptr [[P_RESULT]] // Named T and *T select the proper descriptor and dispatch through the requested interface method. @@ -463,9 +441,7 @@ type I2 interface { // CHECK: [[N1_OBJECT:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) // CHECK: [[N1_FIELD:%[0-9]+]] = getelementptr inbounds nuw %main.T, ptr [[N1_OBJECT]], i32 0, i32 0 // CHECK-NEXT: store i64 100, ptr [[N1_FIELD]] -// CHECK-NEXT: [[N1_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T") -// CHECK-NEXT: [[N1_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[N1_ITAB]], 0 -// CHECK-NEXT: [[N1_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[N1_I0]], ptr [[N1_OBJECT]], 1 +// CHECK-NEXT: [[N1_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[N1_OBJECT]], 1 // CHECK-NEXT: [[N1_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[N1_IFACE]]) // CHECK-NEXT: [[N1_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[N1_IFACE]], 0 // CHECK-NEXT: [[N1_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[N1_TABLE]], i64 3 @@ -482,9 +458,7 @@ type I2 interface { // CHECK: [[N2_VALUE:%[0-9]+]] = load %main.T, ptr %{{[0-9]+}} // CHECK-NEXT: [[N2_OBJECT:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) // CHECK-NEXT: store %main.T [[N2_VALUE]], ptr [[N2_OBJECT]] -// CHECK-NEXT: [[N2_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T) -// CHECK-NEXT: [[N2_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[N2_ITAB]], 0 -// CHECK-NEXT: [[N2_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[N2_I0]], ptr [[N2_OBJECT]], 1 +// CHECK-NEXT: [[N2_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[N2_OBJECT]], 1 // CHECK-NEXT: [[N2_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[N2_IFACE]]) // CHECK-NEXT: [[N2_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[N2_IFACE]], 0 // CHECK-NEXT: [[N2_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[N2_TABLE]], i64 3 @@ -501,9 +475,7 @@ type I2 interface { // CHECK: [[N3_OBJECT:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) // CHECK: [[N3_FIELD:%[0-9]+]] = getelementptr inbounds nuw %main.T, ptr [[N3_OBJECT]], i32 0, i32 0 // CHECK-NEXT: store i64 100, ptr [[N3_FIELD]] -// CHECK-NEXT: [[N3_ITAB:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T") -// CHECK-NEXT: [[N3_I0:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr [[N3_ITAB]], 0 -// CHECK-NEXT: [[N3_IFACE:%[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" [[N3_I0]], ptr [[N3_OBJECT]], 1 +// CHECK-NEXT: [[N3_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[N3_OBJECT]], 1 // CHECK-NEXT: [[N3_DATA:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" [[N3_IFACE]]) // CHECK-NEXT: [[N3_TABLE:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" [[N3_IFACE]], 0 // CHECK-NEXT: [[N3_SLOT:%[0-9]+]] = getelementptr ptr, ptr [[N3_TABLE]], i64 3 diff --git a/cl/_testgo/cgocfiles/cgocfiles.go b/cl/_testgo/cgocfiles/cgocfiles.go index 93d23540bd..67805283cf 100644 --- a/cl/_testgo/cgocfiles/cgocfiles.go +++ b/cl/_testgo/cgocfiles/cgocfiles.go @@ -21,8 +21,7 @@ var checkErrno = func(error) {} // CHECK: [[HAS_ERR:%[0-9]+]] = icmp ne i32 [[ERRNO]], 0 // CHECK: [[ERRNO_VALUE:%[0-9]+]] = sext i32 [[ERRNO]] to i64 // CHECK: store i64 [[ERRNO_VALUE]], ptr [[ERRNO_BOX:%[0-9]+]] -// CHECK: [[ERRNO_ITAB:%[0-9]+]] = call ptr @"{{.*}}NewItab"(ptr {{.*}}, ptr @_llgo_syscall.Errno) -// CHECK: [[ERR_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" {{.*}}, ptr [[ERRNO_BOX]], 1 +// CHECK: [[ERR_IFACE:%[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[ERRNO_BOX]], 1 // CHECK: br i1 [[HAS_ERR]], label %[[ERR_BLOCK:[^, ]+]], label %[[OK_BLOCK:[^, ]+]] // CHECK: [[ERR_PAIR:%[0-9]+]] = insertvalue { i32, %"{{.*}}iface" } {{.*}}, %"{{.*}}iface" [[ERR_IFACE]], 1 // CHECK-NEXT: ret { i32, %"{{.*}}iface" } [[ERR_PAIR]] diff --git a/cl/_testgo/closureall/in.go b/cl/_testgo/closureall/in.go index 88ca7560a1..095a7ec0a3 100644 --- a/cl/_testgo/closureall/in.go +++ b/cl/_testgo/closureall/in.go @@ -124,9 +124,7 @@ func makeWithFree(base int) Fn { // A method expression uses the receiver as an ordinary first argument. // CHECK: call i64 @"main.(*S).Add$thunk"(ptr [[S]], i64 8) // The interface method value keeps the same interface payload and checks it is non-nil. -// CHECK: [[ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr {{.*}}, ptr @"*_llgo_main.S") -// CHECK: [[IFACE_ITAB:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[ITAB]], 0 -// CHECK: [[IFACE:%.*]] = insertvalue %"{{.*}}iface" [[IFACE_ITAB]], ptr [[S]], 1 +// CHECK: [[IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[S]], 1 // CHECK: [[IFACE_TYPE:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType"(%"{{.*}}iface" [[IFACE]]) // CHECK: [[IFACE_OK:%.*]] = icmp ne ptr [[IFACE_TYPE]], null // CHECK: br i1 [[IFACE_OK]], label %{{.*}}, label %{{.*}} diff --git a/cl/_testgo/defer3/in.go b/cl/_testgo/defer3/in.go index 44611925dc..0016cb2d64 100644 --- a/cl/_testgo/defer3/in.go +++ b/cl/_testgo/defer3/in.go @@ -20,9 +20,7 @@ package main // CHECK: store ptr [[D3_FAIL_OLD_HEAD]], ptr %{{[0-9]+}} // CHECK: store %"{{.*}}String" { ptr @[[D3_BYE]], i64 3 }, ptr %{{[0-9]+}} // CHECK-NEXT: store ptr [[D3_FAIL_NODE]], ptr [[D3_FAIL_HEAD]] -// CHECK: store %"{{.*}}String" { ptr @[[D3_PANIC]], i64 13 }, ptr [[D3_PANIC_BOX:%[0-9]+]] -// CHECK-NEXT: [[D3_PANIC_EFACE:%[0-9]+]] = insertvalue %"{{.*}}eface" { ptr @_llgo_string, ptr undef }, ptr [[D3_PANIC_BOX]], 1 -// CHECK-NEXT: call void @"{{.*}}Panic"(%"{{.*}}eface" [[D3_PANIC_EFACE]]) +// CHECK: call void @"{{.*}}Panic"(%"{{.*}}eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK: [[D3_RUN_NODE:%[0-9]+]] = load ptr, ptr [[D3_FAIL_HEAD]] // CHECK: [[D3_RUN_PAYLOAD:%[0-9]+]] = extractvalue { ptr, i64, %"{{.*}}String" } %{{[0-9]+}}, 2 // CHECK-NEXT: call void @"{{.*}}FreeDeferNode"(ptr [[D3_RUN_NODE]]) diff --git a/cl/_testgo/defer4/in.go b/cl/_testgo/defer4/in.go index bd0ed5c26b..07a0325638 100644 --- a/cl/_testgo/defer4/in.go +++ b/cl/_testgo/defer4/in.go @@ -23,9 +23,7 @@ package main // CHECK: store ptr [[D4_FAIL_OLD_HEAD]], ptr %{{[0-9]+}} // CHECK: store %"{{.*}}String" { ptr @[[D4_BYE]], i64 3 }, ptr %{{[0-9]+}} // CHECK-NEXT: store ptr [[D4_FAIL_NODE]], ptr [[D4_FAIL_HEAD]] -// CHECK: store %"{{.*}}String" { ptr @[[D4_PANIC]], i64 13 }, ptr [[D4_PANIC_BOX:%[0-9]+]] -// CHECK-NEXT: [[D4_PANIC_EFACE:%[0-9]+]] = insertvalue %"{{.*}}eface" { ptr @_llgo_string, ptr undef }, ptr [[D4_PANIC_BOX]], 1 -// CHECK-NEXT: call void @"{{.*}}Panic"(%"{{.*}}eface" [[D4_PANIC_EFACE]]) +// CHECK: call void @"{{.*}}Panic"(%"{{.*}}eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK: [[D4_PENDING_NODE:%[0-9]+]] = load ptr, ptr [[D4_FAIL_HEAD]] // CHECK-NEXT: [[D4_HAS_NODE:%[0-9]+]] = icmp ne ptr [[D4_PENDING_NODE]], null // CHECK-NEXT: br i1 [[D4_HAS_NODE]], label %{{.*}}, label %{{.*}} diff --git a/cl/_testgo/defer5/in.go b/cl/_testgo/defer5/in.go index 2bd324a945..1861d2f80e 100644 --- a/cl/_testgo/defer5/in.go +++ b/cl/_testgo/defer5/in.go @@ -54,10 +54,7 @@ func main() { // CHECK-NEXT: [[DEFER_B_ARG:%[0-9]+]] = getelementptr inbounds nuw { ptr, i64, %"{{.*}}.String" }, ptr [[DEFER_B_NODE]], i32 0, i32 2 // CHECK-NEXT: store %"{{.*}}.String" { ptr [[DEFER_B]], i64 1 }, ptr [[DEFER_B_ARG]] // CHECK-NEXT: store ptr [[DEFER_B_NODE]], ptr [[DEFER_HEAD_FIELD]] - // CHECK: [[MAIN_PANIC_BOX:%[0-9]+]] = call ptr @"{{.*}}.AllocU"(i64 16) - // CHECK-NEXT: store %"{{.*}}.String" { ptr [[PANIC_MAIN]], i64 13 }, ptr [[MAIN_PANIC_BOX]] - // CHECK-NEXT: [[MAIN_PANIC_VALUE:%[0-9]+]] = insertvalue %"{{.*}}.eface" { ptr @_llgo_string, ptr undef }, ptr [[MAIN_PANIC_BOX]], 1 - // CHECK-NEXT: call void @"{{.*}}.Panic"(%"{{.*}}.eface" [[MAIN_PANIC_VALUE]]) + // CHECK: call void @"{{.*}}.Panic"(%"{{.*}}.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // The state machine invokes defer 2 first, then enters a recover frame for // defer 1. Capturing block labels keeps the relation without pinning their @@ -119,13 +116,7 @@ func main() { // CHECK-NEXT: [[RECOVER_NONEMPTY:%[0-9]+]] = xor i1 [[RECOVER_EMPTY]], true // CHECK-NEXT: br i1 [[RECOVER_NONEMPTY]], label %{{.*}}, label %{{.*}} // CHECK: call void @"{{.*}}.PrintString"(%"{{.*}}.String" { ptr [[PRINT_DEFER1]], i64 10 }) -// CHECK: [[DEFER1_PANIC_BOX:%[0-9]+]] = call ptr @"{{.*}}.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}.String" { ptr [[PANIC_DEFER1]], i64 16 }, ptr [[DEFER1_PANIC_BOX]] -// CHECK-NEXT: [[DEFER1_PANIC:%[0-9]+]] = insertvalue %"{{.*}}.eface" { ptr @_llgo_string, ptr undef }, ptr [[DEFER1_PANIC_BOX]], 1 -// CHECK-NEXT: call void @"{{.*}}.Panic"(%"{{.*}}.eface" [[DEFER1_PANIC]]) +// CHECK: call void @"{{.*}}.Panic"(%"{{.*}}.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-LABEL: define void @"main.main$2"(){{.*}} { // CHECK: call void @"{{.*}}.PrintString"(%"{{.*}}.String" { ptr [[PRINT_DEFER2]], i64 10 }) -// CHECK: [[DEFER2_PANIC_BOX:%[0-9]+]] = call ptr @"{{.*}}.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}.String" { ptr [[PANIC_DEFER2]], i64 16 }, ptr [[DEFER2_PANIC_BOX]] -// CHECK-NEXT: [[DEFER2_PANIC:%[0-9]+]] = insertvalue %"{{.*}}.eface" { ptr @_llgo_string, ptr undef }, ptr [[DEFER2_PANIC_BOX]], 1 -// CHECK-NEXT: call void @"{{.*}}.Panic"(%"{{.*}}.eface" [[DEFER2_PANIC]]) +// CHECK: call void @"{{.*}}.Panic"(%"{{.*}}.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) diff --git a/cl/_testgo/deferiface/in.go b/cl/_testgo/deferiface/in.go index f5b2f81371..9524c7d12f 100644 --- a/cl/_testgo/deferiface/in.go +++ b/cl/_testgo/deferiface/in.go @@ -31,9 +31,7 @@ func main() { // CHECK: [[ITEM:%.*]] = call ptr @"{{.*}}AllocZ"(i64 8) // CHECK-NEXT: [[ITEM_VALUE:%.*]] = getelementptr inbounds nuw %main.item, ptr [[ITEM]], i32 0, i32 0 // CHECK-NEXT: store i64 42, ptr [[ITEM_VALUE]] -// CHECK: [[ITEM_ITAB:%.*]] = call ptr @"{{.*}}NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.item") -// CHECK-NEXT: [[ITEM_IFACE0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[ITEM_ITAB]], 0 -// CHECK-NEXT: [[ITEM_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[ITEM_IFACE0]], ptr [[ITEM]], 1 +// CHECK: [[ITEM_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[ITEM]], 1 // CHECK-NEXT: call void @main.run(%"{{.*}}iface" [[ITEM_IFACE]]) // run resolves Reset once, stores its code+receiver pair as the defer payload, diff --git a/cl/_testgo/genericembediface/in.go b/cl/_testgo/genericembediface/in.go index f57032f77a..f1b49e0cea 100644 --- a/cl/_testgo/genericembediface/in.go +++ b/cl/_testgo/genericembediface/in.go @@ -49,9 +49,7 @@ func main() { // CHECK: [[H_GENERIC_STREAM:%.*]] = call ptr @"{{.*}}AllocZ"(i64 16) // CHECK: [[H_STREAM_FIELD:%.*]] = getelementptr inbounds nuw %"{{.*}}GenericServerStream[main.Request,main.Response]", ptr [[H_GENERIC_STREAM]], i32 0, i32 0 // CHECK: store %"{{.*}}iface" %1, ptr [[H_STREAM_FIELD]] -// CHECK: [[H_STREAM_ITAB:%.*]] = call ptr @"{{.*}}NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_{{.*}}GenericServerStream[main.Request,main.Response]") -// CHECK: [[H_STREAM_IFACE0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[H_STREAM_ITAB]], 0 -// CHECK: [[H_STREAM_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[H_STREAM_IFACE0]], ptr [[H_GENERIC_STREAM]], 1 +// CHECK: [[H_STREAM_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[H_GENERIC_STREAM]], 1 // CHECK: [[H_SERVER_DATA:%.*]] = call ptr @"{{.*}}IfacePtrData"(%"{{.*}}iface" [[H_REF_IFACE]]) // CHECK: [[H_SERVER_ITAB:%.*]] = extractvalue %"{{.*}}iface" [[H_REF_IFACE]], 0 // CHECK: [[H_METHOD_SLOT:%.*]] = getelementptr ptr, ptr [[H_SERVER_ITAB]], i64 3 @@ -66,11 +64,10 @@ func main() { // CHECK-NEXT: unreachable // main supplies the value implementations of ReflectionServer and ServerStream. +// Zero-sized constants stay AllocU: boxing them would mutate the shared sentinel. // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK: [[M_SERVER:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_main.server, ptr undef }, ptr {{%.*}}, 1 -// CHECK: [[M_STREAM_ITAB:%.*]] = call ptr @"{{.*}}NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.stream) -// CHECK: [[M_STREAM0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[M_STREAM_ITAB]], 0 -// CHECK: [[M_STREAM:%.*]] = insertvalue %"{{.*}}iface" [[M_STREAM0]], ptr {{%.*}}, 1 +// CHECK: [[M_STREAM:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr {{%.*}}, 1 // CHECK: call %"{{.*}}iface" @main.handler(%"{{.*}}eface" [[M_SERVER]], %"{{.*}}iface" [[M_STREAM]]) // CHECK-LABEL: define %"{{.*}}iface" @main.server.ServerReflectionInfo(%main.server %0, %"{{.*}}iface" %1){{.*}} { diff --git a/cl/_testgo/ifaceconv/in.go b/cl/_testgo/ifaceconv/in.go index 20d365a3d8..7a5e701fe3 100644 --- a/cl/_testgo/ifaceconv/in.go +++ b/cl/_testgo/ifaceconv/in.go @@ -127,11 +127,10 @@ func main() { // CHECK: [[NIL_DYNAMIC_TYPE:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType"(%"{{.*}}iface" zeroinitializer) // CHECK: [[NIL_ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/cl/_testgo/ifaceconv.iface${{[-A-Za-z0-9_]+}}", ptr [[NIL_DYNAMIC_TYPE]]) // C1 implements I0 and I1, but its dynamic type is tested against I2. +// Zero-sized constants stay AllocU: boxing them would mutate the shared sentinel. // CHECK: [[C1_DATA:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 0) // CHECK: store %main.C1 zeroinitializer, ptr [[C1_DATA]] -// CHECK: [[C1_ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/cl/_testgo/ifaceconv.iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.C1) -// CHECK: [[C1_IFACE_0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[C1_ITAB]], 0 -// CHECK: [[C1_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[C1_IFACE_0]], ptr [[C1_DATA]], 1 +// CHECK: [[C1_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[C1_DATA]], 1 // CHECK: [[C1_TYPE:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType"(%"{{.*}}iface" [[C1_IFACE]]) // CHECK: [[C1_IS_I0:%.*]] = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.I0, ptr [[C1_TYPE]]) // CHECK: br i1 [[C1_IS_I0]], label %{{.*}}, label %{{.*}} @@ -144,9 +143,7 @@ func main() { // C2 follows the same assertion pipeline, including its positive I2 test. // CHECK: [[C2_DATA:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 0) // CHECK: store %main.C2 zeroinitializer, ptr [[C2_DATA]] -// CHECK: [[C2_ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/cl/_testgo/ifaceconv.iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.C2) -// CHECK: [[C2_IFACE_0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[C2_ITAB]], 0 -// CHECK: [[C2_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[C2_IFACE_0]], ptr [[C2_DATA]], 1 +// CHECK: [[C2_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[C2_DATA]], 1 // CHECK: [[C2_TYPE:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType"(%"{{.*}}iface" [[C2_IFACE]]) // CHECK: [[C2_IS_I0:%.*]] = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.I0, ptr [[C2_TYPE]]) // CHECK: br i1 [[C2_IS_I0]], label %{{.*}}, label %{{.*}} @@ -157,9 +154,7 @@ func main() { // CHECK: [[C2_IS_I2:%.*]] = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.I2, ptr [[C2_I2_TYPE]]) // CHECK: br i1 [[C2_IS_I2]], label %{{.*}}, label %{{.*}} // The final conversions rebuild an eface from the same C1 payload before nil comparison. -// CHECK: [[FINAL_C1_ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/cl/_testgo/ifaceconv.iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.C1) -// CHECK: [[FINAL_C1_IFACE_0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[FINAL_C1_ITAB]], 0 -// CHECK: [[FINAL_C1_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[FINAL_C1_IFACE_0]], ptr [[FINAL_C1_DATA:%.*]], 1 +// CHECK: [[FINAL_C1_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[FINAL_C1_DATA:%.*]], 1 // CHECK: [[FINAL_C1_TYPE:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType"(%"{{.*}}iface" [[FINAL_C1_IFACE]]) // CHECK: [[FINAL_C1_PAYLOAD:%.*]] = extractvalue %"{{.*}}iface" [[FINAL_C1_IFACE]], 1 // CHECK: [[FINAL_EFACE_0:%.*]] = insertvalue %"{{.*}}eface" undef, ptr [[FINAL_C1_TYPE]], 0 diff --git a/cl/_testgo/ifaceprom/in.go b/cl/_testgo/ifaceprom/in.go index 9e43ff0d6b..1df329ea04 100644 --- a/cl/_testgo/ifaceprom/in.go +++ b/cl/_testgo/ifaceprom/in.go @@ -111,11 +111,10 @@ func main() { // CHECK: ret %"{{.*}}String" [[IMPL_TWO_RESULT]] // CHECK-LABEL: define void @main.main(){{.*}} { +// Zero-sized constants stay AllocU: boxing them would mutate the shared sentinel. // CHECK: [[IMPL_DATA:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 0) // CHECK: store %main.impl zeroinitializer, ptr [[IMPL_DATA]] -// CHECK: [[IMPL_ITAB:%.*]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"{{.*}}/cl/_testgo/ifaceprom.iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.impl) -// CHECK: [[MAIN_IFACE_0:%.*]] = insertvalue %"{{.*}}iface" undef, ptr [[IMPL_ITAB]], 0 -// CHECK: [[MAIN_IFACE:%.*]] = insertvalue %"{{.*}}iface" [[MAIN_IFACE_0]], ptr [[IMPL_DATA]], 1 +// CHECK: [[MAIN_IFACE:%.*]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr [[IMPL_DATA]], 1 // CHECK: store %"{{.*}}iface" [[MAIN_IFACE]], ptr [[S_IFACE_FIELD:%.*]] // Direct s.I.one uses slot 3 and compares that call result with 1. // CHECK: [[DIRECT_ONE_IFACE:%.*]] = load %"{{.*}}iface", ptr %{{.*}} diff --git a/cl/_testgo/interface1370/in.go b/cl/_testgo/interface1370/in.go index c3fba9fa50..bcde821a86 100644 --- a/cl/_testgo/interface1370/in.go +++ b/cl/_testgo/interface1370/in.go @@ -7,9 +7,7 @@ import ( // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK: %[[RECT:[0-9]+]] = call ptr @"{{.*}}geometry1370.NewRectangle"(double 5.000000e+00, double 3.000000e+00) -// CHECK: %[[ITAB:[0-9]+]] = call ptr @"{{.*}}NewItab"(ptr @"{{.*}}geometry1370.iface{{.*}}", ptr @"*_llgo_{{.*}}geometry1370.Rectangle") -// CHECK: %[[IFACE_TYPE:[0-9]+]] = insertvalue %"{{.*}}iface" undef, ptr %[[ITAB]], 0 -// CHECK: %[[RECT_IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" %[[IFACE_TYPE]], ptr %[[RECT]], 1 +// CHECK: %[[RECT_IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %[[RECT]], 1 // CHECK: call void @"{{.*}}geometry1370.RegisterShape"(%"{{.*}}iface" %[[RECT_IFACE]], i64 42) // CHECK: %[[ID:[0-9]+]] = call i64 @"{{.*}}geometry1370.(*Rectangle).GetID"(ptr %[[RECT]]) // CHECK: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %[[ID]]) diff --git a/cl/_testgo/invoke/in.go b/cl/_testgo/invoke/in.go index cbb0e9e151..11641639a8 100644 --- a/cl/_testgo/invoke/in.go +++ b/cl/_testgo/invoke/in.go @@ -223,22 +223,20 @@ type M interface { // remaining matrix. // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK: store %main.T6 { ptr @"main.main$1", ptr null }, ptr %{{[0-9]+}} -// CHECK: %[[T_VALUE_ITAB:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T) -// CHECK: %[[T_VALUE_IF0:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %[[T_VALUE_ITAB]], 0 -// CHECK: %[[T_VALUE_IFACE:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %[[T_VALUE_IF0]], ptr %{{[0-9]+}}, 1 +// CHECK: %[[T_VALUE_IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %{{[0-9]+}}, 1 // CHECK: call void @main.invoke(%"{{.*}}/runtime/internal/runtime.iface" %[[T_VALUE_IFACE]]) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T1) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T1") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T2) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T2") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T3") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T4) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T4") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T5) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T5") -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @_llgo_main.T6) -// CHECK: call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.T6") +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } // CHECK: %[[ASSERT_TYPE:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfaceType" // CHECK: %[[ASSERT_ITAB:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr %[[ASSERT_TYPE]]) diff --git a/cl/_testgo/reflectconv/in.go b/cl/_testgo/reflectconv/in.go index 26cf74b7a2..4de9e3b632 100644 --- a/cl/_testgo/reflectconv/in.go +++ b/cl/_testgo/reflectconv/in.go @@ -32,10 +32,7 @@ type namedFunc func(int) int // CHECK-NEXT: [[CAPTURED_EFACE:%[0-9]+]] = insertvalue %"{{.*}}eface" { ptr @{{.*}}, ptr undef }, ptr [[CAPTURED_BOX]], 1 // CHECK-NEXT: [[CAPTURED_VALUE:%[0-9]+]] = call %reflect.Value @reflect.ValueOf(%"{{.*}}eface" [[CAPTURED_EFACE]]) // CHECK-NEXT: call ptr @reflect.Value.UnsafePointer(%reflect.Value [[CAPTURED_VALUE]]) -// CHECK: [[DECLARED_BOX:%[0-9]+]] = call ptr @"{{.*}}AllocU"(i64 16) -// CHECK-NEXT: store { ptr, ptr } { ptr @main.numericConversions, ptr null }, ptr [[DECLARED_BOX]] -// CHECK-NEXT: [[DECLARED_EFACE:%[0-9]+]] = insertvalue %"{{.*}}eface" { ptr @{{.*}}, ptr undef }, ptr [[DECLARED_BOX]], 1 -// CHECK-NEXT: [[DECLARED_VALUE:%[0-9]+]] = call %reflect.Value @reflect.ValueOf(%"{{.*}}eface" [[DECLARED_EFACE]]) +// CHECK: [[DECLARED_VALUE:%[0-9]+]] = call %reflect.Value @reflect.ValueOf(%"{{.*}}eface" { ptr @{{.*}}, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: call ptr @reflect.Value.UnsafePointer(%reflect.Value [[DECLARED_VALUE]]) // CHECK-LABEL: define void @main.main(){{.*}} { diff --git a/cl/_testgo/struczero/in.go b/cl/_testgo/struczero/in.go index 99b857cf3b..b6e6989fa4 100644 --- a/cl/_testgo/struczero/in.go +++ b/cl/_testgo/struczero/in.go @@ -107,23 +107,20 @@ func main() { // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP9]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %[[TMP0]], i8 0, i64 16, i1 false) -// CHECK-NEXT: %[[TMP11:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/cl/_testdata/foo.Foo" zeroinitializer, ptr %[[TMP11]], align 8 -// CHECK-NEXT: %[[TMP12:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"_llgo_{{.*}}/cl/_testdata/foo.Foo", ptr undef }, ptr %[[TMP11]], 1 -// CHECK-NEXT: %[[TMP13:[0-9]+]] = call { %"{{.*}}/cl/_testdata/foo.Foo", i1 } @main.Bar(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP12]]) -// CHECK-NEXT: %[[TMP14:[0-9]+]] = extractvalue { %"{{.*}}/cl/_testdata/foo.Foo", i1 } %[[TMP13]], 0 -// CHECK-NEXT: store %"{{.*}}/cl/_testdata/foo.Foo" %[[TMP14]], ptr %[[TMP0]], align 8 -// CHECK-NEXT: %[[TMP15:[0-9]+]] = extractvalue { %"{{.*}}/cl/_testdata/foo.Foo", i1 } %[[TMP13]], 1 -// CHECK-NEXT: %[[TMP16:[0-9]+]] = load %"{{.*}}/cl/_testdata/foo.Foo", ptr %[[TMP0]], align 8 -// CHECK-NEXT: %[[TMP17:[0-9]+]] = call ptr @"{{.*}}/cl/_testdata/foo.Foo.Pb"(%"{{.*}}/cl/_testdata/foo.Foo" %[[TMP16]]) -// CHECK-NEXT: %[[TMP18:[0-9]+]] = getelementptr inbounds nuw %"{{.*}}/cl/_testdata/foo.Foo", ptr %[[TMP0]], i32 0, i32 1 -// CHECK-NEXT: %[[TMP19:[0-9]+]] = load float, ptr %[[TMP18]], align 4 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP17]]) +// CHECK-NEXT: %[[TMP11:[0-9]+]] = call { %"{{.*}}/cl/_testdata/foo.Foo", i1 } @main.Bar(%"{{.*}}/runtime/internal/runtime.eface" { ptr @"_llgo_{{.*}}/cl/_testdata/foo.Foo", ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK-NEXT: %[[TMP12:[0-9]+]] = extractvalue { %"{{.*}}/cl/_testdata/foo.Foo", i1 } %[[TMP11]], 0 +// CHECK-NEXT: store %"{{.*}}/cl/_testdata/foo.Foo" %[[TMP12]], ptr %[[TMP0]], align 8 +// CHECK-NEXT: %[[TMP13:[0-9]+]] = extractvalue { %"{{.*}}/cl/_testdata/foo.Foo", i1 } %[[TMP11]], 1 +// CHECK-NEXT: %[[TMP14:[0-9]+]] = load %"{{.*}}/cl/_testdata/foo.Foo", ptr %[[TMP0]], align 8 +// CHECK-NEXT: %[[TMP15:[0-9]+]] = call ptr @"{{.*}}/cl/_testdata/foo.Foo.Pb"(%"{{.*}}/cl/_testdata/foo.Foo" %[[TMP14]]) +// CHECK-NEXT: %[[TMP16:[0-9]+]] = getelementptr inbounds nuw %"{{.*}}/cl/_testdata/foo.Foo", ptr %[[TMP0]], i32 0, i32 1 +// CHECK-NEXT: %[[TMP17:[0-9]+]] = load float, ptr %[[TMP16]], align 4 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP15]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: %[[TMP20:[0-9]+]] = fpext float %[[TMP19]] to double -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintFloat"(double %[[TMP20]]) +// CHECK-NEXT: %[[TMP18:[0-9]+]] = fpext float %[[TMP17]] to double +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintFloat"(double %[[TMP18]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP15]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP13]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testgo/tpinst/main.go b/cl/_testgo/tpinst/main.go index 6a1e2f8214..f17ef067d2 100644 --- a/cl/_testgo/tpinst/main.go +++ b/cl/_testgo/tpinst/main.go @@ -41,9 +41,7 @@ func (pt *M[T]) value() T { // CHECK-LABEL: define void @main.demo(){{.*}} { // CHECK: %[[INT_OBJ:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK: %[[INT_ITAB:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.M[int]") -// CHECK: %[[INT_IFACE0:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %[[INT_ITAB]], 0 -// CHECK: %[[INT_IFACE:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %[[INT_IFACE0]], ptr %[[INT_OBJ]], 1 +// CHECK: %[[INT_IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %[[INT_OBJ]], 1 // CHECK: %[[INT_DATA:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %[[INT_IFACE]]) // CHECK: %[[INT_IFACE_TYPE:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %[[INT_IFACE]], 0 // CHECK: %[[INT_METHOD_SLOT:[0-9]+]] = getelementptr ptr, ptr %[[INT_IFACE_TYPE]], i64 3 @@ -56,9 +54,7 @@ func (pt *M[T]) value() T { // CHECK: %[[INT_BAD:[0-9]+]] = icmp ne i64 %[[INT_RESULT]], 100 // CHECK: br i1 %[[INT_BAD]] // CHECK: %[[FLOAT_OBJ:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK: %[[FLOAT_ITAB:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.M[float64]") -// CHECK: %[[FLOAT_IFACE0:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %[[FLOAT_ITAB]], 0 -// CHECK: %[[FLOAT_IFACE:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %[[FLOAT_IFACE0]], ptr %[[FLOAT_OBJ]], 1 +// CHECK: %[[FLOAT_IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %[[FLOAT_OBJ]], 1 // CHECK: %[[FLOAT_DATA:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %[[FLOAT_IFACE]]) // CHECK: %[[FLOAT_IFACE_TYPE:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %[[FLOAT_IFACE]], 0 // CHECK: getelementptr ptr, ptr %[[FLOAT_IFACE_TYPE]], i64 3 diff --git a/cl/_testgo/tprecur/in.go b/cl/_testgo/tprecur/in.go index 6e40db319a..a1b88d09ab 100644 --- a/cl/_testgo/tprecur/in.go +++ b/cl/_testgo/tprecur/in.go @@ -56,10 +56,7 @@ func recur2[T Integer](n T) T { // CHECK-NEXT: br i1 %[[TMP1]], label %_llgo_[[BB1:[0-9]+]], label %_llgo_[[BB2:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB1]]: -// CHECK-NEXT: %[[TMP2:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB0]], i64 5 }, ptr %[[TMP2]], align 8 -// CHECK-NEXT: %[[TMP3:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP2]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP3]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB2]]: diff --git a/cl/_testlto/globaldce_static_itab_devirt/in.go b/cl/_testlto/globaldce_static_itab_devirt/in.go index f21bddcd19..b6b279367c 100644 --- a/cl/_testlto/globaldce_static_itab_devirt/in.go +++ b/cl/_testlto/globaldce_static_itab_devirt/in.go @@ -5,6 +5,7 @@ import "reflect" // SYMBOL-DAG: main{{.*}}A{{.*}}M // SYMBOL-NOT: main{{.*}}B{{.*}}M +// After LTO, callM is proven to use A only, so the static itab is unused and dropped. // SYMBOL-NOT: _llgo_itab type I interface { @@ -47,8 +48,8 @@ func main() { // signature-wide method capability would retain B.M through that descriptor. ok := keepType(B{}) direct := I(A{}) - // Interface equality relies on canonical runtime itab identity. The static - // template is analysis-only; the direct conversion must still agree with an - // interface assembled through reflection. + // LTO T2I still calls NewItab; the static itab is only a de-virt template + // and is dropped after the plugin. Equality with a reflection-built + // interface still holds via the (inter, _type) pair if tab pointers differ. println(callM(direct), direct == reflectedI(A{}), ok) } diff --git a/cl/_testlto/globaldce_static_itab_partial_root/in.go b/cl/_testlto/globaldce_static_itab_partial_root/in.go index d001359adc..28effe309e 100644 --- a/cl/_testlto/globaldce_static_itab_partial_root/in.go +++ b/cl/_testlto/globaldce_static_itab_partial_root/in.go @@ -6,6 +6,7 @@ import "reflect" // SYMBOL-DAG: main{{.*}}A{{.*}}M // SYMBOL-DAG: main{{.*}}B{{.*}}M // SYMBOL-DAG: main{{.*}}C{{.*}}M +// LTO still emits NewItab for mixed static/dynamic roots; the static template is DCE'd. // SYMBOL-NOT: _llgo_itab type I interface { diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt index 1c2b6ba37c..36d9a9577f 100644 --- a/cl/_testmeta/interface_anyonmous/meta-expect.txt +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -13,6 +13,11 @@ _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: github.com/xgo-dev/llgo/runtime/internal/runtime.interequal _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_itab$dACDlmxu21tSXRxXNpIHvwgX0x95CkEbRwSczvSHkCQ: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + _llgo_main.T + main.(*T).M + main.(*T).N _llgo_main.T: *_llgo_main.T github.com/xgo-dev/llgo/runtime/internal/runtime.memequal0 @@ -27,10 +32,8 @@ main.(*T).N: main.init: main.init$guard main.main: - _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo - _llgo_main.T + _llgo_itab$dACDlmxu21tSXRxXNpIHvwgX0x95CkEbRwSczvSHkCQ github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab main.use main.use: github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index 6c157adcbf..971b1d002c 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -119,6 +119,16 @@ _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_itab$vggDeT47dwPKM_RcBnGpsdpFFhF_4t_0ENclFcOmnik: + _llgo_encoding/binary.littleEndian + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + encoding/binary.(*littleEndian).PutUint16 + encoding/binary.(*littleEndian).PutUint32 + encoding/binary.(*littleEndian).PutUint64 + encoding/binary.(*littleEndian).String + encoding/binary.(*littleEndian).Uint16 + encoding/binary.(*littleEndian).Uint32 + encoding/binary.(*littleEndian).Uint64 _llgo_string: github.com/xgo-dev/llgo/runtime/internal/runtime.strequal _llgo_uint16: @@ -134,12 +144,11 @@ main.init: main.init$guard main.main: _llgo_encoding/binary.littleEndian - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + _llgo_itab$vggDeT47dwPKM_RcBnGpsdpFFhF_4t_0ENclFcOmnik encoding/binary.LittleEndian github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU github.com/xgo-dev/llgo/runtime/internal/runtime.AllocZ github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab github.com/xgo-dev/llgo/runtime/internal/runtime.Typedmemmove [UseIface] diff --git a/cl/_testmeta/interface_generic/meta-expect.txt b/cl/_testmeta/interface_generic/meta-expect.txt index 8faadd118b..b50ac9bda0 100644 --- a/cl/_testmeta/interface_generic/meta-expect.txt +++ b/cl/_testmeta/interface_generic/meta-expect.txt @@ -22,6 +22,10 @@ _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA _llgo_int: github.com/xgo-dev/llgo/runtime/internal/runtime.memequal64 +_llgo_itab$NaWLg_FrV5MHzdWJlwBZcXuNDhDi5hcF1S0TD-W3lj8: + *_llgo_main.Box[int] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + main.(*Box[int]).Value _llgo_main.Box[int]: *_llgo_main.Box[int] github.com/xgo-dev/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields @@ -31,10 +35,8 @@ github.com/xgo-dev/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k main.init: main.init$guard main.main: - *_llgo_main.Box[int] - _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + _llgo_itab$NaWLg_FrV5MHzdWJlwBZcXuNDhDi5hcF1S0TD-W3lj8 github.com/xgo-dev/llgo/runtime/internal/runtime.AllocZ - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab main.useInt main.useInt: github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData diff --git a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt index e28b46dd22..88c9ff4b46 100644 --- a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt +++ b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt @@ -44,15 +44,19 @@ _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA _llgo_int: github.com/xgo-dev/llgo/runtime/internal/runtime.memequal64 +_llgo_itab$L6GQSFD-CkxSQ2Op7Cyz91O-GPspzTl0qt0pCN0TtXM: + *_llgo_github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value _llgo_string: github.com/xgo-dev/llgo/runtime/internal/runtime.strequal github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + _llgo_ifacebox$IJDXgz_lfkIFQYqLmlLj5-_6-noFxwI0D9Jm3Mm4-rA _llgo_string - github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU github.com/xgo-dev/llgo/runtime/internal/runtime.Panic github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + _llgo_ifacebox$IJDXgz_lfkIFQYqLmlLj5-_6-noFxwI0D9Jm3Mm4-rA _llgo_string - github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU github.com/xgo-dev/llgo/runtime/internal/runtime.Panic github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$XNpHe-3-A25gBmHjYdF74H8M4k3Eik680huOzY6OtaU$fields: _llgo_string @@ -63,14 +67,12 @@ main.init: github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.init main.init$guard main.main: - *_llgo_github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] *_llgo_github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] - _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + _llgo_itab$L6GQSFD-CkxSQ2Op7Cyz91O-GPspzTl0qt0pCN0TtXM github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewIntBox github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBox github.com/xgo-dev/llgo/cl/_testmeta/interface_generic_crosspkg/model.UseStringBox - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab github.com/xgo-dev/llgo/runtime/internal/runtime.PrintByte github.com/xgo-dev/llgo/runtime/internal/runtime.PrintInt main.sink diff --git a/cl/_testmeta/interface_imported/meta-expect.txt b/cl/_testmeta/interface_imported/meta-expect.txt index 37ad744255..db4dd78801 100644 --- a/cl/_testmeta/interface_imported/meta-expect.txt +++ b/cl/_testmeta/interface_imported/meta-expect.txt @@ -51,6 +51,10 @@ _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_int: github.com/xgo-dev/llgo/runtime/internal/runtime.memequal64 +_llgo_itab$ARsbupbM5LJaIg5IQKIp4jqIVyeW-KWKFWg1yvba33A: + _llgo_github.com/xgo-dev/llgo/cl/_testmeta/interface_imported/api.Source + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + github.com/xgo-dev/llgo/cl/_testmeta/interface_imported/api.(*Source).Read _llgo_string: github.com/xgo-dev/llgo/runtime/internal/runtime.strequal _llgo_uint8: @@ -59,10 +63,8 @@ main.init: github.com/xgo-dev/llgo/cl/_testmeta/interface_imported/api.init main.init$guard main.main: - _llgo_github.com/xgo-dev/llgo/cl/_testmeta/interface_imported/api.Source - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + _llgo_itab$ARsbupbM5LJaIg5IQKIp4jqIVyeW-KWKFWg1yvba33A github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab main.use main.use: github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index c8cabf3d31..f986c02584 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -13,6 +13,10 @@ _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: github.com/xgo-dev/llgo/runtime/internal/runtime.interequal _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_itab$NL8p7q7SARa64DvUmDjl_oDf37OvD-BZTHhiszlZVZ8: + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + _llgo_main.T + main.(*T).M _llgo_main.T: *_llgo_main.T github.com/xgo-dev/llgo/runtime/internal/runtime.memequal0 @@ -23,10 +27,8 @@ main.(*T).M: main.init: main.init$guard main.main: - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 - _llgo_main.T + _llgo_itab$NL8p7q7SARa64DvUmDjl_oDf37OvD-BZTHhiszlZVZ8 github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab main.use main.use: github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index df2eb549e2..c4bcb9bb0b 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -8,6 +8,10 @@ _llgo_main.T: *_llgo_main.T: _llgo_main.T github.com/xgo-dev/llgo/runtime/internal/runtime.memequalptr +_llgo_itab$TICI59jcV6ebL9aVnDFuh3BidBlv40EGYDQe8VdVmdQ: + _llgo_main.T + github.com/xgo-dev/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + main.(*T).m _llgo_main.T: *_llgo_main.T github.com/xgo-dev/llgo/runtime/internal/runtime.memequal0 @@ -23,10 +27,8 @@ main.(*T).m: main.init: main.init$guard main.main: - _llgo_main.T - github.com/xgo-dev/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + _llgo_itab$TICI59jcV6ebL9aVnDFuh3BidBlv40EGYDQe8VdVmdQ github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab main.use main.use: github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 79859291f4..74c253b550 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -236,6 +236,10 @@ _llgo_io.Reader: _llgo_io.Writer: _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods github.com/xgo-dev/llgo/runtime/internal/runtime.interequal +_llgo_itab$rDYHFzHM8ToJeVDHQNMl4dlq5ed22GhfgKkd01126as: + *_llgo_bytes.Buffer + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + bytes.(*Buffer).Read _llgo_string: github.com/xgo-dev/llgo/runtime/internal/runtime.strequal _llgo_uint8: @@ -249,11 +253,9 @@ main.init: io.init main.init$guard main.main: - *_llgo_bytes.Buffer - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + _llgo_itab$rDYHFzHM8ToJeVDHQNMl4dlq5ed22GhfgKkd01126as bytes.NewBuffer github.com/xgo-dev/llgo/runtime/internal/runtime.IfacePtrData - github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab [UseIface] main.main: diff --git a/cl/_testmeta/typechildren_basic/meta-expect.txt b/cl/_testmeta/typechildren_basic/meta-expect.txt index 4e358b43f3..840f9c0feb 100644 --- a/cl/_testmeta/typechildren_basic/meta-expect.txt +++ b/cl/_testmeta/typechildren_basic/meta-expect.txt @@ -24,8 +24,8 @@ _llgo_struct$Z-8Mj1VAYg5xS_jUvJKfmUcA_2fP_OEHpanbgA_46rE$fields: main.init: main.init$guard main.main: + _llgo_ifacebox$YQgG4N1-99ddUlQsDwI81u6aI8_dGWCjWUOOopl1PW4 _llgo_main.Outer - github.com/xgo-dev/llgo/runtime/internal/runtime.AllocU main.use [UseIface] diff --git a/cl/_testrt/abinamed/in.go b/cl/_testrt/abinamed/in.go index 805a7069d9..5a55364477 100644 --- a/cl/_testrt/abinamed/in.go +++ b/cl/_testrt/abinamed/in.go @@ -12,13 +12,13 @@ import ( // runtime type descriptors; the test body then exercises lazy pointer-to-this // and element links from those descriptors. // CHECK-LABEL: define void @main.checkBasicNames(){{.*}} { -// CHECK-DAG: insertvalue %"{{.*}}eface" { ptr @_llgo_int32, -// CHECK-DAG: insertvalue %"{{.*}}eface" { ptr @_llgo_uint8, +// CHECK-DAG: store %"{{.*}}eface" { ptr @_llgo_int32, ptr @"_llgo_ifacebox${{.*}}" } +// CHECK-DAG: store %"{{.*}}eface" { ptr @_llgo_uint8, ptr @"_llgo_ifacebox${{.*}}" } // CHECK: call %"{{.*}}String" @"{{.*}}/runtime/abi.(*Type).String" // CHECK: call %"{{.*}}String" @"{{.*}}/runtime/abi.(*Type).String" // CHECK-LABEL: define void @main.main(){{.*}} { -// CHECK: insertvalue %"{{.*}}eface" { ptr @_llgo_main.T, -// CHECK: insertvalue %"{{.*}}eface" { ptr @"_llgo_{{.*}}/runtime/abi.Type", +// CHECK: call ptr @main.toEface(%"{{.*}}eface" { ptr @_llgo_main.T, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call ptr @main.toEface(%"{{.*}}eface" { ptr @"_llgo_{{.*}}/runtime/abi.Type", ptr @"_llgo_ifacebox${{.*}}" }) // CHECK: call ptr @"{{.*}}/runtime/abi.(*Type).StructType" // CHECK: call ptr @"{{.*}}/runtime/abi.(*Type).Elem" diff --git a/cl/_testrt/closureiface/in.go b/cl/_testrt/closureiface/in.go index 3957a22c67..d2319592da 100644 --- a/cl/_testrt/closureiface/in.go +++ b/cl/_testrt/closureiface/in.go @@ -33,10 +33,7 @@ func main() { // CHECK-NEXT: br i1 %[[TMP7]], label %_llgo_[[BB3:[0-9]+]], label %_llgo_[[BB4:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB1:[0-9]+]]: -// CHECK-NEXT: %[[TMP8:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB5]], i64 5 }, ptr %[[TMP8]], align 8 -// CHECK-NEXT: %[[TMP9:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP8]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP9]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB2:[0-9]+]]: diff --git a/cl/_testrt/eface/in.go b/cl/_testrt/eface/in.go index e516cb5431..b6eced10df 100644 --- a/cl/_testrt/eface/in.go +++ b/cl/_testrt/eface/in.go @@ -81,48 +81,28 @@ type eface struct { } // CHECK-LABEL: define void @main.main(){{.*}} { -// Every source value is boxed with its own descriptor and that exact eface is dumped. -// CHECK: [[BOOL_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_bool, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[BOOL_BOX]]) -// CHECK: [[INT_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[INT_BOX]]) -// CHECK: [[INT8_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int8, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[INT8_BOX]]) -// CHECK: [[INT16_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int16, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[INT16_BOX]]) -// CHECK: [[INT32_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int32, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[INT32_BOX]]) -// CHECK: [[INT64_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_int64, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[INT64_BOX]]) -// CHECK: [[UINT_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uint, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINT_BOX]]) -// CHECK: [[UINT8_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uint8, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINT8_BOX]]) -// CHECK: [[UINT16_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uint16, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINT16_BOX]]) -// CHECK: [[UINT32_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uint32, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINT32_BOX]]) -// CHECK: [[UINT64_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uint64, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINT64_BOX]]) -// CHECK: [[UINTPTR_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_uintptr, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[UINTPTR_BOX]]) -// CHECK: [[FLOAT32_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_float32, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[FLOAT32_BOX]]) -// CHECK: [[FLOAT64_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_float64, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[FLOAT64_BOX]]) -// CHECK: [[ARRAY_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @"[10]_llgo_int", ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[ARRAY_BOX]]) -// CHECK: [[CLOSURE_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @"_llgo_closure${{[-A-Za-z0-9_]+}}", ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[CLOSURE_BOX]]) +// Constant non-direct values use read-only iface boxes instead of AllocU. +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_bool, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_int, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_int8, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_int16, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_int32, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_int64, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uint, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uint8, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uint16, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uint32, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uint64, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_uintptr, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_float32, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_float64, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @"[10]_llgo_int", ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @"_llgo_closure${{.*}}", ptr @"_llgo_ifacebox${{.*}}" }) // CHECK: call void @main.dump(%"{{.*}}eface" { ptr @"*_llgo_int", ptr null }) -// CHECK: [[SLICE_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @"[]_llgo_int", ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[SLICE_BOX]]) -// CHECK: [[STRING_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_string, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[STRING_BOX]]) -// CHECK: [[STRUCT_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @"{{.*}}/cl/_testrt/eface.struct${{[-A-Za-z0-9_]+}}", ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[STRUCT_BOX]]) -// CHECK: [[NAMED_BOX:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_main.T, ptr undef }, ptr %{{.*}}, 1 -// CHECK: call void @main.dump(%"{{.*}}eface" [[NAMED_BOX]]) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @"[]_llgo_int", ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @"{{.*}}/cl/_testrt/eface.struct${{.*}}", ptr @"_llgo_ifacebox${{.*}}" }) +// CHECK: call void @main.dump(%"{{.*}}eface" { ptr @_llgo_main.T, ptr @"_llgo_ifacebox${{.*}}" }) func main() { dump(true) dump(0) diff --git a/cl/_testrt/float2any/in.go b/cl/_testrt/float2any/in.go index e95f8af9f3..6ad9c82234 100644 --- a/cl/_testrt/float2any/in.go +++ b/cl/_testrt/float2any/in.go @@ -86,17 +86,11 @@ func main() { // CHECK-NEXT: br i1 %[[TMP10]], label %_llgo_[[BB3:[0-9]+]], label %_llgo_[[BB4:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB2:[0-9]+]]: -// CHECK-NEXT: %[[TMP11:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB1]], i64 14 }, ptr %[[TMP11]], align 8 -// CHECK-NEXT: %[[TMP12:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP11]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP12]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB3]]: -// CHECK-NEXT: %[[TMP13:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB3]], i64 14 }, ptr %[[TMP13]], align 8 -// CHECK-NEXT: %[[TMP14:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP13]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP14]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB4]]: @@ -146,17 +140,11 @@ func main() { // CHECK-NEXT: br i1 %[[TMP13]], label %_llgo_[[BB3:[0-9]+]], label %_llgo_[[BB5:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB2:[0-9]+]]: -// CHECK-NEXT: %[[TMP14:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB5]], i64 14 }, ptr %[[TMP14]], align 8 -// CHECK-NEXT: %[[TMP15:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP14]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP15]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB3]]: -// CHECK-NEXT: %[[TMP16:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB6]], i64 14 }, ptr %[[TMP16]], align 8 -// CHECK-NEXT: %[[TMP17:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP16]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP17]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB4:[0-9]+]]: diff --git a/cl/_testrt/funcdecl/in.go b/cl/_testrt/funcdecl/in.go index cdb882d5af..b9fa05f9aa 100644 --- a/cl/_testrt/funcdecl/in.go +++ b/cl/_testrt/funcdecl/in.go @@ -8,17 +8,13 @@ import ( // A declared function is represented as a closure pair with a nil environment, // and interface assertions use the closure type descriptor. // CHECK-LABEL: define void @main.check({ ptr, ptr } %0){{.*}} { -// CHECK: %[[DECL_BOX:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store { ptr, ptr } { ptr @main.demo, ptr null }, ptr %[[DECL_BOX]] -// CHECK: %[[DECL_EFACE:[0-9]+]] = insertvalue %"{{.*}}runtime.eface" { ptr @[[CLOSURE_TYPE:"_llgo_closure\$[^"]+"]], ptr undef }, ptr %[[DECL_BOX]], 1 // CHECK: %[[ARG_BOX:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) // CHECK-NEXT: store { ptr, ptr } %0, ptr %[[ARG_BOX]] -// CHECK: %[[ARG_EFACE:[0-9]+]] = insertvalue %"{{.*}}runtime.eface" { ptr @[[CLOSURE_TYPE]], ptr undef }, ptr %[[ARG_BOX]], 1 -// CHECK: %[[DECL_TYPE:[0-9]+]] = extractvalue %"{{.*}}runtime.eface" %[[DECL_EFACE]], 0 -// CHECK-NEXT: %[[DECL_MATCH:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @[[CLOSURE_TYPE]], ptr %[[DECL_TYPE]]) +// CHECK: %[[ARG_EFACE:[0-9]+]] = insertvalue %"{{.*}}runtime.eface" { ptr @[[CLOSURE_TYPE:"_llgo_closure\$[^"]+"]], ptr undef }, ptr %[[ARG_BOX]], 1 +// CHECK: call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @[[CLOSURE_TYPE]], ptr @[[CLOSURE_TYPE]]) // CHECK: %[[ARG_TYPE:[0-9]+]] = extractvalue %"{{.*}}runtime.eface" %[[ARG_EFACE]], 0 -// CHECK-NEXT: %[[ARG_MATCH:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @[[CLOSURE_TYPE]], ptr %[[ARG_TYPE]]) -// CHECK: %[[DECL_PTR:[0-9]+]] = call ptr @main.closurePtr(%"{{.*}}runtime.eface" %[[DECL_EFACE]]) +// CHECK: %[[ARG_MATCH:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @[[CLOSURE_TYPE]], ptr %[[ARG_TYPE]]) +// CHECK: %[[DECL_PTR:[0-9]+]] = call ptr @main.closurePtr(%"{{.*}}runtime.eface" { ptr @[[CLOSURE_TYPE]], ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: %[[ARG_PTR:[0-9]+]] = call ptr @main.closurePtr(%"{{.*}}runtime.eface" %[[ARG_EFACE]]) // CHECK-NEXT: %[[SAME_PTR:[0-9]+]] = icmp eq ptr %[[DECL_PTR]], %[[ARG_PTR]] // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[SAME_PTR]]) diff --git a/cl/_testrt/mapclosure/in.go b/cl/_testrt/mapclosure/in.go index c7651a1a46..c54a3acec8 100644 --- a/cl/_testrt/mapclosure/in.go +++ b/cl/_testrt/mapclosure/in.go @@ -43,12 +43,12 @@ var ( // CHECK-NEXT: [[LIST_LEN:%[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" [[LOADED_LIST]], 1 // CHECK: [[LIST_SLOT:%[0-9]+]] = getelementptr inbounds { ptr, ptr }, ptr [[LIST_DATA]], i64 0 // CHECK-NEXT: [[LIST_FN:%[0-9]+]] = load { ptr, ptr }, ptr [[LIST_SLOT]] -// CHECK-NEXT: [[ITAB1:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.typ") +// CHECK-NEXT: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } // CHECK: [[MAP_ENV:%[0-9]+]] = extractvalue { ptr, ptr } [[MAP_FN]], 1 // CHECK-NEXT: [[MAP_CODE:%[0-9]+]] = extractvalue { ptr, ptr } [[MAP_FN]], 0 // ARM64: [[MAP_RESULT:%[0-9]+]] = call %"{{.*}}/runtime/internal/runtime.String" %__llgo_funcval_code(ptr swiftself [[MAP_ENV]], %"{{.*}}/runtime/internal/runtime.iface" [[MAP_ARG:%[0-9]+]]) // AMD64: [[MAP_RESULT:%[0-9]+]] = call %"{{.*}}/runtime/internal/runtime.String" %__llgo_funcval_code(ptr nest [[MAP_ENV]], %"{{.*}}/runtime/internal/runtime.iface" [[MAP_ARG:%[0-9]+]]) -// CHECK: [[ITAB2:%[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface${{[-A-Za-z0-9_]+}}", ptr @"*_llgo_main.typ") +// CHECK: insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef } // CHECK: [[LIST_ENV:%[0-9]+]] = extractvalue { ptr, ptr } [[LIST_FN]], 1 // CHECK-NEXT: [[LIST_CODE:%[0-9]+]] = extractvalue { ptr, ptr } [[LIST_FN]], 0 // ARM64: [[LIST_RESULT:%[0-9]+]] = call %"{{.*}}/runtime/internal/runtime.String" %__llgo_funcval_code1(ptr swiftself [[LIST_ENV]], %"{{.*}}/runtime/internal/runtime.iface" [[LIST_ARG:%[0-9]+]]) diff --git a/cl/_testrt/methodthunk/in.go b/cl/_testrt/methodthunk/in.go index d0499963f3..fefcdc402a 100644 --- a/cl/_testrt/methodthunk/in.go +++ b/cl/_testrt/methodthunk/in.go @@ -47,69 +47,56 @@ func (m *outer) M() {} // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_[[BB0:[0-9]+]]: -// CHECK-NEXT: %[[TMP0:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store { ptr, ptr } { ptr @"main.(*outer).M$thunk", ptr null }, ptr %[[TMP0]], align 8 -// CHECK-NEXT: %[[TMP1:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M", ptr undef }, ptr %[[TMP0]], 1 -// CHECK-NEXT: %[[TMP2:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store { ptr, ptr } { ptr @"main.(*InnerInt).M$thunk", ptr null }, ptr %[[TMP2]], align 8 -// CHECK-NEXT: %[[TMP3:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"_llgo_closure$WmZDdXg-2mo-o0lqY2QOZoNsd6Ks2TbzdX4upfVJ3j8", ptr undef }, ptr %[[TMP2]], 1 -// CHECK-NEXT: %[[TMP4:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %[[TMP1]], 0 -// CHECK-NEXT: %[[TMP5:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M", ptr %[[TMP4]]) -// CHECK-NEXT: br i1 %[[TMP5]], label %_llgo_[[BB3:[0-9]+]], label %_llgo_[[BB4:[0-9]+]] +// CHECK-NEXT: %[[TMP0:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M", ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M") +// CHECK-NEXT: br i1 %[[TMP0]], label %_llgo_[[BB3:[0-9]+]], label %_llgo_[[BB4:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB1:[0-9]+]]: -// CHECK-NEXT: %[[TMP6:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB17]], i64 47 }, ptr %[[TMP6]], align 8 -// CHECK-NEXT: %[[TMP7:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %[[TMP6]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %[[TMP7]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB2:[0-9]+]]: // CHECK-NEXT: ret void // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB3]]: -// CHECK-NEXT: %[[TMP8:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %[[TMP1]], 1 -// CHECK-NEXT: %[[TMP9:[0-9]+]] = load { ptr, ptr }, ptr %[[TMP8]], align 8 -// CHECK-NEXT: %[[TMP10:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } undef, { ptr, ptr } %[[TMP9]], 0 -// CHECK-NEXT: %[[TMP11:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } %[[TMP10]], i1 true, 1 +// CHECK-NEXT: %[[TMP1:[0-9]+]] = load { ptr, ptr }, ptr @"_llgo_ifacebox${{.*}}", align 8 +// CHECK-NEXT: %[[TMP2:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } undef, { ptr, ptr } %[[TMP1]], 0 +// CHECK-NEXT: %[[TMP3:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } %[[TMP2]], i1 true, 1 // CHECK-NEXT: br label %_llgo_[[BB5:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB4]]: // CHECK-NEXT: br label %_llgo_[[BB5]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB5]]: -// CHECK-NEXT: %[[TMP12:[0-9]+]] = phi { { ptr, ptr }, i1 } [ %[[TMP11]], %_llgo_[[BB3]] ], [ zeroinitializer, %_llgo_[[BB4]] ] -// CHECK-NEXT: %[[TMP13:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP12]], 0 -// CHECK-NEXT: %[[TMP14:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP12]], 1 -// CHECK-NEXT: %[[TMP15:[0-9]+]] = extractvalue { ptr, ptr } %[[TMP13]], 0 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP15]]) +// CHECK-NEXT: %[[TMP4:[0-9]+]] = phi { { ptr, ptr }, i1 } [ %[[TMP3]], %_llgo_[[BB3]] ], [ zeroinitializer, %_llgo_[[BB4]] ] +// CHECK-NEXT: %[[TMP5:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP4]], 0 +// CHECK-NEXT: %[[TMP6:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP4]], 1 +// CHECK-NEXT: %[[TMP7:[0-9]+]] = extractvalue { ptr, ptr } %[[TMP5]], 0 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP7]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP14]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP6]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %[[TMP16:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %[[TMP3]], 0 -// CHECK-NEXT: %[[TMP17:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M", ptr %[[TMP16]]) -// CHECK-NEXT: br i1 %[[TMP17]], label %_llgo_[[BB6:[0-9]+]], label %_llgo_[[BB7:[0-9]+]] +// CHECK-NEXT: %[[TMP8:[0-9]+]] = call i1 @"{{.*}}/runtime/internal/runtime.MatchesClosure"(ptr @"_llgo_closure$p06T23YeLPAu3v8p2hFtiY7Rlq6V9sr5dXePfqXtx6M", ptr @"_llgo_closure$WmZDdXg-2mo-o0lqY2QOZoNsd6Ks2TbzdX4upfVJ3j8") +// CHECK-NEXT: br i1 %[[TMP8]], label %_llgo_[[BB6:[0-9]+]], label %_llgo_[[BB7:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB6]]: -// CHECK-NEXT: %[[TMP18:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %[[TMP3]], 1 -// CHECK-NEXT: %[[TMP19:[0-9]+]] = load { ptr, ptr }, ptr %[[TMP18]], align 8 -// CHECK-NEXT: %[[TMP20:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } undef, { ptr, ptr } %[[TMP19]], 0 -// CHECK-NEXT: %[[TMP21:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } %[[TMP20]], i1 true, 1 +// CHECK-NEXT: %[[TMP9:[0-9]+]] = load { ptr, ptr }, ptr @"_llgo_ifacebox${{.*}}", align 8 +// CHECK-NEXT: %[[TMP10:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } undef, { ptr, ptr } %[[TMP9]], 0 +// CHECK-NEXT: %[[TMP11:[0-9]+]] = insertvalue { { ptr, ptr }, i1 } %[[TMP10]], i1 true, 1 // CHECK-NEXT: br label %_llgo_[[BB8:[0-9]+]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB7]]: // CHECK-NEXT: br label %_llgo_[[BB8]] // CHECK-EMPTY: // CHECK-NEXT: _llgo_[[BB8]]: -// CHECK-NEXT: %[[TMP22:[0-9]+]] = phi { { ptr, ptr }, i1 } [ %[[TMP21]], %_llgo_[[BB6]] ], [ zeroinitializer, %_llgo_[[BB7]] ] -// CHECK-NEXT: %[[TMP23:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP22]], 0 -// CHECK-NEXT: %[[TMP24:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP22]], 1 -// CHECK-NEXT: %[[TMP25:[0-9]+]] = extractvalue { ptr, ptr } %[[TMP23]], 0 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP25]]) +// CHECK-NEXT: %[[TMP12:[0-9]+]] = phi { { ptr, ptr }, i1 } [ %[[TMP11]], %_llgo_[[BB6]] ], [ zeroinitializer, %_llgo_[[BB7]] ] +// CHECK-NEXT: %[[TMP13:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP12]], 0 +// CHECK-NEXT: %[[TMP14:[0-9]+]] = extractvalue { { ptr, ptr }, i1 } %[[TMP12]], 1 +// CHECK-NEXT: %[[TMP15:[0-9]+]] = extractvalue { ptr, ptr } %[[TMP13]], 0 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintPointer"(ptr %[[TMP15]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP24]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP14]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: br i1 %[[TMP24]], label %_llgo_[[BB1]], label %_llgo_[[BB2]] +// CHECK-NEXT: br i1 %[[TMP14]], label %_llgo_[[BB1]], label %_llgo_[[BB2]] // CHECK-NEXT: } // CHECK-LABEL: define void @"main.(*outer).M$thunk"( diff --git a/cl/_testrt/reflectclosureenv/in.go b/cl/_testrt/reflectclosureenv/in.go index 063d743c15..9c521f5481 100644 --- a/cl/_testrt/reflectclosureenv/in.go +++ b/cl/_testrt/reflectclosureenv/in.go @@ -66,7 +66,7 @@ type receiver struct { // CHECK-DAG: [[MAIN_NESTED_VALUE:%.*]] = call %reflect.Value @reflect.ValueOf(%"{{.*}}eface" [[MAIN_NESTED_BOX]]) // CHECK-DAG: call void @main.checkInt(%reflect.Value [[MAIN_NESTED_VALUE]], %"{{.*}}Slice" [[MAIN_INTS]]) // MakeFunc receives the type of the nine-argument literal and the slice callback main$2. -// CHECK-DAG: [[FUNC_TYPE:%.*]] = call %"{{.*}}iface" @reflect.TypeOf(%"{{.*}}eface" %{{.*}}) +// CHECK-DAG: [[FUNC_TYPE:%.*]] = call %"{{.*}}iface" @reflect.TypeOf(%"{{.*}}eface" { ptr @"_llgo_closure${{[-A-Za-z0-9_]+}}", ptr @"_llgo_ifacebox${{.*}}" }) // CHECK-DAG: [[MADE:%.*]] = call %reflect.Value @reflect.MakeFunc(%"{{.*}}iface" [[FUNC_TYPE]], { ptr, ptr } { ptr @"main.main$2", ptr null }) // CHECK-DAG: call void @main.checkInt(%reflect.Value [[MADE]], %"{{.*}}Slice" [[MAIN_INTS]]) // MethodByName is performed on receiver{base: 10} and checked with the same arguments. diff --git a/cl/_testrt/staticifacebox/in.go b/cl/_testrt/staticifacebox/in.go new file mode 100644 index 0000000000..100741d9b4 --- /dev/null +++ b/cl/_testrt/staticifacebox/in.go @@ -0,0 +1,42 @@ +// LITTEST +// Scope: common +package main + +type I interface { + M() int +} + +type T int + +func (t T) M() int { return int(t) + 1 } + +// Constant non-direct iface data (integers) go in a read-only box, not AllocU. +// CHECK-LABEL: define {{.*}} @main.boxed(){{.*}} { +// CHECK-NOT: AllocU +// CHECK: _llgo_ifacebox$ +func boxed() any { + return int64(42) +} + +func twoStrings() (any, any) { + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbb" +} + +func main() { + v := boxed() + if v.(int64) != 42 { + panic(v) + } + if boxed() != any(int64(42)) { + panic("eface equality") + } + var i I = T(41) + if i.M() != 42 { + panic(i.M()) + } + a, b := twoStrings() + if a.(string) == b.(string) { + panic("same-length string boxes collided") + } + println("ok") +} diff --git a/cl/_testrt/staticitab/in.go b/cl/_testrt/staticitab/in.go new file mode 100644 index 0000000000..b3ba43cac9 --- /dev/null +++ b/cl/_testrt/staticitab/in.go @@ -0,0 +1,35 @@ +// LITTEST +// Scope: common +package main + +type I interface { + M() int +} + +type T int + +func (t T) M() int { return int(t) + 1 } + +// Known T2I uses a static itab global instead of runtime.NewItab. +// CHECK-LABEL: define {{.*}} @main.boxed({{.*}}){{.*}} { +// CHECK-NOT: NewItab +// CHECK: _llgo_itab$ +func boxed(t T) I { + return t +} + +func main() { + var i I = T(41) + if i.M() != 42 { + panic(i.M()) + } + j := boxed(T(41)) + if i != j { + panic("iface equality") + } + k := any(T(41)).(I) + if i != k { + panic("static vs assert itab") + } + println("ok") +} diff --git a/cl/_testrt/tpabi/in.go b/cl/_testrt/tpabi/in.go index aa6ce3eb97..7bb1e811f7 100644 --- a/cl/_testrt/tpabi/in.go +++ b/cl/_testrt/tpabi/in.go @@ -58,9 +58,7 @@ func main() { // CHECK: %[[ETYPE:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %[[EBOX]], 0 // CHECK: icmp eq ptr %[[ETYPE]], @"_llgo_main.T[string,int]" // CHECK: %[[OBJ:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK: %[[ITAB:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr {{.*}}, ptr @"*_llgo_main.T[string,int]") -// CHECK: %[[IFACE0:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %[[ITAB]], 0 -// CHECK: %[[IFACE:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %[[IFACE0]], ptr %[[OBJ]], 1 +// CHECK: %[[IFACE:[0-9]+]] = insertvalue %"{{.*}}iface" { ptr @"_llgo_itab${{.*}}", ptr undef }, ptr %[[OBJ]], 1 // CHECK: %[[DATA:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %[[IFACE]]) // CHECK: %[[VTAB:[0-9]+]] = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %[[IFACE]], 0 // CHECK: %[[SLOT:[0-9]+]] = getelementptr ptr, ptr %[[VTAB]], i64 3 diff --git a/cl/_testrt/tpmap/in.go b/cl/_testrt/tpmap/in.go index 04802c1f97..cd6b5763af 100644 --- a/cl/_testrt/tpmap/in.go +++ b/cl/_testrt/tpmap/in.go @@ -48,23 +48,20 @@ func main() { // CHECK-NEXT: %[[TMP11:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP5]], i32 0, i32 2 // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %[[TMP3]], i8 0, i64 16, i1 false) // CHECK-NEXT: %[[TMP12:[0-9]+]] = getelementptr inbounds nuw %"main.T3[interface{}]", ptr %[[TMP3]], i32 0, i32 0 -// CHECK-NEXT: %[[TMP13:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) -// CHECK-NEXT: store i64 0, ptr %[[TMP13]], align 8 -// CHECK-NEXT: %[[TMP14:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr undef }, ptr %[[TMP13]], 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %[[TMP14]], ptr %[[TMP12]], align 8 -// CHECK-NEXT: %[[TMP15:[0-9]+]] = load %"main.T3[interface{}]", ptr %[[TMP3]], align 8 -// CHECK-NEXT: %[[TMP16:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP5]], i32 0, i32 3 -// CHECK-NEXT: %[[TMP17:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP5]], i32 0, i32 4 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr @"_llgo_ifacebox${{.*}}" }, ptr %[[TMP12]], align 8 +// CHECK-NEXT: %[[TMP13:[0-9]+]] = load %"main.T3[interface{}]", ptr %[[TMP3]], align 8 +// CHECK-NEXT: %[[TMP14:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP5]], i32 0, i32 3 +// CHECK-NEXT: %[[TMP15:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP5]], i32 0, i32 4 // CHECK-NEXT: store i64 0, ptr %[[TMP7]], align 8 // CHECK-NEXT: store %main.T2 %[[TMP10]], ptr %[[TMP8]], align 8 -// CHECK-NEXT: store %"main.T3[interface{}]" %[[TMP15]], ptr %[[TMP11]], align 8 -// CHECK-NEXT: store ptr null, ptr %[[TMP16]], align 8 -// CHECK-NEXT: store i64 0, ptr %[[TMP17]], align 8 -// CHECK-NEXT: %[[TMP18:[0-9]+]] = load %main.cacheKey, ptr %[[TMP5]], align 8 -// CHECK-NEXT: %[[TMP19:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 48) -// CHECK-NEXT: store %main.cacheKey %[[TMP18]], ptr %[[TMP19]], align 8 -// CHECK-NEXT: %[[TMP20:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_main.cacheKey]_llgo_string", ptr %[[TMP6]], ptr %[[TMP19]]) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB29]], i64 5 }, ptr %[[TMP20]], align 8 +// CHECK-NEXT: store %"main.T3[interface{}]" %[[TMP13]], ptr %[[TMP11]], align 8 +// CHECK-NEXT: store ptr null, ptr %[[TMP14]], align 8 +// CHECK-NEXT: store i64 0, ptr %[[TMP15]], align 8 +// CHECK-NEXT: %[[TMP16:[0-9]+]] = load %main.cacheKey, ptr %[[TMP5]], align 8 +// CHECK-NEXT: %[[TMP17:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 48) +// CHECK-NEXT: store %main.cacheKey %[[TMP16]], ptr %[[TMP17]], align 8 +// CHECK-NEXT: %[[TMP18:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_main.cacheKey]_llgo_string", ptr %[[TMP6]], ptr %[[TMP17]]) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @[[GLOB29]], i64 5 }, ptr %[[TMP18]], align 8 // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %[[TMP2]], i8 0, i64 48, i1 false) // CHECK-NEXT: %[[TMP21:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 0 // CHECK-NEXT: %[[TMP22:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 1 @@ -75,32 +72,29 @@ func main() { // CHECK-NEXT: %[[TMP25:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 2 // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %[[TMP0]], i8 0, i64 16, i1 false) // CHECK-NEXT: %[[TMP26:[0-9]+]] = getelementptr inbounds nuw %"main.T3[interface{}]", ptr %[[TMP0]], i32 0, i32 0 -// CHECK-NEXT: %[[TMP27:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) -// CHECK-NEXT: store i64 0, ptr %[[TMP27]], align 8 -// CHECK-NEXT: %[[TMP28:[0-9]+]] = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr undef }, ptr %[[TMP27]], 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %[[TMP28]], ptr %[[TMP26]], align 8 -// CHECK-NEXT: %[[TMP29:[0-9]+]] = load %"main.T3[interface{}]", ptr %[[TMP0]], align 8 -// CHECK-NEXT: %[[TMP30:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 3 -// CHECK-NEXT: %[[TMP31:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 4 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int, ptr @"_llgo_ifacebox${{.*}}" }, ptr %[[TMP26]], align 8 +// CHECK-NEXT: %[[TMP27:[0-9]+]] = load %"main.T3[interface{}]", ptr %[[TMP0]], align 8 +// CHECK-NEXT: %[[TMP28:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 3 +// CHECK-NEXT: %[[TMP29:[0-9]+]] = getelementptr inbounds nuw %main.cacheKey, ptr %[[TMP2]], i32 0, i32 4 // CHECK-NEXT: store i64 0, ptr %[[TMP21]], align 8 // CHECK-NEXT: store %main.T2 %[[TMP24]], ptr %[[TMP22]], align 8 -// CHECK-NEXT: store %"main.T3[interface{}]" %[[TMP29]], ptr %[[TMP25]], align 8 -// CHECK-NEXT: store ptr null, ptr %[[TMP30]], align 8 -// CHECK-NEXT: store i64 0, ptr %[[TMP31]], align 8 -// CHECK-NEXT: %[[TMP32:[0-9]+]] = load %main.cacheKey, ptr %[[TMP2]], align 8 -// CHECK-NEXT: %[[TMP33:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 48) -// CHECK-NEXT: store %main.cacheKey %[[TMP32]], ptr %[[TMP33]], align 8 -// CHECK-NEXT: %[[TMP34:[0-9]+]] = call { ptr, i1 } @"{{.*}}/runtime/internal/runtime.MapAccess2"(ptr @"map[_llgo_main.cacheKey]_llgo_string", ptr %[[TMP6]], ptr %[[TMP33]]) -// CHECK-NEXT: %[[TMP35:[0-9]+]] = extractvalue { ptr, i1 } %[[TMP34]], 0 -// CHECK-NEXT: %[[TMP36:[0-9]+]] = load %"{{.*}}/runtime/internal/runtime.String", ptr %[[TMP35]], align 8 -// CHECK-NEXT: %[[TMP37:[0-9]+]] = extractvalue { ptr, i1 } %[[TMP34]], 1 -// CHECK-NEXT: %[[TMP38:[0-9]+]] = insertvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } undef, %"{{.*}}/runtime/internal/runtime.String" %[[TMP36]], 0 -// CHECK-NEXT: %[[TMP39:[0-9]+]] = insertvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP38]], i1 %[[TMP37]], 1 -// CHECK-NEXT: %[[TMP40:[0-9]+]] = extractvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP39]], 0 -// CHECK-NEXT: %[[TMP41:[0-9]+]] = extractvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP39]], 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %[[TMP40]]) +// CHECK-NEXT: store %"main.T3[interface{}]" %[[TMP27]], ptr %[[TMP25]], align 8 +// CHECK-NEXT: store ptr null, ptr %[[TMP28]], align 8 +// CHECK-NEXT: store i64 0, ptr %[[TMP29]], align 8 +// CHECK-NEXT: %[[TMP30:[0-9]+]] = load %main.cacheKey, ptr %[[TMP2]], align 8 +// CHECK-NEXT: %[[TMP31:[0-9]+]] = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 48) +// CHECK-NEXT: store %main.cacheKey %[[TMP30]], ptr %[[TMP31]], align 8 +// CHECK-NEXT: %[[TMP32:[0-9]+]] = call { ptr, i1 } @"{{.*}}/runtime/internal/runtime.MapAccess2"(ptr @"map[_llgo_main.cacheKey]_llgo_string", ptr %[[TMP6]], ptr %[[TMP31]]) +// CHECK-NEXT: %[[TMP33:[0-9]+]] = extractvalue { ptr, i1 } %[[TMP32]], 0 +// CHECK-NEXT: %[[TMP34:[0-9]+]] = load %"{{.*}}/runtime/internal/runtime.String", ptr %[[TMP33]], align 8 +// CHECK-NEXT: %[[TMP35:[0-9]+]] = extractvalue { ptr, i1 } %[[TMP32]], 1 +// CHECK-NEXT: %[[TMP36:[0-9]+]] = insertvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } undef, %"{{.*}}/runtime/internal/runtime.String" %[[TMP34]], 0 +// CHECK-NEXT: %[[TMP37:[0-9]+]] = insertvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP36]], i1 %[[TMP35]], 1 +// CHECK-NEXT: %[[TMP38:[0-9]+]] = extractvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP37]], 0 +// CHECK-NEXT: %[[TMP39:[0-9]+]] = extractvalue { %"{{.*}}/runtime/internal/runtime.String", i1 } %[[TMP37]], 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %[[TMP38]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP41]]) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %[[TMP39]]) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testrt/typed/in.go b/cl/_testrt/typed/in.go index 2b92ca30b1..9e51ec19c9 100644 --- a/cl/_testrt/typed/in.go +++ b/cl/_testrt/typed/in.go @@ -24,17 +24,10 @@ func main() { // CHECK-LABEL: define void @main.main(){{.*}} { // The named string uses an exact (panicking) assertion, then deliberately fails // a comma-ok assertion to the underlying unnamed string type. -// CHECK: [[T_DATA:%.*]] = call ptr @"{{.*}}AllocU"(i64 16) -// CHECK: [[T_EFACE:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_main.T, ptr undef }, ptr [[T_DATA]], 1 -// CHECK: [[T_DYN_TYPE:%.*]] = extractvalue %"{{.*}}eface" [[T_EFACE]], 0 -// CHECK-NEXT: [[IS_T:%.*]] = icmp eq ptr [[T_DYN_TYPE]], @_llgo_main.T -// CHECK: br i1 [[IS_T]], label %{{.*}}, label %{{.*}} -// CHECK: [[T_DYN_DATA:%.*]] = extractvalue %"{{.*}}eface" [[T_EFACE]], 1 -// CHECK-NEXT: [[T_VALUE:%.*]] = load %"{{.*}}String", ptr [[T_DYN_DATA]] +// CHECK: br i1 true, label %{{.*}}, label %{{.*}} +// CHECK: [[T_VALUE:%.*]] = load %"{{.*}}String", ptr @"_llgo_ifacebox${{.*}}" // CHECK-NEXT: call void @"{{.*}}PrintString"(%"{{.*}}String" [[T_VALUE]]) -// CHECK: [[STRING_DYN_TYPE:%.*]] = extractvalue %"{{.*}}eface" [[T_EFACE]], 0 -// CHECK-NEXT: [[IS_STRING:%.*]] = icmp eq ptr [[STRING_DYN_TYPE]], @_llgo_string -// CHECK: call void @"{{.*}}PanicTypeAssert"(ptr null, ptr [[T_DYN_TYPE]], ptr @_llgo_main.T) +// CHECK: call void @"{{.*}}PanicTypeAssert"(ptr null, ptr @_llgo_main.T, ptr @_llgo_main.T) // CHECK-NEXT: unreachable // CHECK: [[STRING_OK_RESULT:%.*]] = phi { %"{{.*}}String", i1 } [ {{%.*}}, %{{.*}} ], [ zeroinitializer, %{{.*}} ] // CHECK: [[STRING_VALUE:%.*]] = extractvalue { %"{{.*}}String", i1 } [[STRING_OK_RESULT]], 0 @@ -59,12 +52,8 @@ func main() { // CHECK: call void @"{{.*}}PrintBool"(i1 [[A_OK]]) // A named slice uses its named descriptor while the asserted payload retains // the ordinary three-word slice representation. -// CHECK: [[BYTES_DATA:%.*]] = call ptr @"{{.*}}AllocU"(i64 24) -// CHECK: [[BYTES_EFACE:%.*]] = insertvalue %"{{.*}}eface" { ptr @_llgo_main.MyBytes, ptr undef }, ptr [[BYTES_DATA]], 1 -// CHECK: [[BYTES_TYPE:%.*]] = extractvalue %"{{.*}}eface" [[BYTES_EFACE]], 0 -// CHECK-NEXT: [[IS_BYTES:%.*]] = icmp eq ptr [[BYTES_TYPE]], @_llgo_main.MyBytes -// CHECK: [[BYTES_PAYLOAD:%.*]] = extractvalue %"{{.*}}eface" [[BYTES_EFACE]], 1 -// CHECK-NEXT: load %"{{.*}}Slice", ptr [[BYTES_PAYLOAD]] +// CHECK: br i1 true, label %{{.*}}, label %{{.*}} +// CHECK: load %"{{.*}}Slice", ptr @"_llgo_ifacebox${{.*}}" // CHECK: [[BYTES_RESULT:%.*]] = phi { %"{{.*}}Slice", i1 } [ {{.*}}, %{{.*}} ], [ zeroinitializer, %{{.*}} ] // CHECK: [[BYTES_OK:%.*]] = extractvalue { %"{{.*}}Slice", i1 } [[BYTES_RESULT]], 1 // CHECK: call void @"{{.*}}PrintBool"(i1 [[BYTES_OK]]) diff --git a/cl/cgo_test.go b/cl/cgo_test.go index 5837943703..6bc002eb91 100644 --- a/cl/cgo_test.go +++ b/cl/cgo_test.go @@ -157,14 +157,15 @@ func _C2func_sum(a int32, b int32) (int32, error) { for _, want := range []string{ "icmp ne i32", "sext i32", - "NewItab", - "_llgo_syscall.Errno", "br i1", } { if !strings.Contains(ir, want) { t.Fatalf("C2func wrapper is missing %q:\n%s", want, ir) } } + if !strings.Contains(ir, "NewItab") && !strings.Contains(ir, "_llgo_itab$") { + t.Fatalf("C2func wrapper is missing itab construction:\n%s", ir) + } if got := strings.Count(ir, "ret {"); got != 2 { t.Fatalf("C2func wrapper return paths = %d, want 2:\n%s", got, ir) } @@ -421,14 +422,15 @@ func TestCgoC2Return_ErrnoNeedsConvert(t *testing.T) { for _, want := range []string{ "trunc i64", "icmp ne i32", - "NewItab", - "_llgo_syscall.Errno", "br i1", } { if !strings.Contains(ir, want) { t.Fatalf("cgoC2Return is missing %q:\n%s", want, ir) } } + if !strings.Contains(ir, "NewItab") && !strings.Contains(ir, "_llgo_itab$") { + t.Fatalf("cgoC2Return is missing itab construction:\n%s", ir) + } if got := strings.Count(ir, "ret {"); got != 2 { t.Fatalf("cgoC2Return return paths = %d, want 2:\n%s", got, ir) } diff --git a/runtime/internal/runtime/alg.go b/runtime/internal/runtime/alg.go index 475c2d960b..4d9a5b4c93 100644 --- a/runtime/internal/runtime/alg.go +++ b/runtime/internal/runtime/alg.go @@ -254,7 +254,15 @@ func strequal(p, q unsafe.Pointer) bool { func interequal(p, q unsafe.Pointer) bool { x := *(*iface)(p) y := *(*iface)(q) - return x.tab == y.tab && ifaceeq(x.tab, x.data, y.data) + if x.tab == y.tab { + return ifaceeq(x.tab, x.data, y.data) + } + // LTO and deadcode-drop T2I still use NewItab while ordinary T2I uses a + // static itab. Mixed paths compare the (inter, _type) pair. + if x.tab == nil || y.tab == nil { + return false + } + return x.tab.inter == y.tab.inter && x.tab._type == y.tab._type && ifaceeq(x.tab, x.data, y.data) } func nilinterequal(p, q unsafe.Pointer) bool { x := *(*eface)(p) diff --git a/runtime/internal/runtime/z_face.go b/runtime/internal/runtime/z_face.go index 5be52a8642..60b83148ff 100644 --- a/runtime/internal/runtime/z_face.go +++ b/runtime/internal/runtime/z_face.go @@ -109,6 +109,18 @@ func addItab(i *Itab) { itabTable.Unlock() } +// RegisterStaticItab records a compiler-emitted itab so NewItab/getitab +// return the same pointer as T2I. Idempotent if the pair is already present. +func RegisterStaticItab(i *Itab) { + if i == nil || i._type == nil { + return + } + if findItab(i.inter, i._type) != nil { + return + } + addItab(i) +} + // NewItab returns a new itab. func NewItab(inter *InterfaceType, typ *Type) *Itab { if typ == nil { diff --git a/ssa/interface.go b/ssa/interface.go index 568bfa5df7..cbcfff3d48 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -22,6 +22,9 @@ import ( "encoding/binary" "go/token" "go/types" + "math" + "strconv" + "strings" "github.com/xgo-dev/llgo/ssa/abi" "github.com/xgo-dev/llvm" @@ -46,10 +49,23 @@ func (b Builder) newItab(tintf, typ Expr) Expr { func (b Builder) staticItab(rawIntf *types.Interface, concrete types.Type, tintf, typ Expr) (Expr, bool) { prog := b.Prog - if !prog.enableGoGlobalDCE || !prog.enableLTOPluginMarker || - rawIntf.NumMethods() == 0 || concrete == nil { + if rawIntf.NumMethods() == 0 || concrete == nil { return Expr{}, false } + // Deadcode-drop treats Fun[] as live uses. Skip the static vtable and let + // T2I call NewItab, matching LTO method-drop. + if !prog.useRuntimeStaticItab() && !prog.enableLTOPluginMarker { + return Expr{}, false + } + rawIntf = rawIntf.Complete() + intfName, _ := prog.abi.TypeName(rawIntf) + typeName, _ := prog.abi.TypeName(concrete) + sum := sha256.Sum256([]byte(intfName + "\x00" + typeName)) + name := "_llgo_itab$" + base64.RawURLEncoding.EncodeToString(sum[:]) + itabTy := prog.Pointer(prog.rtType("Itab")) + if global := b.Pkg.VarOf(name); global != nil { + return Expr{global.impl, itabTy}, true + } if !types.AssignableTo(concrete, rawIntf) { return Expr{}, false } @@ -64,14 +80,6 @@ func (b Builder) staticItab(rawIntf *types.Interface, concrete types.Type, tintf methods[i] = method } - intfName, _ := prog.abi.TypeName(rawIntf) - typeName, _ := prog.abi.TypeName(concrete) - sum := sha256.Sum256([]byte(intfName + "\x00" + typeName)) - name := "_llgo_itab$" + base64.RawURLEncoding.EncodeToString(sum[:]) - if global := b.Pkg.VarOf(name); global != nil { - return Expr{global.impl, prog.Pointer(prog.rtType("Itab"))}, true - } - ptr := prog.VoidPtr() funArray := prog.rawType(types.NewArray(ptr.RawType(), int64(len(methods)))) staticType := prog.rawType(types.NewStruct([]*types.Var{ @@ -80,13 +88,20 @@ func (b Builder) staticItab(rawIntf *types.Interface, concrete types.Type, tintf types.NewVar(token.NoPos, nil, "hash", types.Typ[types.Uint32]), types.NewVar(token.NoPos, nil, "fun", funArray.RawType()), }, nil)) - global := b.Pkg.NewVarEx(name, prog.Pointer(staticType)) funcs := make([]llvm.Value, len(methods)) for i, method := range methods { - funcs[i], _ = b.abiMethodFuncs(concrete, method) + ifn, _ := b.abiMethodFuncs(concrete, method) + if ifn.IsNil() { + return Expr{}, false + } + funcs[i] = ifn + } + hash, ok := typeHashFromGlobal(typ.impl) + if !ok { + hashBytes := sha256.Sum256([]byte(typeName)) + hash = binary.LittleEndian.Uint32(hashBytes[:4]) } - hashBytes := sha256.Sum256([]byte(typeName)) - hash := binary.LittleEndian.Uint32(hashBytes[:4]) + global := b.Pkg.NewVarEx(name, prog.Pointer(staticType)) global.impl.SetInitializer(prog.constStructValue(staticType, []llvm.Value{ tintf.impl, typ.impl, @@ -101,28 +116,122 @@ func (b Builder) staticItab(rawIntf *types.Interface, concrete types.Type, tintf })) global.impl.SetGlobalConstant(true) b.Pkg.setODRLinkage(global.impl, llvm.WeakODRLinkage) + // The T2I site is the only reference so --gc-sections/-dead_strip can + // drop itabs that belong to dead functions. + + if prog.enableLTOPluginMarker { + slotKind := prog.ctx.MDKindID("llgo.static.itab.slot") + funOffset := uint64(prog.td.ElementOffset(staticType.ll, 3)) + stride := uint64(prog.td.TypeAllocSize(prog.storageType(ptr))) + interfaceTypeID := prog.interfaceCapabilityKey(rawIntf) + for i := range methods { + offset := funOffset + uint64(i)*stride + typeID := interfaceMethodCapabilityKeyFromID(interfaceTypeID, i) + node := prog.ctx.MDNode([]llvm.Metadata{ + llvm.ConstInt(prog.Int64().ll, offset, false).ConstantAsMetadata(), + prog.ctx.MDString(typeID), + }) + global.impl.AddMetadata(slotKind, node) + } + // Keep the template until the LTO plugin consumes it. Runtime T2I + // still uses NewItab so GlobalDCE can drop unused itab methods. + b.Pkg.markLLVMUsed(global.impl) + } + return Expr{global.impl, itabTy}, true +} - // Describe each function slot with private LLGo metadata. The template is a - // compile-time certificate, not a runtime vtable, so it must not participate - // in LLVM's type-test candidate sets before the plugin consumes it. - slotKind := prog.ctx.MDKindID("llgo.static.itab.slot") - funOffset := uint64(prog.td.ElementOffset(staticType.ll, 3)) - stride := uint64(prog.td.TypeAllocSize(prog.storageType(ptr))) - interfaceTypeID := prog.interfaceCapabilityKey(rawIntf) - for i := range methods { - offset := funOffset + uint64(i)*stride - typeID := interfaceMethodCapabilityKeyFromID(interfaceTypeID, i) - node := prog.ctx.MDNode([]llvm.Metadata{ - llvm.ConstInt(prog.Int64().ll, offset, false).ConstantAsMetadata(), - prog.ctx.MDString(typeID), - }) - global.impl.AddMetadata(slotKind, node) +// staticIfaceBox puts a compile-time constant in a read-only global and +// returns its address, matching cmd/compile's static temps / staticuint64s +// for non-direct interface data. Integers and bools stay IfaceIndir (data is +// a pointer to the copy), so reflect and equality keep Go's layout. +func (b Builder) staticIfaceBox(typ Type, x Expr) (llvm.Value, bool) { + if x.impl.IsNil() || x.impl.IsAConstant().IsNil() { + return llvm.Value{}, false + } + if box, ok := b.Pkg.ifaceBoxByValue[x.impl]; ok { + return box, true + } + prog := b.Prog + // Zero-sized values alias the shared module sentinel. Mutating that + // global's const/linkage would affect every zero-sized alloc in the package. + if prog.td.TypeAllocSize(prog.storageType(typ)) == 0 { + return llvm.Value{}, false + } + typeName, _ := prog.abi.TypeName(typ.raw.Type) + // Hash the constant's bits, not LLVM's printed form. Value.String() uses + // module-local names like @0, so same-length strings in different + // packages collided under Windows COMDAT any and mixed panic payloads. + sum := sha256.Sum256([]byte(typeName + "\x00" + constantFingerprint(x.impl))) + name := "_llgo_ifacebox$" + base64.RawURLEncoding.EncodeToString(sum[:]) + if g := b.Pkg.VarOf(name); g != nil { + b.Pkg.cacheIfaceBox(x.impl, g.impl) + return g.impl, true } - // Keep the otherwise-dormant template through package optimization without - // perturbing function IR. The LTO plugin removes this compiler.used entry - // after using the template as a compile-time devirtualization certificate. - b.Pkg.markLLVMUsed(global.impl) - return Expr{global.impl, prog.Pointer(prog.rtType("Itab"))}, true + g := b.Pkg.NewVarEx(name, prog.Pointer(typ)) + if g.isZeroSizedAlias || g.impl.IsNil() { + return llvm.Value{}, false + } + g.Init(x) + g.impl.SetGlobalConstant(true) + g.impl.SetUnnamedAddr(true) + b.Pkg.setODRLinkage(g.impl, llvm.WeakODRLinkage) + b.Pkg.cacheIfaceBox(x.impl, g.impl) + return g.impl, true +} + +func (p Package) cacheIfaceBox(val, box llvm.Value) { + if p.ifaceBoxByValue == nil { + p.ifaceBoxByValue = make(map[llvm.Value]llvm.Value) + } + p.ifaceBoxByValue[val] = box +} + +func constantFingerprint(v llvm.Value) string { + if v.IsNil() { + return "-" + } + if !v.IsAConstantInt().IsNil() { + return "i" + strconv.FormatUint(v.ZExtValue(), 16) + } + if !v.IsAConstantFP().IsNil() { + value, _ := v.DoubleValue() + switch v.Type().TypeKind() { + case llvm.FloatTypeKind: + return "f32:" + strconv.FormatUint(uint64(math.Float32bits(float32(value))), 16) + case llvm.DoubleTypeKind: + return "f64:" + strconv.FormatUint(math.Float64bits(value), 16) + } + } + if !v.IsAConstantPointerNull().IsNil() { + return "n" + } + if !v.IsAConstantAggregateZero().IsNil() { + return "z" + } + if !v.IsAFunction().IsNil() { + return "f:" + v.Name() + } + if !v.IsAGlobalVariable().IsNil() { + if init := v.Initializer(); !init.IsNil() { + if init.IsConstantString() { + return "b:" + init.ConstGetAsString() + } + return "g{" + constantFingerprint(init) + "}" + } + return "gn:" + v.Name() + } + n := v.OperandsCount() + if n == 0 { + if v.IsConstantString() { + return "b:" + v.ConstGetAsString() + } + return "?" + } + parts := make([]string, n) + for i := 0; i < n; i++ { + parts[i] = constantFingerprint(v.Operand(i)) + } + return "(" + strings.Join(parts, ",") + ")" } func (b Builder) unsafeInterface(rawIntf *types.Interface, concrete types.Type, t Expr, data llvm.Value) llvm.Value { @@ -130,13 +239,29 @@ func (b Builder) unsafeInterface(rawIntf *types.Interface, concrete types.Type, return b.unsafeEface(t.impl, data) } tintf := b.abiType(rawIntf) - // Emit a constant template for LTO analysis. Keep the runtime NewItab call - // even after devirtualization so dynamically-created interfaces continue to - // share the runtime's canonical itab pointer. Every template disappears - // before GlobalDCE. - b.staticItab(rawIntf, concrete, tintf, t) - itab := b.newItab(tintf, t) - return b.unsafeIface(itab.impl, data) + itab, ok := b.staticItab(rawIntf, concrete, tintf, t) + if ok && b.Prog.useRuntimeStaticItab() { + return b.unsafeIface(itab.impl, data) + } + dyn := b.newItab(tintf, t) + return b.unsafeIface(dyn.impl, data) +} + +// typeHashFromGlobal reads Hash (field 2 of abi.Type) from a type descriptor +// global so the itab matches NewItab's typ.Hash. +func typeHashFromGlobal(typ llvm.Value) (uint32, bool) { + if typ.IsNil() || typ.IsAGlobalVariable().IsNil() { + return 0, false + } + init := typ.Initializer() + if init.IsNil() || init.OperandsCount() <= 2 { + return 0, false + } + h := init.Operand(2) + if h.IsNil() || h.IsAConstantInt().IsNil() { + return 0, false + } + return uint32(h.ZExtValue()), true } func iMethodOf(rawIntf *types.Interface, method *types.Func) int { @@ -238,6 +363,9 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { b.recordUseIface(typ) tabi := b.abiType(typ.raw.Type) if !directIfaceType(typ.raw.Type) { + if box, ok := b.staticIfaceBox(typ, x); ok { + return Expr{b.unsafeInterface(rawIntf, typ.raw.Type, tabi, box), tinter} + } vptr := b.AllocU(typ) b.Store(vptr, x) return Expr{b.unsafeInterface(rawIntf, typ.raw.Type, tabi, vptr.impl), tinter} @@ -245,6 +373,9 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { kind, _, lvl := abi.DataKindOf(typ.raw.Type, 0, prog.is32Bits) switch kind { case abi.Indirect: + if box, ok := b.staticIfaceBox(typ, x); ok { + return Expr{b.unsafeInterface(rawIntf, typ.raw.Type, tabi, box), tinter} + } vptr := b.AllocU(typ) b.Store(vptr, x) return Expr{b.unsafeInterface(rawIntf, typ.raw.Type, tabi, vptr.impl), tinter} diff --git a/ssa/package.go b/ssa/package.go index 436680ef48..e3df16fbe3 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -430,6 +430,12 @@ func (p Program) DeadcodeDropEnabled() bool { return p.enableDeadcodeDrop } +// useRuntimeStaticItab reports whether T2I can use a compiler-emitted itab as +// the live vtable. LTO and deadcode-drop keep Fun[] from pinning unused methods. +func (p Program) useRuntimeStaticItab() bool { + return !p.enableLTOPluginMarker && !p.enableDeadcodeDrop +} + // DisableBoundsChecks controls index, slice, and slice-to-array conversion // bounds checks. Other dynamic validity checks, including nil pointer and // unsafe builtin checks, are not affected. @@ -987,6 +993,7 @@ type aPackage struct { llvmUsedValues []llvm.Value abiTypeFakeUseCache map[llvm.Value][]llvm.Value + ifaceBoxByValue map[llvm.Value]llvm.Value wasmReflectBridges map[string]wasmReflectBridgePair } diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 25ab6e1e14..c39eae8bff 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -22,6 +22,7 @@ import ( "go/importer" "go/token" "go/types" + "math" "os" "reflect" "runtime" @@ -743,8 +744,8 @@ func TestDevLTOGlobalDCEConcreteInterfaceEmitsStaticItabTemplate(t *testing.T) { t.Fatal("unassignable concrete type emitted a static itab template") } prog.EnableLTOPluginMarkers(false) - if _, ok := b.staticItab(intf, concrete, b.abiType(intf), b.abiType(concrete)); ok { - t.Fatal("static itab template emitted without LTO plugin markers") + if _, ok := b.staticItab(intf, concrete, b.abiType(intf), b.abiType(concrete)); !ok { + t.Fatal("runtime static itab was not emitted without LTO plugin markers") } prog.EnableLTOPluginMarkers(true) noInterface := types.NewNamed( @@ -768,7 +769,6 @@ func TestDevLTOGlobalDCEConcreteInterfaceEmitsStaticItabTemplate(t *testing.T) { interfaceTypeID := prog.interfaceCapabilityKey(intf) for _, want := range []string{ `_llgo_itab$`, - `@llvm.compiler.used`, `!"go.method.M:func()"`, `!"go.method.N:func()"`, `!llgo.static.itab.slot`, @@ -782,7 +782,10 @@ func TestDevLTOGlobalDCEConcreteInterfaceEmitsStaticItabTemplate(t *testing.T) { } } if !strings.Contains(ir, `call ptr @"github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab"`) { - t.Fatalf("static itab template replaced NewItab before LTO proof:\n%s", ir) + t.Fatalf("LTO T2I dropped NewItab; method-drop DCE needs the runtime call:\n%s", ir) + } + if !strings.Contains(ir, `@llvm.compiler.used`) { + t.Fatalf("missing compiler.used for LTO itab template:\n%s", ir) } for _, typeID := range []string{ prog.interfaceMethodCapabilityKey(intf, 0), @@ -798,6 +801,380 @@ func TestDevLTOGlobalDCEConcreteInterfaceEmitsStaticItabTemplate(t *testing.T) { } } +func TestDeadcodeDropT2IUsesNewItab(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.EnableDeadcodeDrop(true) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkgTypes := types.NewPackage("example.com/deadcodedrop", "deadcodedrop") + concrete := types.NewNamed( + types.NewTypeName(token.NoPos, pkgTypes, "T", nil), + types.NewStruct(nil, nil), nil) + recv := types.NewVar(token.NoPos, pkgTypes, "", concrete) + methodSig := types.NewSignatureType(recv, nil, nil, nil, nil, false) + concrete.AddMethod(types.NewFunc(token.NoPos, pkgTypes, "Read", methodSig)) + concrete.AddMethod(types.NewFunc(token.NoPos, pkgTypes, "Write", methodSig)) + interfaceMethodSig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + intf := types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, pkgTypes, "Read", interfaceMethodSig), + types.NewFunc(token.NoPos, pkgTypes, "Write", interfaceMethodSig), + }, nil) + intf.Complete() + + pkg := prog.NewPackage("deadcodedrop", pkgTypes.Path()) + returns := types.NewTuple(types.NewVar(token.NoPos, nil, "", intf)) + fn := pkg.NewFunc("Make", types.NewSignatureType(nil, nil, nil, nil, returns, false), InGo) + b := fn.MakeBody(1) + b.Return(b.MakeInterface(prog.Type(intf, InGo), prog.Zero(prog.Type(concrete, InGo)))) + if _, ok := b.staticItab(intf, concrete, b.abiType(intf), b.abiType(concrete)); ok { + t.Fatal("deadcode-drop T2I emitted a static itab vtable") + } + b.EndBuild() + + ir := pkg.String() + if strings.Contains(ir, `_llgo_itab$`) { + t.Fatalf("deadcode-drop T2I emitted a static itab global:\n%s", ir) + } + if !strings.Contains(ir, `call ptr @"github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab"`) { + t.Fatalf("deadcode-drop T2I dropped NewItab:\n%s", ir) + } +} + +func TestTypeHashFromGlobal(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("thash", "thash") + + if _, ok := typeHashFromGlobal(llvm.Value{}); ok { + t.Fatal("nil value produced a hash") + } + if _, ok := typeHashFromGlobal(prog.IntVal(1, prog.Int()).impl); ok { + t.Fatal("non-global produced a hash") + } + g := pkg.NewVarEx("noinit", prog.Pointer(prog.Int())) + if _, ok := typeHashFromGlobal(g.impl); ok { + t.Fatal("uninitialized global produced a hash") + } + + two := prog.rawType(types.NewStruct([]*types.Var{ + types.NewVar(token.NoPos, nil, "a", types.Typ[types.Int]), + types.NewVar(token.NoPos, nil, "b", types.Typ[types.Int]), + }, nil)) + g2 := pkg.NewVarEx("two", prog.Pointer(two)) + g2.impl.SetInitializer(prog.constStructValue(two, []llvm.Value{ + prog.IntVal(1, prog.Int()).impl, + prog.IntVal(2, prog.Int()).impl, + })) + if _, ok := typeHashFromGlobal(g2.impl); ok { + t.Fatal("two-field struct produced a hash") + } + + ptr := prog.VoidPtr() + null := llvm.ConstPointerNull(prog.tyVoidPtr()) + bad := prog.rawType(types.NewStruct([]*types.Var{ + types.NewVar(token.NoPos, nil, "a", ptr.RawType()), + types.NewVar(token.NoPos, nil, "b", ptr.RawType()), + types.NewVar(token.NoPos, nil, "c", ptr.RawType()), + }, nil)) + g3 := pkg.NewVarEx("badhash", prog.Pointer(bad)) + g3.impl.SetInitializer(prog.constStructValue(bad, []llvm.Value{null, null, null})) + if _, ok := typeHashFromGlobal(g3.impl); ok { + t.Fatal("non-int hash field produced a hash") + } + + okTy := prog.rawType(types.NewStruct([]*types.Var{ + types.NewVar(token.NoPos, nil, "a", ptr.RawType()), + types.NewVar(token.NoPos, nil, "b", ptr.RawType()), + types.NewVar(token.NoPos, nil, "h", types.Typ[types.Uint32]), + }, nil)) + g4 := pkg.NewVarEx("okhash", prog.Pointer(okTy)) + g4.impl.SetInitializer(prog.constStructValue(okTy, []llvm.Value{ + null, null, prog.IntVal(0x11, prog.Uint32()).impl, + })) + h, ok := typeHashFromGlobal(g4.impl) + if !ok || h != 0x11 { + t.Fatalf("hash=%#x ok=%v", h, ok) + } +} + +func TestRuntimeStaticItabUsesVtableAndInitItabs(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkgTypes := types.NewPackage("example.com/runtimeitab", "runtimeitab") + concrete := types.NewNamed( + types.NewTypeName(token.NoPos, pkgTypes, "T", nil), + types.NewStruct(nil, nil), nil) + recv := types.NewVar(token.NoPos, pkgTypes, "", concrete) + methodSig := types.NewSignatureType(recv, nil, nil, nil, nil, false) + concrete.AddMethod(types.NewFunc(token.NoPos, pkgTypes, "M", methodSig)) + interfaceMethodSig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + intf := types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, pkgTypes, "M", interfaceMethodSig), + }, nil) + intf.Complete() + + pkg := prog.NewPackage("runtimeitab", pkgTypes.Path()) + returns := types.NewTuple(types.NewVar(token.NoPos, nil, "", intf)) + fn := pkg.NewFunc("Make", types.NewSignatureType(nil, nil, nil, nil, returns, false), InGo) + b := fn.MakeBody(1) + tabi := b.abiType(intf) + tcon := b.abiType(concrete) + itab1, ok1 := b.staticItab(intf, concrete, tabi, tcon) + itab2, ok2 := b.staticItab(intf, concrete, tabi, tcon) + if !ok1 || !ok2 || itab1.impl != itab2.impl { + t.Fatal("repeated T2I did not reuse the static itab global") + } + b.Return(b.MakeInterface(prog.Type(intf, InGo), prog.Zero(prog.Type(concrete, InGo)))) + b.EndBuild() + + ir := pkg.String() + if !strings.Contains(ir, `_llgo_itab$`) { + t.Fatalf("missing static itab global:\n%s", ir) + } + if strings.Contains(ir, `call ptr @"github.com/xgo-dev/llgo/runtime/internal/runtime.NewItab"`) { + t.Fatalf("runtime T2I still called NewItab:\n%s", ir) + } + if strings.Contains(ir, `RegisterStaticItab`) { + t.Fatalf("T2I must not register itabs from package init:\n%s", ir) + } +} + +func TestStaticIfaceBoxFingerprintsDistinguishSameLengthStrings(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackage("boxfp", "boxfp") + empty := types.NewInterfaceType(nil, nil) + empty.Complete() + rets := types.NewTuple( + types.NewVar(token.NoPos, nil, "", empty), + types.NewVar(token.NoPos, nil, "", empty), + ) + fn := pkg.NewFunc("F", types.NewSignatureType(nil, nil, nil, nil, rets, false), InGo) + b := fn.MakeBody(1) + tany := prog.Type(empty, InGo) + e1 := b.MakeInterface(tany, pkg.ConstString(strings.Repeat("a", 28))) + e2 := b.MakeInterface(tany, pkg.ConstString(strings.Repeat("b", 28))) + b.Return(e1, e2) + b.EndBuild() + ir := pkg.String() + seen := map[string]bool{} + for _, line := range strings.Split(ir, "\n") { + if i := strings.Index(line, `@"_llgo_ifacebox$`); i >= 0 { + rest := line[i+2:] + end := strings.Index(rest, `"`) + if end > 0 { + seen[rest[:end]] = true + } + } + } + if len(seen) < 2 { + t.Fatalf("same-length strings shared an ifacebox name: %v\n%s", seen, ir) + } +} + +func TestConstantFingerprintFloats(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + + f32 := ctx.FloatType() + f64 := ctx.DoubleType() + tests := []struct { + name string + x llvm.Value + y llvm.Value + }{ + {"float32 values", llvm.ConstFloat(f32, 0), llvm.ConstFloat(f32, 3.14)}, + {"float64 values", llvm.ConstFloat(f64, 0), llvm.ConstFloat(f64, 3.14)}, + {"signed float32 zero", llvm.ConstFloat(f32, 0), llvm.ConstFloat(f32, math.Copysign(0, -1))}, + {"signed float64 zero", llvm.ConstFloat(f64, 0), llvm.ConstFloat(f64, math.Copysign(0, -1))}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if x, y := constantFingerprint(test.x), constantFingerprint(test.y); x == y { + t.Fatalf("fingerprints collide: %q", x) + } + }) + } + + c1 := ctx.ConstStruct([]llvm.Value{llvm.ConstFloat(f64, 1), llvm.ConstFloat(f64, 2)}, false) + c2 := ctx.ConstStruct([]llvm.Value{llvm.ConstFloat(f64, 1), llvm.ConstFloat(f64, 3)}, false) + if x, y := constantFingerprint(c1), constantFingerprint(c2); x == y { + t.Fatalf("complex fingerprints collide: %q", x) + } +} + +func TestStaticIfaceBoxReusesLLVMValue(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackage("boxreuse", "boxreuse") + empty := types.NewInterfaceType(nil, nil) + empty.Complete() + rets := types.NewTuple(types.NewVar(token.NoPos, nil, "", empty)) + fn := pkg.NewFunc("F", types.NewSignatureType(nil, nil, nil, nil, rets, false), InGo) + b := fn.MakeBody(1) + tany := prog.Type(empty, InGo) + c := prog.IntVal(42, prog.Int64()) + e1 := b.MakeInterface(tany, c) + e2 := b.MakeInterface(tany, c) + if len(pkg.ifaceBoxByValue) != 1 { + t.Fatalf("pointer cache size = %d, want 1", len(pkg.ifaceBoxByValue)) + } + b.Return(e1) + _ = e2 + b.EndBuild() + ir := pkg.String() + seen := map[string]bool{} + for _, line := range strings.Split(ir, "\n") { + if i := strings.Index(line, `@"_llgo_ifacebox$`); i >= 0 { + rest := line[i+2:] + end := strings.Index(rest, `"`) + if end > 0 { + seen[rest[:end]] = true + } + } + } + if len(seen) != 1 { + t.Fatalf("repeated boxing of the same constant created %d boxes: %v\n%s", len(seen), seen, ir) + } +} + +func TestConstantFingerprintKinds(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("fpkinds") + defer mod.Dispose() + + var none llvm.Value + if got := constantFingerprint(none); got != "-" { + t.Fatalf("nil fingerprint = %q, want -", got) + } + + ptr := llvm.PointerType(ctx.Int8Type(), 0) + if got := constantFingerprint(llvm.ConstPointerNull(ptr)); got != "n" { + t.Fatalf("null fingerprint = %q, want n", got) + } + + fn := llvm.AddFunction(mod, "fp.fn", llvm.FunctionType(ctx.VoidType(), nil, false)) + if got := constantFingerprint(fn); got != "f:fp.fn" { + t.Fatalf("function fingerprint = %q, want f:fp.fn", got) + } + + g := llvm.AddGlobal(mod, ctx.Int32Type(), "fp.g") + if got := constantFingerprint(g); got != "gn:fp.g" { + t.Fatalf("uninitialized global fingerprint = %q, want gn:fp.g", got) + } + + g.SetInitializer(llvm.ConstInt(ctx.Int32Type(), 7, false)) + if got := constantFingerprint(g); got != "g{i7}" { + t.Fatalf("initialized global fingerprint = %q, want g{i7}", got) + } + + sg := llvm.AddGlobal(mod, llvm.ArrayType(ctx.Int8Type(), 2), "fp.s") + sg.SetInitializer(ctx.ConstString("hi", false)) + if got := constantFingerprint(sg); got != "b:hi" { + t.Fatalf("string global fingerprint = %q, want b:hi", got) + } + + str := ctx.ConstString("ab", false) + if got := constantFingerprint(str); got != "b:ab" { + t.Fatalf("constant string fingerprint = %q, want b:ab", got) + } + + if got := constantFingerprint(llvm.Undef(ctx.Int32Type())); got != "?" { + t.Fatalf("undef fingerprint = %q, want ?", got) + } +} + +func TestStaticIfaceBoxReusesByName(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackage("boxname", "boxname") + empty := types.NewInterfaceType(nil, nil) + empty.Complete() + rets := types.NewTuple(types.NewVar(token.NoPos, nil, "", empty)) + fn := pkg.NewFunc("F", types.NewSignatureType(nil, nil, nil, nil, rets, false), InGo) + b := fn.MakeBody(1) + tany := prog.Type(empty, InGo) + first := b.MakeInterface(tany, prog.IntVal(42, prog.Int64())) + pkg.ifaceBoxByValue = nil + second := b.MakeInterface(tany, prog.IntVal(42, prog.Int64())) + if first.impl.IsNil() || second.impl.IsNil() { + t.Fatal("MakeInterface returned a nil iface") + } + if len(pkg.ifaceBoxByValue) != 1 { + t.Fatalf("name-cache reuse size = %d, want 1", len(pkg.ifaceBoxByValue)) + } + b.Return(first) + b.EndBuild() + ir := pkg.String() + if strings.Count(ir, `_llgo_ifacebox$`) == 0 { + t.Fatalf("missing ifacebox after VarOf reuse:\n%s", ir) + } +} + +func TestStaticIfaceBoxSkipsZeroSizedTypes(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackage("boxzero", "boxzero") + empty := types.NewInterfaceType(nil, nil) + empty.Complete() + z := types.NewStruct(nil, nil) + rets := types.NewTuple(types.NewVar(token.NoPos, nil, "", empty)) + fn := pkg.NewFunc("F", types.NewSignatureType(nil, nil, nil, nil, rets, false), InGo) + b := fn.MakeBody(1) + e := b.MakeInterface(prog.Type(empty, InGo), prog.Zero(prog.Type(z, InGo))) + b.Return(e) + b.EndBuild() + zero := pkg.Module().NamedGlobal(moduleZeroName) + if !zero.IsNil() && zero.Linkage() == llvm.WeakODRLinkage { + t.Fatal("zero-sized iface box mutated the shared sentinel linkage") + } + ir := pkg.String() + if strings.Contains(ir, `_llgo_ifacebox$`) { + t.Fatalf("zero-sized value used an ifacebox:\n%s", ir) + } +} + func TestDevLTOGlobalDCEReflectMethodByNameCallMarkers(t *testing.T) { requireGoGlobalDCE(t) From 4f1be2eba56109f5364c1c7bcb53cd8916e3ced2 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 29 Sep 2026 21:11:23 +0800 Subject: [PATCH 2/2] cl: fold trivial constant iface constructors A call whose SSA body is MakeInterface of a Convert/ChangeType of a constant parameter is lowered at the call site to MakeInterface of that concrete value. No function-name matching: constant.MakeInt64 and user helpers such as boxMyInt(x int64) any { return myInt(x) } use the same path. Depends on static itabs and iface boxes so the folded value is a compile-time {itab, box} pair. --- cl/_testrt/constiface/in.go | 51 +++++++++++++++ cl/constiface.go | 120 ++++++++++++++++++++++++++++++++++++ cl/instr.go | 5 ++ 3 files changed, 176 insertions(+) create mode 100644 cl/_testrt/constiface/in.go create mode 100644 cl/constiface.go diff --git a/cl/_testrt/constiface/in.go b/cl/_testrt/constiface/in.go new file mode 100644 index 0000000000..1dd980fa00 --- /dev/null +++ b/cl/_testrt/constiface/in.go @@ -0,0 +1,51 @@ +// LITTEST +// Scope: common +package main + +import "go/constant" + +type myInt int64 + +func boxMyInt(x int64) any { return myInt(x) } + +// CHECK-LABEL: define {{.*}} @main.boxed(){{.*}} { +// CHECK-NOT: MakeInt64 +// CHECK-NOT: MakeBool +// CHECK-NOT: boxMyInt +// CHECK-NOT: AllocU +// CHECK: _llgo_itab$ +// CHECK: _llgo_ifacebox$ +func boxed() (constant.Value, constant.Value, any) { + return constant.MakeInt64(42), constant.MakeBool(true), boxMyInt(7) +} + +// Convert changes the value; do not fold the raw constant as the result type. +func boxRune(x int32) any { return any(string(x)) } + +// CHECK-LABEL: define void @main.main(){{.*}} { +// CHECK: call {{.*}} @main.boxRune +func main() { + i, b, m := boxed() + n, ok := constant.Int64Val(i) + if !ok || n != 42 || constant.BoolVal(b) != true { + panic("const iface") + } + if m.(myInt) != 7 { + panic(m) + } + s := constant.MakeString("hi") + if constant.StringVal(s) != "hi" { + panic(s) + } + e := constant.MakeString("") + if constant.StringVal(e) != "" { + panic("empty") + } + if constant.MakeInt64(42) != i { + panic("int64 equality") + } + if boxRune(65).(string) != "A" { + panic("convert fold") + } + println("ok") +} diff --git a/cl/constiface.go b/cl/constiface.go new file mode 100644 index 0000000000..910c7f3f5b --- /dev/null +++ b/cl/constiface.go @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cl + +import ( + llssa "github.com/xgo-dev/llgo/ssa" + "go/types" + + "golang.org/x/tools/go/ssa" +) + +// foldConstantMakeValue inlines a trivial "box a parameter as an interface" +// function when the argument is a compile-time constant. The callee is +// recognized by its SSA body (ChangeType/ChangeInterface of a param, then +// MakeInterface, then return), not by name. That matches cmd/compile +// inlining constructors such as constant.MakeInt64. Convert is excluded: +// it changes the value (int32→string, narrowing) and cannot reuse the +// argument's raw constant. +func (p *context) foldConstantMakeValue(b llssa.Builder, fn *ssa.Function, call *ssa.CallCommon) (llssa.Expr, bool) { + param, concrete, ok := analyzeTrivialIfaceBox(fn) + if !ok { + return llssa.Expr{}, false + } + idx := -1 + for i, p := range fn.Params { + if p == param { + idx = i + break + } + } + if idx < 0 || idx >= len(call.Args) { + return llssa.Expr{}, false + } + c, ok := call.Args[idx].(*ssa.Const) + if !ok || c.Value == nil { + return llssa.Expr{}, false + } + x := b.Const(c.Value, p.type_(concrete, llssa.InGo)) + retTy := p.type_(call.Signature().Results().At(0).Type(), llssa.InGo) + return b.MakeInterface(retTy, x), true +} + +// analyzeTrivialIfaceBox reports that fn is a single-block function whose +// result is MakeInterface of a parameter (optionally after ChangeType +// /ChangeInterface). The concrete type is the value being boxed. +func analyzeTrivialIfaceBox(fn *ssa.Function) (*ssa.Parameter, types.Type, bool) { + if fn == nil || fn.Signature.Recv() != nil || fn.Recover != nil || fn.Synthetic != "" { + return nil, nil, false + } + if hasNoInlineDirective(fn) { + return nil, nil, false + } + if len(fn.Blocks) != 1 { + return nil, nil, false + } + blk := fn.Blocks[0] + var ret *ssa.Return + for _, instr := range blk.Instrs { + r, ok := instr.(*ssa.Return) + if !ok { + continue + } + if ret != nil { + return nil, nil, false + } + ret = r + } + if ret == nil || len(ret.Results) != 1 { + return nil, nil, false + } + mi, ok := ret.Results[0].(*ssa.MakeInterface) + if !ok { + return nil, nil, false + } + chain := map[ssa.Instruction]bool{ret: true, mi: true} + v := mi.X + for { + switch t := v.(type) { + case *ssa.Parameter: + if blockHasExtraInstrs(blk, chain) { + return nil, nil, false + } + return t, mi.X.Type(), true + case *ssa.ChangeType: + chain[t] = true + v = t.X + case *ssa.ChangeInterface: + chain[t] = true + v = t.X + default: + return nil, nil, false + } + } +} + +func blockHasExtraInstrs(blk *ssa.BasicBlock, chain map[ssa.Instruction]bool) bool { + for _, instr := range blk.Instrs { + if _, ok := instr.(*ssa.DebugRef); ok { + continue + } + if !chain[instr] { + return true + } + } + return false +} diff --git a/cl/instr.go b/cl/instr.go index 6c168182e2..e4622cdfb9 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -2645,6 +2645,11 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm args := p.compileValues(b, args, kind) ret = p.emitDo(b, act, ds, false, llssa.Builtin(fn), llssa.Builder.Call, args...) case *ssa.Function: + if act == llssa.Call { + if folded, ok := p.foldConstantMakeValue(b, cv, call); ok { + return folded + } + } aFn, pyFn, ftype := p.compileFunction(cv) // TODO(xsw): check ca != llssa.Call switch ftype {