cmd/compile: clean up buggy DWARF inlined info PC ranges

Repair the code that generates PC ranges for DWARF inlined routine
instances to insure that if II Y is a child of II X within the inline
tree, X's ranges include the ranges from Y. This is similar to what
we're already doing for DWARF scopes.

Updates #33188.

Change-Id: I9bb552777fcd1ae93dc01872707667ad092b1dd9
Reviewed-on: https://go-review.googlesource.com/c/go/+/248724
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
Trust: Than McIntosh <thanm@google.com>
diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go
index 5120fa1..bb5ae61 100644
--- a/src/cmd/compile/internal/gc/dwinl.go
+++ b/src/cmd/compile/internal/gc/dwinl.go
@@ -8,6 +8,7 @@
 	"cmd/internal/dwarf"
 	"cmd/internal/obj"
 	"cmd/internal/src"
+	"fmt"
 	"strings"
 )
 
@@ -170,12 +171,32 @@
 		addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
 	}
 
+	// Issue 33188: if II foo is a child of II bar, then ensure that
+	// bar's ranges include the ranges of foo (the loop above will produce
+	// disjoint ranges).
+	for k, c := range inlcalls.Calls {
+		if c.Root {
+			unifyCallRanges(inlcalls, k)
+		}
+	}
+
 	// Debugging
 	if Debug_gendwarfinl != 0 {
 		dumpInlCalls(inlcalls)
 		dumpInlVars(dwVars)
 	}
 
+	// Perform a consistency check on inlined routine PC ranges
+	// produced by unifyCallRanges above. In particular, complain in
+	// cases where you have A -> B -> C (e.g. C is inlined into B, and
+	// B is inlined into A) and the ranges for B are not enclosed
+	// within the ranges for A, or C within B.
+	for k, c := range inlcalls.Calls {
+		if c.Root {
+			checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1)
+		}
+	}
+
 	return inlcalls
 }
 
@@ -355,3 +376,74 @@
 		Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
 	}
 }
+
+func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) {
+	for _, r := range par {
+		if rng.Start >= r.Start && rng.End <= r.End {
+			return true, ""
+		}
+	}
+	msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End)
+	for _, r := range par {
+		msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End)
+	}
+	msg += " }"
+	return false, msg
+}
+
+func rangesContainsAll(parent, child []dwarf.Range) (bool, string) {
+	for _, r := range child {
+		c, m := rangesContains(parent, r)
+		if !c {
+			return false, m
+		}
+	}
+	return true, ""
+}
+
+// checkInlCall verifies that the PC ranges for inline info 'idx' are
+// enclosed/contained within the ranges of its parent inline (or if
+// this is a root/toplevel inline, checks that the ranges fall within
+// the extent of the top level function). A panic is issued if a
+// malformed range is found.
+func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) {
+
+	// Callee
+	ic := inlCalls.Calls[idx]
+	callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
+	calleeRanges := ic.Ranges
+
+	// Caller
+	caller := funcName
+	parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
+	if parentIdx != -1 {
+		pic := inlCalls.Calls[parentIdx]
+		caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
+		parentRanges = pic.Ranges
+	}
+
+	// Callee ranges contained in caller ranges?
+	c, m := rangesContainsAll(parentRanges, calleeRanges)
+	if !c {
+		Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
+	}
+
+	// Now visit kids
+	for _, k := range ic.Children {
+		checkInlCall(funcName, inlCalls, funcSize, k, idx)
+	}
+}
+
+// unifyCallRanges ensures that the ranges for a given inline
+// transitively include all of the ranges for its child inlines.
+func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) {
+	ic := &inlcalls.Calls[idx]
+	for _, childIdx := range ic.Children {
+		// First make sure child ranges are unified.
+		unifyCallRanges(inlcalls, childIdx)
+
+		// Then merge child ranges into ranges for this inline.
+		cic := inlcalls.Calls[childIdx]
+		ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges)
+	}
+}
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index b2fd526..e1a70ef 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -101,26 +101,26 @@
 	logDwarf = doit
 }
 
-// UnifyRanges merges the list of ranges of c into the list of ranges of s
-func (s *Scope) UnifyRanges(c *Scope) {
-	out := make([]Range, 0, len(s.Ranges)+len(c.Ranges))
-
+// MergeRanges creates a new range list by merging the ranges from
+// its two arguments, then returns the new list.
+func MergeRanges(in1, in2 []Range) []Range {
+	out := make([]Range, 0, len(in1)+len(in2))
 	i, j := 0, 0
 	for {
 		var cur Range
-		if i < len(s.Ranges) && j < len(c.Ranges) {
-			if s.Ranges[i].Start < c.Ranges[j].Start {
-				cur = s.Ranges[i]
+		if i < len(in2) && j < len(in1) {
+			if in2[i].Start < in1[j].Start {
+				cur = in2[i]
 				i++
 			} else {
-				cur = c.Ranges[j]
+				cur = in1[j]
 				j++
 			}
-		} else if i < len(s.Ranges) {
-			cur = s.Ranges[i]
+		} else if i < len(in2) {
+			cur = in2[i]
 			i++
-		} else if j < len(c.Ranges) {
-			cur = c.Ranges[j]
+		} else if j < len(in1) {
+			cur = in1[j]
 			j++
 		} else {
 			break
@@ -133,7 +133,12 @@
 		}
 	}
 
-	s.Ranges = out
+	return out
+}
+
+// UnifyRanges merges the ranges from 'c' into the list of ranges for 's'.
+func (s *Scope) UnifyRanges(c *Scope) {
+	s.Ranges = MergeRanges(s.Ranges, c.Ranges)
 }
 
 // AppendRange adds r to s, if r is non-empty.