From 9a1095c1ba6f54c6528d91300eafd105081aa47f Mon Sep 17 00:00:00 2001 From: ebak Date: Mon, 6 Jul 2026 20:14:56 +0200 Subject: [PATCH 1/4] More verbose -print-allocs messages. Initial commit. --- transform/allocs.go | 58 ++++++++++++++++++++++++++-- transform/testdata/allocs2.go | 27 +++++++------ transform/testdata/allocs2.out.cover | 23 +++++------ 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/transform/allocs.go b/transform/allocs.go index 5c1021a3fb..a2adbf5f81 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -57,7 +57,9 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u if heapalloc.Operand(0).IsAConstantInt().IsNil() { // Do not allocate variable length arrays on the stack. if logAllocs { - logger(getPosition(heapalloc), "size is not constant") + logger( + getPosition(heapalloc), + fmt.Sprintf("%s size is not constant", getID(heapalloc))) } continue } @@ -67,7 +69,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u // The maximum size for a stack allocation. if logAllocs { logger(getPosition(heapalloc), - fmt.Sprintf("object size %d exceeds maximum stack allocation size %d", size, maxStackAlloc)) + fmt.Sprintf("%s size %d exceeds maximum stack allocation size %d", getID(heapalloc), size, maxStackAlloc)) } continue } @@ -104,7 +106,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u if atPos.Line != 0 { msg = fmt.Sprintf("escapes at line %d", atPos.Line) } - logger(getPosition(heapalloc), msg) + logger(getPosition(heapalloc), fmt.Sprintf("%s %s", getID(heapalloc), msg)) } continue } @@ -316,3 +318,53 @@ func lineLengthAt(filename string, lineNumber int) int { } return 0 } + +func getID(v llvm.Value) string { + id := v.Name() + if len(id) == 0 { + if v.InstructionOpcode() == llvm.Call && (!v.IsAAllocaInst().IsNil() || !v.IsACallInst().IsNil() || + !v.IsAInvokeInst().IsNil()) { + id = v.AllocatedType().String() + } + } + if id == "complit" { + // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) + /* + %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 + %0 = getelementptr inbounds nuw i8, ptr %complit, i32 4, !dbg !53 + %1 = getelementptr inbounds nuw i8, ptr %complit, i32 8, !dbg !53 + store float 4.000000e+00, ptr %complit, align 4, !dbg !54 + store float 5.000000e+00, ptr %0, align 4, !dbg !55 + store float 6.000000e+00, ptr %1, align 4, !dbg !56 + %2 = call ptr @main.scaleVector3(ptr nonnull %complit, float 5.000000e-01, ptr undef), !dbg !57 + store ptr %2, ptr @main.escapedVector3, align 4, !dbg !60 + ret void, !dbg !61 + */ + /* + * 1. Find the next "call", where %complit is passed as parameter. + * 2. Produce ID: "Arg 0 of main.scaleVector3() call" // escapes at ... + */ + n := v + InstLoop: + for _ = range 64 { + n = llvm.NextInstruction(n) + if n.IsNil() { + break + } + if call := n.IsACallInst(); !call.IsNil() { + argIdx := 0 + for argIdx = range call.OperandsCount() { + argV := call.Operand(argIdx) + if argV.Name() == "complit" { + id = fmt.Sprintf("Arg %d of %s() call", argIdx, call.CalledValue().Name()) + break InstLoop + } + } + } + } + } + if len(id) == 0 { + id = v.String() + } + return id +} diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index b8131d821e..02f015bbfd 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -21,19 +21,19 @@ func main() { s3 := make([]int, 3) returnIntSlice(s3) - useSlice(make([]int, getUnknownNumber())) // OUT: size is not constant + useSlice(make([]int, getUnknownNumber())) // OUT: makeslice.buf size is not constant - s4 := make([]byte, 300) // OUT: object size 300 exceeds maximum stack allocation size 256 + s4 := make([]byte, 300) // OUT: makeslice4 size 300 exceeds maximum stack allocation size 256 readByteSlice(s4) - s5 := make([]int, 4) // OUT: escapes at line 30 + s5 := make([]int, 4) // OUT: makeslice6 escapes at line 30 _ = append(s5, 5) s6 := make([]int, 3) s7 := []int{1, 2, 3} copySlice(s6, s7) - c1 := getComplex128() // OUT: escapes at line 37 + c1 := getComplex128() // OUT: FloatType escapes at line 37 useInterface(c1) n3 := 5 @@ -41,13 +41,13 @@ func main() { return n3 }() - callVariadic(3, 5, 8) // OUT: escapes at line 44 + callVariadic(3, 5, 8) // OUT: varargs12 escapes at line 44 - s8 := []int{3, 5, 8} // OUT: escapes at line 47 + s8 := []int{3, 5, 8} // OUT: slicelit14 escapes at line 47 callVariadic(s8...) - n4 := 3 // OUT: escapes at line 51 - n5 := 7 // OUT: escapes at line 51 + n4 := 3 // OUT: n4 escapes at line 51 + n5 := 7 // OUT: n5 escapes at line 51 func() { n4 = n5 }() @@ -104,19 +104,24 @@ func nonEscapingReturnedPointer() vector3 { var escapedSlice []int func escapingReturnedSlice() { - s := make([]int, 3) // OUT: escapes at line 108 + s := make([]int, 3) // OUT: makeslice escapes at line 108 escapedSlice = returnIntSlice(s) } var escapedVector3 *vector3 func escapingReturnedPointer() { - b := vector3{4, 5, 6} // OUT: escapes at line 117 + b := vector3{4, 5, 6} // OUT: b escapes at line 117 c := scaleVector3(&b, 0.5) escapedVector3 = c } +func escapingReturnedPointer2() { + c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() call escapes at line 122 + escapedVector3 = c +} + func recursiveScaleVector3(vec *vector3, n int) *vector3 { if n == 0 { return vec @@ -125,7 +130,7 @@ func recursiveScaleVector3(vec *vector3, n int) *vector3 { } func recursiveReturnedPointer() vector3 { - b := vector3{4, 5, 6} // OUT: escapes at unknown line + b := vector3{4, 5, 6} // OUT: b escapes at unknown line c := recursiveScaleVector3(&b, 1) return *c diff --git a/transform/testdata/allocs2.out.cover b/transform/testdata/allocs2.out.cover index e17f3d421e..e5c3dfff5f 100644 --- a/transform/testdata/allocs2.out.cover +++ b/transform/testdata/allocs2.out.cover @@ -1,11 +1,12 @@ -testdata/allocs2.go:24.1,24.72 1 0 -testdata/allocs2.go:26.1,26.91 1 0 -testdata/allocs2.go:29.1,29.49 1 0 -testdata/allocs2.go:36.1,36.50 1 0 -testdata/allocs2.go:44.1,44.50 1 0 -testdata/allocs2.go:46.1,46.49 1 0 -testdata/allocs2.go:49.1,49.36 1 0 -testdata/allocs2.go:50.1,50.36 1 0 -testdata/allocs2.go:107.1,107.49 1 0 -testdata/allocs2.go:114.1,114.51 1 0 -testdata/allocs2.go:128.1,128.55 1 0 +testdata/allocs2.go:24.1,24.86 1 0 +testdata/allocs2.go:26.1,26.95 1 0 +testdata/allocs2.go:29.1,29.60 1 0 +testdata/allocs2.go:36.1,36.60 1 0 +testdata/allocs2.go:44.1,44.60 1 0 +testdata/allocs2.go:46.1,46.60 1 0 +testdata/allocs2.go:49.1,49.39 1 0 +testdata/allocs2.go:50.1,50.39 1 0 +testdata/allocs2.go:107.1,107.59 1 0 +testdata/allocs2.go:114.1,114.53 1 0 +testdata/allocs2.go:121.1,121.105 1 0 +testdata/allocs2.go:133.1,133.57 1 0 From 59d0c219f77ba183221bf3ee7b183285d677ff59 Mon Sep 17 00:00:00 2001 From: ebak Date: Tue, 7 Jul 2026 17:08:41 +0200 Subject: [PATCH 2/4] More verbose -print-allocs: Fix in complex literal handling. --- transform/allocs.go | 23 ++++++++++++++++------- transform/allocs_test.go | 20 ++++++++++++++++++-- transform/testdata/allocs2.go | 6 +++++- transform/testdata/allocs2.out.cover | 6 ++++-- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/transform/allocs.go b/transform/allocs.go index a2adbf5f81..dfe6ec51df 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -52,6 +52,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u heapallocs = append(heapallocs, getUses(allocator)...) } + idMiner := makeIdMiner() for _, heapalloc := range heapallocs { logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name()) if heapalloc.Operand(0).IsAConstantInt().IsNil() { @@ -59,7 +60,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u if logAllocs { logger( getPosition(heapalloc), - fmt.Sprintf("%s size is not constant", getID(heapalloc))) + fmt.Sprintf("%s size is not constant", idMiner.get(heapalloc))) } continue } @@ -69,7 +70,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u // The maximum size for a stack allocation. if logAllocs { logger(getPosition(heapalloc), - fmt.Sprintf("%s size %d exceeds maximum stack allocation size %d", getID(heapalloc), size, maxStackAlloc)) + fmt.Sprintf("%s size %d exceeds maximum stack allocation size %d", idMiner.get(heapalloc), size, maxStackAlloc)) } continue } @@ -106,7 +107,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u if atPos.Line != 0 { msg = fmt.Sprintf("escapes at line %d", atPos.Line) } - logger(getPosition(heapalloc), fmt.Sprintf("%s %s", getID(heapalloc), msg)) + logger(getPosition(heapalloc), fmt.Sprintf("%s %s", idMiner.get(heapalloc), msg)) } continue } @@ -319,7 +320,15 @@ func lineLengthAt(filename string, lineNumber int) int { return 0 } -func getID(v llvm.Value) string { +type idMiner struct { + complitReg *regexp.Regexp +} + +func makeIdMiner() idMiner { + return idMiner{complitReg: regexp.MustCompile(`^complit\d*$`)} +} + +func (this *idMiner) get(v llvm.Value) string { id := v.Name() if len(id) == 0 { if v.InstructionOpcode() == llvm.Call && (!v.IsAAllocaInst().IsNil() || !v.IsACallInst().IsNil() || @@ -327,7 +336,7 @@ func getID(v llvm.Value) string { id = v.AllocatedType().String() } } - if id == "complit" { + if this.complitReg.MatchString(id) { // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) /* %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 @@ -355,7 +364,7 @@ func getID(v llvm.Value) string { argIdx := 0 for argIdx = range call.OperandsCount() { argV := call.Operand(argIdx) - if argV.Name() == "complit" { + if argV.Name() == id { id = fmt.Sprintf("Arg %d of %s() call", argIdx, call.CalledValue().Name()) break InstLoop } @@ -364,7 +373,7 @@ func getID(v llvm.Value) string { } } if len(id) == 0 { - id = v.String() + id = "allocation" } return id } diff --git a/transform/allocs_test.go b/transform/allocs_test.go index 83ba90ffe8..794d717dde 100644 --- a/transform/allocs_test.go +++ b/transform/allocs_test.go @@ -60,8 +60,24 @@ func TestAllocs2(t *testing.T) { for i, line := range strings.Split(strings.ReplaceAll(string(testInput), "\r\n", "\n"), "\n") { const prefix = " // OUT: " if idx := strings.Index(line, prefix); idx > 0 { - msg := line[idx+len(prefix):] - fmt.Fprintf(&expectedTestOutput, "allocs2.go:%d: %s\n", i+1, msg) + msgLine := strings.TrimSpace(line[idx+len(prefix):]) + msgs := make([]string, 0, 2) + const separator = " && " + for true { + idx = strings.Index(msgLine, separator) + if idx < 0 { + break + } + msg := strings.TrimSpace(msgLine[:idx]) + msgs = append(msgs, msg) + msgLine = strings.TrimSpace(msgLine[idx+len(separator):]) + } + if len(msgLine) > 0 { + msgs = append(msgs, msgLine) + } + for _, msg := range msgs { + fmt.Fprintf(&expectedTestOutput, "allocs2.go:%d: %s\n", i+1, msg) + } } } diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index 02f015bbfd..1b2efd72ae 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -117,9 +117,13 @@ func escapingReturnedPointer() { escapedVector3 = c } +var escapedArray [2](*vector3) + func escapingReturnedPointer2() { - c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() call escapes at line 122 + c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() call escapes at line 124 escapedVector3 = c + a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: complit1 escapes at line 126 && complit2 escapes at line 126 + escapedArray = a } func recursiveScaleVector3(vec *vector3, n int) *vector3 { diff --git a/transform/testdata/allocs2.out.cover b/transform/testdata/allocs2.out.cover index e5c3dfff5f..a9e02123af 100644 --- a/transform/testdata/allocs2.out.cover +++ b/transform/testdata/allocs2.out.cover @@ -8,5 +8,7 @@ testdata/allocs2.go:49.1,49.39 1 0 testdata/allocs2.go:50.1,50.39 1 0 testdata/allocs2.go:107.1,107.59 1 0 testdata/allocs2.go:114.1,114.53 1 0 -testdata/allocs2.go:121.1,121.105 1 0 -testdata/allocs2.go:133.1,133.57 1 0 +testdata/allocs2.go:123.1,123.105 1 0 +testdata/allocs2.go:125.1,125.127 1 0 +testdata/allocs2.go:125.1,125.127 1 0 +testdata/allocs2.go:137.1,137.57 1 0 From 32be597a65989e54b1c16040ef5a03a34cdb4acc Mon Sep 17 00:00:00 2001 From: ebak Date: Sun, 12 Jul 2026 11:59:07 +0200 Subject: [PATCH 3/4] More verbose -print-allocs: better identification of heap allocated data --- transform/allocs.go | 60 +---- transform/idminer.go | 352 +++++++++++++++++++++++++++ transform/testdata/allocs2.go | 77 +++++- transform/testdata/allocs2.out.cover | 37 +-- 4 files changed, 442 insertions(+), 84 deletions(-) create mode 100644 transform/idminer.go diff --git a/transform/allocs.go b/transform/allocs.go index dfe6ec51df..bd8eaa17df 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -148,7 +148,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u // FormatAllocReason renders the heap allocation in a human-readable format. func FormatAllocReason(pos token.Position, reason string) string { - return fmt.Sprintf("%s: object allocated on the heap: %s", pos.String(), reason) + return fmt.Sprintf("%s: heap allocation: %s", pos.String(), reason) } // FormatAllocCover renders the heap allocation in the go coverage tool format. @@ -319,61 +319,3 @@ func lineLengthAt(filename string, lineNumber int) int { } return 0 } - -type idMiner struct { - complitReg *regexp.Regexp -} - -func makeIdMiner() idMiner { - return idMiner{complitReg: regexp.MustCompile(`^complit\d*$`)} -} - -func (this *idMiner) get(v llvm.Value) string { - id := v.Name() - if len(id) == 0 { - if v.InstructionOpcode() == llvm.Call && (!v.IsAAllocaInst().IsNil() || !v.IsACallInst().IsNil() || - !v.IsAInvokeInst().IsNil()) { - id = v.AllocatedType().String() - } - } - if this.complitReg.MatchString(id) { - // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) - /* - %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 - %0 = getelementptr inbounds nuw i8, ptr %complit, i32 4, !dbg !53 - %1 = getelementptr inbounds nuw i8, ptr %complit, i32 8, !dbg !53 - store float 4.000000e+00, ptr %complit, align 4, !dbg !54 - store float 5.000000e+00, ptr %0, align 4, !dbg !55 - store float 6.000000e+00, ptr %1, align 4, !dbg !56 - %2 = call ptr @main.scaleVector3(ptr nonnull %complit, float 5.000000e-01, ptr undef), !dbg !57 - store ptr %2, ptr @main.escapedVector3, align 4, !dbg !60 - ret void, !dbg !61 - */ - /* - * 1. Find the next "call", where %complit is passed as parameter. - * 2. Produce ID: "Arg 0 of main.scaleVector3() call" // escapes at ... - */ - n := v - InstLoop: - for _ = range 64 { - n = llvm.NextInstruction(n) - if n.IsNil() { - break - } - if call := n.IsACallInst(); !call.IsNil() { - argIdx := 0 - for argIdx = range call.OperandsCount() { - argV := call.Operand(argIdx) - if argV.Name() == id { - id = fmt.Sprintf("Arg %d of %s() call", argIdx, call.CalledValue().Name()) - break InstLoop - } - } - } - } - } - if len(id) == 0 { - id = "allocation" - } - return id -} diff --git a/transform/idminer.go b/transform/idminer.go new file mode 100644 index 0000000000..9b92ab03fe --- /dev/null +++ b/transform/idminer.go @@ -0,0 +1,352 @@ +package transform + +import ( + "fmt" + "regexp" + "strings" + "tinygo.org/x/go-llvm" +) + +type idMiner struct { + complitReg *regexp.Regexp + v llvm.Value +} + +func makeIdMiner() idMiner { + return idMiner{complitReg: regexp.MustCompile(`^complit\d*$`)} +} + +func (this *idMiner) get(v llvm.Value) string { + this.v = v + id := v.Name() + if len(id) == 0 { + id = this.lookForAllocatedType() + } + var handled bool + id, handled = this.handleCompositLiteral(id) + if handled { + return id + } + if id == "FloatType" { + // When tv.AllocatedType() gives "FloatType" it often means packing something into `interface`. + id, handled = this.lookForInterfaceWrapping(id) + if handled { + return id + } + } + if len(id) == 0 { + id = "unidentified" + } + return id +} + +func (this *idMiner) lookForAllocatedType() string { + if !this.v.IsACallInst().IsNil() { + return this.v.AllocatedType().String() + } + return "" +} + +func (this *idMiner) handleCompositLiteral(id string) (newId string, handled bool) { + if this.complitReg.MatchString(id) { + // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) + /* + %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 + %0 = getelementptr inbounds nuw i8, ptr %complit, i32 4, !dbg !53 + %1 = getelementptr inbounds nuw i8, ptr %complit, i32 8, !dbg !53 + store float 4.000000e+00, ptr %complit, align 4, !dbg !54 + store float 5.000000e+00, ptr %0, align 4, !dbg !55 + store float 6.000000e+00, ptr %1, align 4, !dbg !56 + %2 = call ptr @main.scaleVector3(ptr nonnull %complit, float 5.000000e-01, ptr undef), !dbg !57 + store ptr %2, ptr @main.escapedVector3, align 4, !dbg !60 + ret void, !dbg !61 + */ + /* + * 1. Find the next "call", where %complit is passed as parameter. + * 2. Produce ID: "Arg 0 of main.scaleVector3() call" // escapes at ... + */ + n := this.v + for _ = range 64 { + n = llvm.NextInstruction(n) + if n.IsNil() { + break + } + if call := n.IsACallInst(); !call.IsNil() { + args := getArgs(call) + for idx, arg := range args { + if arg.arg.Name() == id { + return getId(call, idx, &arg), true + } + } + } + } + } + return id, false +} + +// TODO: make a testcase where a function which have multiple escaping interface and not interface values, +// is called and check that the arg indicies displayed correctly +func (this *idMiner) lookForInterfaceWrapping(id string) (newId string, handled bool) { + /* --- The costumError -> error, struct -> interface --- + * %10 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #1, !dbg !193 + * %.elt = extractvalue { double, double } %9, 0, !dbg !193 + * store double %.elt, ptr %10, align 4, !dbg !193 + * %.repack18 = getelementptr inbounds nuw i8, ptr %10, i32 8, !dbg !193 + * %.elt19 = extractvalue { double, double } %9, 1, !dbg !193 + * store double %.elt19, ptr %.repack18, align 4, !dbg !193 + * call void @main.useInterface(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %10, ptr undef) #1, !dbg !196 + * + * %11 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #1, !dbg !218 + * store i32 10, ptr %11, align 4, !dbg !218 + * %.repack25 = getelementptr inbounds nuw i8, ptr %11, i32 4, !dbg !218 + * store i32 11, ptr %.repack25, align 4, !dbg !218 + * %.repack26 = getelementptr inbounds nuw i8, ptr %11, i32 8, !dbg !218 + * store i32 12, ptr %.repack26, align 4, !dbg !218 + * call void @main.useInterface(ptr nonnull @"reflect/types.type:array:3:basic:int32", ptr nonnull %11, ptr undef) #1, !dbg !219 + * + * %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #1, !dbg !134 + * store ptr @"main$string.1", ptr %0, align 4, !dbg !134 + * %.repack5 = getelementptr inbounds nuw i8, ptr %0, i32 4, !dbg !134 + * store i32 5, ptr %.repack5, align 4, !dbg !134 + * %1 = insertvalue %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [14 x i8] }, ptr @"reflect/types.type:named:main.theError", i32 0, i32 1), ptr undef }, ptr %0, 1, !dbg !134 + * ret %runtime._interface %1, !dbg !135 + * + * %1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #1, !dbg !133 + * %2 = extractvalue %main.theError %0, 0, !dbg !133 + * %.elt = extractvalue %runtime._string %2, 0, !dbg !133 + * store ptr %.elt, ptr %1, align 4, !dbg !133 + * %.repack1 = getelementptr inbounds nuw i8, ptr %1, i32 4, !dbg !133 + * %.elt2 = extractvalue %runtime._string %2, 1, !dbg !133 + * store i32 %.elt2, ptr %.repack1, align 4, !dbg !133 + * %3 = insertvalue %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [14 x i8] }, ptr @"reflect/types.type:named:main.theError", i32 0, i32 1), ptr undef }, ptr %1, 1, !dbg !133 + * ret %runtime._interface %3, !dbg !134 + * + * pattern: + * %reg = alloc + * store reg/var -> ptr %reg // write to struct, array or slice + * call with a (*, ptr nonnull @"reflect/types.type:...", ptr %reg arg, *) arg pair + * - Error message: "Arg x of fnName() call with type array:3:basic:int32" + " escapes at ..." + * or + * insertvalue %runtime._interface with ptr %reg param and ptr @"reflect/types.type:named:main.theError" (name of struct) + * - @"reflect/types.type:array:3:basic:int32": [3]int32 + * - Error message: "array:3:basic:int32" + " escapes at ..." + * + */ + // dumpIR("--- FloatType ----", 32, this.v) + ptrToAllocated := this.v + n := this.v + for _ = range 64 { + n = llvm.NextInstruction(n) + if n.IsNil() { + break + } + if !n.IsACallInst().IsNil() { + args := getArgs(n) + for idx, arg := range args { + if len(arg.descTypeName) > 0 && arg.arg == ptrToAllocated { + return getId(n, idx, &arg), true + } + } + } else if !n.IsAInsertValueInst().IsNil() { // interface wrapping + insertedValue := n.Operand(1) + if insertedValue == ptrToAllocated { + typeName, found := getTypeNameFromInsertValue(n) + if found { + return typeName, true + } + } + } + } + // %10 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #1, !dbg !193 + // As a fallback produce a message something like: "16 bytes escapes..." + if !this.v.IsACallInst().IsNil() { + if cv := this.v.CalledValue(); cv.Name() == "runtime.alloc" && this.v.OperandsCount() > 0 { + allocSizeArg := this.v.Operand(0) + allocSize := allocSizeArg.ZExtValue() + return fmt.Sprint(allocSize, " bytes"), true + } + } + return id, false +} + +type arg struct { + descTypeName string // It is set when arg is an interface arg, + // this is the type name which is wrapped by the interface. + name string // sometimes the name is available + arg llvm.Value +} + +func getId(callInst llvm.Value, idx int, a *arg) string { + ta := len(a.descTypeName) > 0 + na := len(a.name) > 0 + fn := callInst.CalledValue().Name() + if ta && na { + return fmt.Sprintf("Arg %d (%s %s) of %s()", idx, a.name, a.descTypeName, fn) + } else if ta { + return fmt.Sprintf("Arg %d (type: %s) of %s()", idx, a.descTypeName, fn) + } else if na { + return fmt.Sprintf("Arg %d (name: %s) of %s()", idx, a.name, fn) + } + return fmt.Sprintf("Arg %d of %s()", idx, fn) +} + +func getArgs(callInst llvm.Value) []arg { + /* + func useInterfaces(int, interface{}, reflect.Type, int, interface{}) + call void @main.useInterfaces( + i32 16, + ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, + ptr %5, ptr %6, + i32 32, + ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, + ptr undef) #1, !dbg !191 + */ + /* Scanning backward could identify which ptr pairs are interfaces: + * + * %5 = extractvalue %runtime._interface %2, 0, !dbg !541 + * %6 = extractvalue %runtime._interface %2, 1, !dbg !541 + * call void @main.useInterfaces(i32 16, ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, ptr %5, ptr %6, i32 32, ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, ptr undef) #1, !dbg !541 + * + * When the passed interface is the argument of the called function: + * define hidden void @main.passAnInterface( + * ptr %t.typecode, ptr %t.value, ptr nocapture readnone %context + * ) unnamed_addr #1 !dbg !553 { + * call void @main.useInterfaces( + * i32 16, + * ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, + * ptr %t.typecode, ptr %t.value, + * i32 32, + * ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, + * ptr undef) #1, !dbg !569 + ret void, !dbg !570 + */ + typeSuffix := ".typecode" + valueSuffix := ".value" + res := make([]arg, 0, 4) + opCnt := callInst.OperandsCount() + idx := 0 + for idx < opCnt { + argV := callInst.Operand(idx) + placed := false + idx++ + var nextArgV llvm.Value + if idx < opCnt { + nextArgV = callInst.Operand(idx) + if argV.Type().TypeKind() == llvm.PointerTypeKind && nextArgV.Type().TypeKind() == llvm.PointerTypeKind { + // Check pointer pair + ptrName := argV.Name() + nextPtrName := nextArgV.Name() + if len(ptrName) > 0 { + typeName, found := removeReflexPrefix(ptrName) + if found { + res = append(res, arg{descTypeName: typeName, arg: nextArgV}) + placed = true + idx++ + } + } + // The easier case + if !placed { + argName, found := strings.CutSuffix(ptrName, typeSuffix) + if found { + _, valueFound := strings.CutSuffix(nextPtrName, valueSuffix) + if valueFound { + res = append(res, + arg{name: argName, arg: nextArgV}) + placed = true + idx++ + } + } + } + // Step back on the instruction list to look for 'extractvalue' + if !placed { + p := callInst + p0Found := false + p1Found := false + for _ = range 64 { + p = llvm.PrevInstruction(p) + if p.IsNil() { + break + } + if !p.IsAExtractValueInst().IsNil() { + operand := p.Operand(0) + tp := operand.Type() + tpKind := tp.TypeKind() + if tpKind == llvm.StructTypeKind && tp.StructName() == "runtime._interface" { + p0Found = p0Found || argV == p + p1Found = p1Found || nextArgV == p + } + if p0Found && p1Found { + res = append(res, + arg{descTypeName: "unknown", arg: nextArgV}) + placed = true + idx++ + break + } + } + } + } + } + } + if !placed { + res = append(res, arg{arg: argV}) + } + } + return res +} + +func removeReflexPrefix(typeName string) (newTypeName string, removed bool) { + prefixs := []string{"reflect/types.type:named:", "reflect/types.type:"} + for _, prefix := range prefixs { + newTypeName, removed = strings.CutPrefix(typeName, prefix) + if removed { + return + } + } + return +} + +func getTypeNameFromInsertValue(insertValueInst llvm.Value) (typeName string, found bool) { + // %3 = insertvalue %runtime._interface { + // ptr getelementptr ( + // { ptr, i8, i16, ptr, ptr, ptr, { + // i32, [1 x ptr] }, [14 x i8] + // }, + // ptr @"reflect/types.type:named:main.theError", + // i32 0, i32 1 + // ), ptr undef + // }, + // ptr %1, 1, !dbg !133 + baseAggregate := insertValueInst.Operand(0) + + // Since it's an inline constant structure, let's look at its elements. + // The type descriptor is the first element of this constant struct. + if !baseAggregate.IsAConstantStruct().IsNil() || !baseAggregate.IsAConstantAggregateZero().IsNil() { + // Extract the first element (the GEP expression) + gepExpr := baseAggregate.Operand(0) + + // Drill down through the GEP/Casts to find the Global Variable symbol + for !gepExpr.IsAConstantExpr().IsNil() { + // In a GEP or bitcast expression, Operand(0) is the source pointer + gepExpr = gepExpr.Operand(0) + } + + // If we successfully drilled down to the Global Variable, grab its name + if !gepExpr.IsAGlobalVariable().IsNil() { + typeName, found = removeReflexPrefix(gepExpr.Name()) + } + } + return +} + +func dumpIR(hdr string, instCnt int, fstInst llvm.Value) { + fmt.Println("---", hdr, "---") + n := fstInst + fmt.Println(n.String()) + for _ = range instCnt { + n = llvm.NextInstruction(n) + if n.IsNil() { + break + } + fmt.Println(n.String()) + } +} diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index 1b2efd72ae..ca11f6c423 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "reflect" "runtime/volatile" "unsafe" ) @@ -26,28 +28,32 @@ func main() { s4 := make([]byte, 300) // OUT: makeslice4 size 300 exceeds maximum stack allocation size 256 readByteSlice(s4) - s5 := make([]int, 4) // OUT: makeslice6 escapes at line 30 + s5 := make([]int, 4) // OUT: makeslice6 escapes at line 32 _ = append(s5, 5) s6 := make([]int, 3) s7 := []int{1, 2, 3} copySlice(s6, s7) - c1 := getComplex128() // OUT: FloatType escapes at line 37 + c1 := getComplex128() // OUT: Arg 0 (type: basic:complex128) of main.useInterface() escapes at line 40 + useInterface(c1) + a1 := [3]int32{10, 11, 12} + useInterface(a1) // OUT: Arg 0 (type: array:3:basic:int32) of main.useInterface() escapes at line 43 + n3 := 5 func() int { return n3 }() - callVariadic(3, 5, 8) // OUT: varargs12 escapes at line 44 + callVariadic(3, 5, 8) // OUT: varargs12 escapes at line 50 - s8 := []int{3, 5, 8} // OUT: slicelit14 escapes at line 47 + s8 := []int{3, 5, 8} // OUT: slicelit14 escapes at line 53 callVariadic(s8...) - n4 := 3 // OUT: n4 escapes at line 51 - n5 := 7 // OUT: n5 escapes at line 51 + n4 := 3 // OUT: n4 escapes at line 57 + n5 := 7 // OUT: n5 escapes at line 57 func() { n4 = n5 }() @@ -74,6 +80,11 @@ func main() { pseudoVolatile.Set(uint32(unsafeNoEscape(unsafe.Pointer(&dmaBuf2[0])))) // ...use the buffer in the DMA peripheral keepAliveNoEscape(unsafe.Pointer(&dmaBuf2[0])) + // Doesn't escape. + theErr := theError{} + var err error = theErr + theErr.set("hello") + println(err.Error()) } type vector3 [3]float32 @@ -93,6 +104,20 @@ func crossVector3(a, b *vector3) vector3 { } } +type msgSetter interface { + set(msg string) +} + +type theError struct { + msg string +} + +func makeTheError(msg string) theError { return theError{msg: msg} } + +func (this theError) Error() string { return this.msg } + +func (this theError) set(msg string) { this.msg = msg } + func nonEscapingReturnedPointer() vector3 { a := vector3{1, 2, 3} b := vector3{4, 5, 6} @@ -104,14 +129,14 @@ func nonEscapingReturnedPointer() vector3 { var escapedSlice []int func escapingReturnedSlice() { - s := make([]int, 3) // OUT: makeslice escapes at line 108 + s := make([]int, 3) // OUT: makeslice escapes at line 133 escapedSlice = returnIntSlice(s) } var escapedVector3 *vector3 func escapingReturnedPointer() { - b := vector3{4, 5, 6} // OUT: b escapes at line 117 + b := vector3{4, 5, 6} // OUT: b escapes at line 142 c := scaleVector3(&b, 0.5) escapedVector3 = c @@ -119,13 +144,22 @@ func escapingReturnedPointer() { var escapedArray [2](*vector3) -func escapingReturnedPointer2() { - c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() call escapes at line 124 +func escapingCompositeLiterals() { + c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() escapes at line 149 escapedVector3 = c - a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: complit1 escapes at line 126 && complit2 escapes at line 126 + a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: complit1 escapes at line 151 && complit2 escapes at line 151 escapedArray = a } +func escapingInterfaceArguments() { + // Check that the argument indices in the escape messages are right. + useInterfaces(16, &vector3{7, 8, 9}, reflect.TypeFor[int](), 32, &vector3{6, 7, 8}) // OUT: Arg 1 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 156 && Arg 4 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 156 +} + +func passAnInterface(t reflect.Type) { + useInterfaces(16, &vector3{7, 8, 9}, t, 32, &vector3{6, 7, 8}) // OUT: Arg 1 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 160 && Arg 4 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 160 +} + func recursiveScaleVector3(vec *vector3, n int) *vector3 { if n == 0 { return vec @@ -140,6 +174,25 @@ func recursiveReturnedPointer() vector3 { return *c } +func theFmt() string { + v := 48 + res := fmt.Sprintf("The number is %d", v) // OUT: varargs escapes at line 179 + return res +} + +func giveAnError() error { + return theError{msg: "hello"} // OUT: main.theError escapes at line 184 +} + +func giveOtherError() error { + return makeTheError("wellcome") // OUT: main.theError escapes at line 188 +} + +func giveAnInterface() interface{} { + a := [3]int32{1, 2, 3} + return a // OUT: array:3:basic:int32 escapes at line 193 +} + func derefInt(x *int) int { return *x } @@ -183,3 +236,5 @@ func unsafeNoEscape(ptr unsafe.Pointer) uintptr func keepAliveNoEscape(ptr unsafe.Pointer) var pseudoVolatile volatile.Register32 + +func useInterfaces(int, interface{}, reflect.Type, int, interface{}) diff --git a/transform/testdata/allocs2.out.cover b/transform/testdata/allocs2.out.cover index a9e02123af..e90cca42a0 100644 --- a/transform/testdata/allocs2.out.cover +++ b/transform/testdata/allocs2.out.cover @@ -1,14 +1,23 @@ -testdata/allocs2.go:24.1,24.86 1 0 -testdata/allocs2.go:26.1,26.95 1 0 -testdata/allocs2.go:29.1,29.60 1 0 -testdata/allocs2.go:36.1,36.60 1 0 -testdata/allocs2.go:44.1,44.60 1 0 -testdata/allocs2.go:46.1,46.60 1 0 -testdata/allocs2.go:49.1,49.39 1 0 -testdata/allocs2.go:50.1,50.39 1 0 -testdata/allocs2.go:107.1,107.59 1 0 -testdata/allocs2.go:114.1,114.53 1 0 -testdata/allocs2.go:123.1,123.105 1 0 -testdata/allocs2.go:125.1,125.127 1 0 -testdata/allocs2.go:125.1,125.127 1 0 -testdata/allocs2.go:137.1,137.57 1 0 +testdata/allocs2.go:26.1,26.86 1 0 +testdata/allocs2.go:28.1,28.95 1 0 +testdata/allocs2.go:31.1,31.60 1 0 +testdata/allocs2.go:38.1,38.104 1 0 +testdata/allocs2.go:43.1,43.102 1 0 +testdata/allocs2.go:50.1,50.60 1 0 +testdata/allocs2.go:52.1,52.60 1 0 +testdata/allocs2.go:55.1,55.39 1 0 +testdata/allocs2.go:56.1,56.39 1 0 +testdata/allocs2.go:132.1,132.59 1 0 +testdata/allocs2.go:139.1,139.53 1 0 +testdata/allocs2.go:148.1,148.100 1 0 +testdata/allocs2.go:150.1,150.127 1 0 +testdata/allocs2.go:150.1,150.127 1 0 +testdata/allocs2.go:156.1,156.266 1 0 +testdata/allocs2.go:156.1,156.266 1 0 +testdata/allocs2.go:160.1,160.245 1 0 +testdata/allocs2.go:160.1,160.245 1 0 +testdata/allocs2.go:171.1,171.57 1 0 +testdata/allocs2.go:179.1,179.79 1 0 +testdata/allocs2.go:184.1,184.73 1 0 +testdata/allocs2.go:188.1,188.75 1 0 +testdata/allocs2.go:193.1,193.58 1 0 From 4bc85ce7ae2c5a44d97a1936bd72f93924cb2b31 Mon Sep 17 00:00:00 2001 From: ebak Date: Tue, 14 Jul 2026 11:30:43 +0200 Subject: [PATCH 4/4] More verbose -print-allocs: Initial commit of using attributes --- compiler/compiler.go | 4 + compiler/testdata/gc.ll | 88 +++++++----- compiler/testdata/generics.ll | 38 +++--- .../testdata/goroutine-cortex-m-qemu-tasks.ll | 3 +- compiler/testdata/goroutine-wasm-asyncify.ll | 3 +- compiler/testdata/pragma.ll | 5 +- compiler/testdata/slice.ll | 18 ++- transform/idminer.go | 127 ++++++------------ transform/testdata/allocs2.go | 35 +++-- transform/testdata/allocs2.out.cover | 38 +++--- 10 files changed, 173 insertions(+), 186 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 2b3419ed48..e07eee6716 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -2189,6 +2189,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { layoutValue := b.createObjectLayout(typ, expr.Pos()) align := b.targetData.ABITypeAlignment(typ) buf := b.createAlloc(sizeValue, layoutValue, align, expr.Comment) + buf.AddCallSiteAttribute(-1, b.ctx.CreateStringAttribute("tinygo-alloc-type", expr.Type().String())) + buf.AddCallSiteAttribute(-1, b.ctx.CreateStringAttribute("tinygo-alloc-name", expr.Comment)) return buf, nil } else { buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment) @@ -2419,6 +2421,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") layoutValue := b.createObjectLayout(llvmElemType, expr.Pos()) slicePtr := b.createAlloc(sliceSize, layoutValue, 0, "makeslice.buf") + slicePtr.AddCallSiteAttribute(-1, b.ctx.CreateStringAttribute("tinygo-alloc-type", expr.Type().String())) + slicePtr.AddCallSiteAttribute(-1, b.ctx.CreateStringAttribute("tinygo-alloc-name", expr.Block().Comment)) slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign))) // Extend or truncate if necessary. This is safe as we've already done diff --git a/compiler/testdata/gc.ll b/compiler/testdata/gc.ll index e2b0090698..f81fadafb0 100644 --- a/compiler/testdata/gc.ll +++ b/compiler/testdata/gc.ll @@ -38,16 +38,16 @@ define hidden void @main.newScalar(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new, ptr @main.scalar1, align 4 - %new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new1, ptr @main.scalar2, align 4 - %new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #6 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new2, ptr @main.scalar3, align 4 - %new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 + %new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new3, ptr @main.scalar4, align 4 ret void } @@ -59,14 +59,14 @@ declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2 define hidden void @main.newArray(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new, ptr @main.array1, align 4 - %new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new1, ptr @main.array2, align 4 - %new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #10 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new2, ptr @main.array3, align 4 ret void } @@ -75,20 +75,20 @@ entry: define hidden void @main.newStruct(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 1 ptr @runtime.alloc_zero(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 1 ptr @runtime.alloc_zero(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new, ptr @main.struct1, align 4 - %new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #12 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new1, ptr @main.struct2, align 4 - %new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #13 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new2, ptr @main.struct3, align 4 - %new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 + %new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #14 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new3, ptr @main.struct4, align 4 - %new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3 + %new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #15 + call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #4 store ptr %new4, ptr @main.struct5, align 4 ret void } @@ -100,8 +100,8 @@ declare noalias nonnull ptr @runtime.alloc_zero(i32, ptr, ptr) #2 define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #16 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #4 ret ptr %new } @@ -109,18 +109,18 @@ entry: define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #17 + call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #4 store ptr %makeslice, ptr @main.slice1, align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 4), align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 8), align 4 - %makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #18 + call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #4 store ptr %makeslice1, ptr @main.slice2, align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 4), align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 8), align 4 - %makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #19 + call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #4 store ptr %makeslice3, ptr @main.slice3, align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 4), align 4 store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 8), align 4 @@ -131,18 +131,34 @@ entry: define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3 + %0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #4 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #4 store double %v.r, ptr %0, align 8 %.repack1 = getelementptr inbounds nuw i8, ptr %0, i32 8 store double %v.i, ptr %.repack1, align 8 %1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #4 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #4 ret %runtime._interface %1 } attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } -attributes #3 = { nounwind } +attributes #3 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*byte" } +attributes #4 = { nounwind } +attributes #5 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*int32" } +attributes #6 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*int64" } +attributes #7 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*float32" } +attributes #8 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*[3]byte" } +attributes #9 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*[71]byte" } +attributes #10 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*[3]*byte" } +attributes #11 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*struct{}" } +attributes #12 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*struct{x int; y int}" } +attributes #13 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*struct{x *byte; y [60]uintptr; z *byte}" } +attributes #14 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*struct{x *byte; y [61]uintptr}" } +attributes #15 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*struct{x *byte; y [30]uintptr}" } +attributes #16 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*func()" } +attributes #17 = { nounwind "tinygo-alloc-name"="makeslice" "tinygo-alloc-type"="*[5]byte" } +attributes #18 = { nounwind "tinygo-alloc-name"="makeslice" "tinygo-alloc-type"="*[5]*int" } +attributes #19 = { nounwind "tinygo-alloc-name"="makeslice" "tinygo-alloc-type"="*[5][]byte" } diff --git a/compiler/testdata/generics.ll b/compiler/testdata/generics.ll index 2a3fcd0abf..5a8e3776e3 100644 --- a/compiler/testdata/generics.ll +++ b/compiler/testdata/generics.ll @@ -27,19 +27,19 @@ define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a. entry: %stackalloc = alloca i8, align 1 %a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4 store float %a.X, ptr %a, align 4 %a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4 store float %a.Y, ptr %a.repack5, align 4 - %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 + %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4 store float %b.X, ptr %b, align 4 %b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4 store float %b.Y, ptr %b.repack7, align 4 - call void @main.checkSize(i32 4, ptr undef) #3 - call void @main.checkSize(i32 8, ptr undef) #3 - %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 + call void @main.checkSize(i32 4, ptr undef) #4 + call void @main.checkSize(i32 8, ptr undef) #4 + %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #6 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4 br i1 false, label %deref.throw, label %deref.next deref.next: ; preds = %entry @@ -89,20 +89,20 @@ declare void @runtime.nilPanic(ptr) #0 define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 + %a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4 store i32 %a.X, ptr %a, align 4 %a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4 store i32 %a.Y, ptr %a.repack5, align 4 - %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 + %b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4 store i32 %b.X, ptr %b, align 4 %b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4 store i32 %b.Y, ptr %b.repack7, align 4 - call void @main.checkSize(i32 4, ptr undef) #3 - call void @main.checkSize(i32 8, ptr undef) #3 - %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 + call void @main.checkSize(i32 4, ptr undef) #4 + call void @main.checkSize(i32 8, ptr undef) #4 + %complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4 br i1 false, label %deref.throw, label %deref.next deref.next: ; preds = %entry @@ -144,4 +144,10 @@ deref.throw: ; preds = %store.next, %deref. attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } -attributes #3 = { nounwind } +attributes #3 = { nounwind "tinygo-alloc-name"="a" "tinygo-alloc-type"="*main.Point[float32]" } +attributes #4 = { nounwind } +attributes #5 = { nounwind "tinygo-alloc-name"="b" "tinygo-alloc-type"="*main.Point[float32]" } +attributes #6 = { nounwind "tinygo-alloc-name"="complit" "tinygo-alloc-type"="*main.Point[float32]" } +attributes #7 = { nounwind "tinygo-alloc-name"="a" "tinygo-alloc-type"="*main.Point[int]" } +attributes #8 = { nounwind "tinygo-alloc-name"="b" "tinygo-alloc-type"="*main.Point[int]" } +attributes #9 = { nounwind "tinygo-alloc-name"="complit" "tinygo-alloc-type"="*main.Point[int]" } diff --git a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll index a1e81a9b1b..a581dff294 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll @@ -58,7 +58,7 @@ entry: ; Function Attrs: nounwind define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 { entry: - %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 + %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #12 store i32 3, ptr %n, align 4 %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11 store i32 5, ptr %0, align 4 @@ -198,3 +198,4 @@ attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" } attributes #11 = { nounwind } +attributes #12 = { nounwind "tinygo-alloc-name"="n" "tinygo-alloc-type"="*int" } diff --git a/compiler/testdata/goroutine-wasm-asyncify.ll b/compiler/testdata/goroutine-wasm-asyncify.ll index 137b601f77..a11fb5bd7b 100644 --- a/compiler/testdata/goroutine-wasm-asyncify.ll +++ b/compiler/testdata/goroutine-wasm-asyncify.ll @@ -61,7 +61,7 @@ entry: define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 + %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #12 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 store i32 3, ptr %n, align 4 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 @@ -209,3 +209,4 @@ attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" } attributes #11 = { nounwind } +attributes #12 = { nounwind "tinygo-alloc-name"="n" "tinygo-alloc-type"="*int" } diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index c2c06b3d49..55cd429c3a 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -95,7 +95,7 @@ define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #10 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #11 ret ptr %new } @@ -112,4 +112,5 @@ attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirec attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" } attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" } attributes #9 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } -attributes #10 = { nounwind } +attributes #10 = { nounwind "tinygo-alloc-name"="new" "tinygo-alloc-type"="*int" } +attributes #11 = { nounwind } diff --git a/compiler/testdata/slice.ll b/compiler/testdata/slice.ll index a0756b54b6..e5342f6f6c 100644 --- a/compiler/testdata/slice.ll +++ b/compiler/testdata/slice.ll @@ -45,7 +45,7 @@ declare void @runtime.lookupPanic(ptr) #0 define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #6 call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #5 store i32 1, ptr %varargs, align 4 %0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4 @@ -106,7 +106,7 @@ entry: br i1 %slice.maxcap, label %slice.throw, label %slice.next slice.next: ; preds = %entry - %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 @@ -129,7 +129,7 @@ entry: slice.next: ; preds = %entry %makeslice.cap = shl nuw i32 %len, 1 - %makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 @@ -150,7 +150,7 @@ entry: slice.next: ; preds = %entry %makeslice.cap = mul i32 %len, 3 - %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 @@ -171,7 +171,7 @@ entry: slice.next: ; preds = %entry %makeslice.cap = shl nuw i32 %len, 2 - %makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 @@ -222,7 +222,7 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #0 define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 + %makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #5 br i1 false, label %slicetoarray.throw, label %slicetoarray.next @@ -334,3 +334,9 @@ attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #5 = { nounwind } +attributes #6 = { nounwind "tinygo-alloc-name"="varargs" "tinygo-alloc-type"="*[3]int" } +attributes #7 = { nounwind "tinygo-alloc-name"="entry" "tinygo-alloc-type"="[]byte" } +attributes #8 = { nounwind "tinygo-alloc-name"="entry" "tinygo-alloc-type"="[]int16" } +attributes #9 = { nounwind "tinygo-alloc-name"="entry" "tinygo-alloc-type"="[][3]byte" } +attributes #10 = { nounwind "tinygo-alloc-name"="entry" "tinygo-alloc-type"="[]int32" } +attributes #11 = { nounwind "tinygo-alloc-name"="makeslice" "tinygo-alloc-type"="*[6]int" } diff --git a/transform/idminer.go b/transform/idminer.go index 9b92ab03fe..7fc0db5094 100644 --- a/transform/idminer.go +++ b/transform/idminer.go @@ -17,6 +17,47 @@ func makeIdMiner() idMiner { } func (this *idMiner) get(v llvm.Value) string { + if !v.IsACallInst().IsNil() { + getAttr := func(key string) (value string, found bool) { + attr := v.GetCallSiteStringAttribute(-1, key) + if attr.IsNil() { + return "", false + } + value = attr.GetStringValue() + return value, len(value) > 0 + } + typeAttr, hasType := getAttr("tinygo-alloc-type") + nameAttr, hasName := getAttr("tinygo-alloc-name") + typeAttr, _ = strings.CutPrefix(typeAttr, "*") + if nameAttr == "entry" && v.Name() == "makeslice.buf" { + nameAttr = "slice" + } + if hasType && hasName { + if nameAttr == "complit" { + return typeAttr + } + if nameAttr == "varargs" { + return fmt.Sprint(nameAttr, " ", typeAttr) + } + if nameAttr == "makeslice" || nameAttr == "slicelit" { + return fmt.Sprint("slice ", typeAttr) + } + } + if hasName { + if nameAttr == "complit" { + return "composite literal" + } + if nameAttr == "makeslice" || nameAttr == "slicelit" { + return "slice" + } + return nameAttr + } + if hasType { + return typeAttr + } + } + // Not all allocations are attributed with "tinygo-alloc-..." falling back + // to LLVM IR mining. this.v = v id := v.Name() if len(id) == 0 { @@ -50,17 +91,6 @@ func (this *idMiner) lookForAllocatedType() string { func (this *idMiner) handleCompositLiteral(id string) (newId string, handled bool) { if this.complitReg.MatchString(id) { // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) - /* - %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 - %0 = getelementptr inbounds nuw i8, ptr %complit, i32 4, !dbg !53 - %1 = getelementptr inbounds nuw i8, ptr %complit, i32 8, !dbg !53 - store float 4.000000e+00, ptr %complit, align 4, !dbg !54 - store float 5.000000e+00, ptr %0, align 4, !dbg !55 - store float 6.000000e+00, ptr %1, align 4, !dbg !56 - %2 = call ptr @main.scaleVector3(ptr nonnull %complit, float 5.000000e-01, ptr undef), !dbg !57 - store ptr %2, ptr @main.escapedVector3, align 4, !dbg !60 - ret void, !dbg !61 - */ /* * 1. Find the next "call", where %complit is passed as parameter. * 2. Produce ID: "Arg 0 of main.scaleVector3() call" // escapes at ... @@ -88,39 +118,6 @@ func (this *idMiner) handleCompositLiteral(id string) (newId string, handled boo // is called and check that the arg indicies displayed correctly func (this *idMiner) lookForInterfaceWrapping(id string) (newId string, handled bool) { /* --- The costumError -> error, struct -> interface --- - * %10 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #1, !dbg !193 - * %.elt = extractvalue { double, double } %9, 0, !dbg !193 - * store double %.elt, ptr %10, align 4, !dbg !193 - * %.repack18 = getelementptr inbounds nuw i8, ptr %10, i32 8, !dbg !193 - * %.elt19 = extractvalue { double, double } %9, 1, !dbg !193 - * store double %.elt19, ptr %.repack18, align 4, !dbg !193 - * call void @main.useInterface(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %10, ptr undef) #1, !dbg !196 - * - * %11 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #1, !dbg !218 - * store i32 10, ptr %11, align 4, !dbg !218 - * %.repack25 = getelementptr inbounds nuw i8, ptr %11, i32 4, !dbg !218 - * store i32 11, ptr %.repack25, align 4, !dbg !218 - * %.repack26 = getelementptr inbounds nuw i8, ptr %11, i32 8, !dbg !218 - * store i32 12, ptr %.repack26, align 4, !dbg !218 - * call void @main.useInterface(ptr nonnull @"reflect/types.type:array:3:basic:int32", ptr nonnull %11, ptr undef) #1, !dbg !219 - * - * %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #1, !dbg !134 - * store ptr @"main$string.1", ptr %0, align 4, !dbg !134 - * %.repack5 = getelementptr inbounds nuw i8, ptr %0, i32 4, !dbg !134 - * store i32 5, ptr %.repack5, align 4, !dbg !134 - * %1 = insertvalue %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [14 x i8] }, ptr @"reflect/types.type:named:main.theError", i32 0, i32 1), ptr undef }, ptr %0, 1, !dbg !134 - * ret %runtime._interface %1, !dbg !135 - * - * %1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #1, !dbg !133 - * %2 = extractvalue %main.theError %0, 0, !dbg !133 - * %.elt = extractvalue %runtime._string %2, 0, !dbg !133 - * store ptr %.elt, ptr %1, align 4, !dbg !133 - * %.repack1 = getelementptr inbounds nuw i8, ptr %1, i32 4, !dbg !133 - * %.elt2 = extractvalue %runtime._string %2, 1, !dbg !133 - * store i32 %.elt2, ptr %.repack1, align 4, !dbg !133 - * %3 = insertvalue %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [14 x i8] }, ptr @"reflect/types.type:named:main.theError", i32 0, i32 1), ptr undef }, ptr %1, 1, !dbg !133 - * ret %runtime._interface %3, !dbg !134 - * * pattern: * %reg = alloc * store reg/var -> ptr %reg // write to struct, array or slice @@ -132,7 +129,6 @@ func (this *idMiner) lookForInterfaceWrapping(id string) (newId string, handled * - Error message: "array:3:basic:int32" + " escapes at ..." * */ - // dumpIR("--- FloatType ----", 32, this.v) ptrToAllocated := this.v n := this.v for _ = range 64 { @@ -170,7 +166,7 @@ func (this *idMiner) lookForInterfaceWrapping(id string) (newId string, handled } type arg struct { - descTypeName string // It is set when arg is an interface arg, + descTypeName string // It is set when arg is an interface arg, // this is the type name which is wrapped by the interface. name string // sometimes the name is available arg llvm.Value @@ -191,35 +187,6 @@ func getId(callInst llvm.Value, idx int, a *arg) string { } func getArgs(callInst llvm.Value) []arg { - /* - func useInterfaces(int, interface{}, reflect.Type, int, interface{}) - call void @main.useInterfaces( - i32 16, - ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, - ptr %5, ptr %6, - i32 32, - ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, - ptr undef) #1, !dbg !191 - */ - /* Scanning backward could identify which ptr pairs are interfaces: - * - * %5 = extractvalue %runtime._interface %2, 0, !dbg !541 - * %6 = extractvalue %runtime._interface %2, 1, !dbg !541 - * call void @main.useInterfaces(i32 16, ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, ptr %5, ptr %6, i32 32, ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, ptr undef) #1, !dbg !541 - * - * When the passed interface is the argument of the called function: - * define hidden void @main.passAnInterface( - * ptr %t.typecode, ptr %t.value, ptr nocapture readnone %context - * ) unnamed_addr #1 !dbg !553 { - * call void @main.useInterfaces( - * i32 16, - * ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit, - * ptr %t.typecode, ptr %t.value, - * i32 32, - * ptr nonnull @"reflect/types.type:pointer:named:main.vector3", ptr nonnull %complit1, - * ptr undef) #1, !dbg !569 - ret void, !dbg !570 - */ typeSuffix := ".typecode" valueSuffix := ".value" res := make([]arg, 0, 4) @@ -306,16 +273,6 @@ func removeReflexPrefix(typeName string) (newTypeName string, removed bool) { } func getTypeNameFromInsertValue(insertValueInst llvm.Value) (typeName string, found bool) { - // %3 = insertvalue %runtime._interface { - // ptr getelementptr ( - // { ptr, i8, i16, ptr, ptr, ptr, { - // i32, [1 x ptr] }, [14 x i8] - // }, - // ptr @"reflect/types.type:named:main.theError", - // i32 0, i32 1 - // ), ptr undef - // }, - // ptr %1, 1, !dbg !133 baseAggregate := insertValueInst.Operand(0) // Since it's an inline constant structure, let's look at its elements. diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index ca11f6c423..1ec603ec27 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -23,12 +23,12 @@ func main() { s3 := make([]int, 3) returnIntSlice(s3) - useSlice(make([]int, getUnknownNumber())) // OUT: makeslice.buf size is not constant + useSlice(make([]int, getUnknownNumber())) // OUT: slice size is not constant - s4 := make([]byte, 300) // OUT: makeslice4 size 300 exceeds maximum stack allocation size 256 + s4 := make([]byte, 300) // OUT: slice [300]byte size 300 exceeds maximum stack allocation size 256 readByteSlice(s4) - s5 := make([]int, 4) // OUT: makeslice6 escapes at line 32 + s5 := make([]int, 4) // OUT: slice [4]int escapes at line 32 _ = append(s5, 5) s6 := make([]int, 3) @@ -47,9 +47,9 @@ func main() { return n3 }() - callVariadic(3, 5, 8) // OUT: varargs12 escapes at line 50 + callVariadic(3, 5, 8) // OUT: varargs [3]int escapes at line 50 - s8 := []int{3, 5, 8} // OUT: slicelit14 escapes at line 53 + s8 := []int{3, 5, 8} // OUT: slice [3]int escapes at line 53 callVariadic(s8...) n4 := 3 // OUT: n4 escapes at line 57 @@ -80,11 +80,6 @@ func main() { pseudoVolatile.Set(uint32(unsafeNoEscape(unsafe.Pointer(&dmaBuf2[0])))) // ...use the buffer in the DMA peripheral keepAliveNoEscape(unsafe.Pointer(&dmaBuf2[0])) - // Doesn't escape. - theErr := theError{} - var err error = theErr - theErr.set("hello") - println(err.Error()) } type vector3 [3]float32 @@ -129,14 +124,14 @@ func nonEscapingReturnedPointer() vector3 { var escapedSlice []int func escapingReturnedSlice() { - s := make([]int, 3) // OUT: makeslice escapes at line 133 + s := make([]int, 3) // OUT: slice [3]int escapes at line 128 escapedSlice = returnIntSlice(s) } var escapedVector3 *vector3 func escapingReturnedPointer() { - b := vector3{4, 5, 6} // OUT: b escapes at line 142 + b := vector3{4, 5, 6} // OUT: b escapes at line 137 c := scaleVector3(&b, 0.5) escapedVector3 = c @@ -145,19 +140,19 @@ func escapingReturnedPointer() { var escapedArray [2](*vector3) func escapingCompositeLiterals() { - c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() escapes at line 149 + c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: main.vector3 escapes at line 144 escapedVector3 = c - a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: complit1 escapes at line 151 && complit2 escapes at line 151 + a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: main.vector3 escapes at line 146 && main.vector3 escapes at line 146 escapedArray = a } func escapingInterfaceArguments() { // Check that the argument indices in the escape messages are right. - useInterfaces(16, &vector3{7, 8, 9}, reflect.TypeFor[int](), 32, &vector3{6, 7, 8}) // OUT: Arg 1 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 156 && Arg 4 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 156 + useInterfaces(16, &vector3{7, 8, 9}, reflect.TypeFor[int](), 32, &vector3{6, 7, 8}) // OUT: main.vector3 escapes at line 151 && main.vector3 escapes at line 151 } func passAnInterface(t reflect.Type) { - useInterfaces(16, &vector3{7, 8, 9}, t, 32, &vector3{6, 7, 8}) // OUT: Arg 1 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 160 && Arg 4 (type: pointer:named:main.vector3) of main.useInterfaces() escapes at line 160 + useInterfaces(16, &vector3{7, 8, 9}, t, 32, &vector3{6, 7, 8}) // OUT: main.vector3 escapes at line 155 && main.vector3 escapes at line 155 } func recursiveScaleVector3(vec *vector3, n int) *vector3 { @@ -176,21 +171,21 @@ func recursiveReturnedPointer() vector3 { func theFmt() string { v := 48 - res := fmt.Sprintf("The number is %d", v) // OUT: varargs escapes at line 179 + res := fmt.Sprintf("The number is %d", v) // OUT: varargs [1]any escapes at line 174 return res } func giveAnError() error { - return theError{msg: "hello"} // OUT: main.theError escapes at line 184 + return theError{msg: "hello"} // OUT: main.theError escapes at line 179 } func giveOtherError() error { - return makeTheError("wellcome") // OUT: main.theError escapes at line 188 + return makeTheError("wellcome") // OUT: main.theError escapes at line 183 } func giveAnInterface() interface{} { a := [3]int32{1, 2, 3} - return a // OUT: array:3:basic:int32 escapes at line 193 + return a // OUT: array:3:basic:int32 escapes at line 188 } func derefInt(x *int) int { diff --git a/transform/testdata/allocs2.out.cover b/transform/testdata/allocs2.out.cover index e90cca42a0..f5ae90dcf5 100644 --- a/transform/testdata/allocs2.out.cover +++ b/transform/testdata/allocs2.out.cover @@ -1,23 +1,23 @@ -testdata/allocs2.go:26.1,26.86 1 0 -testdata/allocs2.go:28.1,28.95 1 0 -testdata/allocs2.go:31.1,31.60 1 0 +testdata/allocs2.go:26.1,26.78 1 0 +testdata/allocs2.go:28.1,28.100 1 0 +testdata/allocs2.go:31.1,31.62 1 0 testdata/allocs2.go:38.1,38.104 1 0 testdata/allocs2.go:43.1,43.102 1 0 -testdata/allocs2.go:50.1,50.60 1 0 -testdata/allocs2.go:52.1,52.60 1 0 +testdata/allocs2.go:50.1,50.65 1 0 +testdata/allocs2.go:52.1,52.62 1 0 testdata/allocs2.go:55.1,55.39 1 0 testdata/allocs2.go:56.1,56.39 1 0 -testdata/allocs2.go:132.1,132.59 1 0 -testdata/allocs2.go:139.1,139.53 1 0 -testdata/allocs2.go:148.1,148.100 1 0 -testdata/allocs2.go:150.1,150.127 1 0 -testdata/allocs2.go:150.1,150.127 1 0 -testdata/allocs2.go:156.1,156.266 1 0 -testdata/allocs2.go:156.1,156.266 1 0 -testdata/allocs2.go:160.1,160.245 1 0 -testdata/allocs2.go:160.1,160.245 1 0 -testdata/allocs2.go:171.1,171.57 1 0 -testdata/allocs2.go:179.1,179.79 1 0 -testdata/allocs2.go:184.1,184.73 1 0 -testdata/allocs2.go:188.1,188.75 1 0 -testdata/allocs2.go:193.1,193.58 1 0 +testdata/allocs2.go:127.1,127.62 1 0 +testdata/allocs2.go:134.1,134.53 1 0 +testdata/allocs2.go:143.1,143.84 1 0 +testdata/allocs2.go:145.1,145.135 1 0 +testdata/allocs2.go:145.1,145.135 1 0 +testdata/allocs2.go:151.1,151.162 1 0 +testdata/allocs2.go:151.1,151.162 1 0 +testdata/allocs2.go:155.1,155.141 1 0 +testdata/allocs2.go:155.1,155.141 1 0 +testdata/allocs2.go:166.1,166.57 1 0 +testdata/allocs2.go:174.1,174.86 1 0 +testdata/allocs2.go:179.1,179.73 1 0 +testdata/allocs2.go:183.1,183.75 1 0 +testdata/allocs2.go:188.1,188.58 1 0