aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/symtab.go
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2019-11-20 17:10:34 -0500
committerCherry Zhang <cherryyz@google.com>2020-05-06 15:41:12 +0000
commitee330385ca684f7c166913e10998f791d1be06e7 (patch)
tree63f9b19a0811656bb78fc35df4ad2c7ced36a49d /src/runtime/symtab.go
parent4daf8719e7f4c71a620f650d73caab2a9d7ea499 (diff)
downloadgo-ee330385ca684f7c166913e10998f791d1be06e7.tar.gz
go-ee330385ca684f7c166913e10998f791d1be06e7.zip
cmd/internal/obj, runtime: preempt & restart some instruction sequences
On some architectures, for async preemption the injected call needs to clobber a register (usually REGTMP) in order to return to the preempted function. As a consequence, the PC ranges where REGTMP is live are not preemptible. The uses of REGTMP are usually generated by the assembler, where it needs to load or materialize a large constant or offset that doesn't fit into the instruction. In those cases, REGTMP is not live at the start of the instruction sequence. Instead of giving up preemption in those cases, we could preempt it and restart the sequence when resuming the execution. Basically, this is like reissuing an interrupted instruction, except that here the "instruction" is a Prog that consists of multiple machine instructions. For this to work, we need to generate PC data to mark the start of the Prog. Currently this is only done for ARM64. TODO: the split-stack function prologue is currently not async preemptible. We could use this mechanism, preempt it and restart at the function entry. Change-Id: I37cb282f8e606e7ab6f67b3edfdc6063097b4bd1 Reviewed-on: https://go-review.googlesource.com/c/go/+/208126 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/symtab.go')
-rw-r--r--src/runtime/symtab.go53
1 files changed, 40 insertions, 13 deletions
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 04aa90e077..ce2ec6dd1d 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -287,6 +287,18 @@ const (
// PCDATA_UnsafePoint values.
_PCDATA_UnsafePointSafe = -1 // Safe for async preemption
_PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption
+
+ // _PCDATA_Restart1(2) apply on a sequence of instructions, within
+ // which if an async preemption happens, we should back off the PC
+ // to the start of the sequence when resume.
+ // We need two so we can distinguish the start/end of the sequence
+ // in case that two sequences are next to each other.
+ _PCDATA_Restart1 = -3
+ _PCDATA_Restart2 = -4
+
+ // Like _PCDATA_RestartAtEntry, but back to function entry if async
+ // preempted.
+ _PCDATA_RestartAtEntry = -5
)
// A FuncID identifies particular functions that need to be treated
@@ -708,9 +720,11 @@ func pcvalueCacheKey(targetpc uintptr) uintptr {
return (targetpc / sys.PtrSize) % uintptr(len(pcvalueCache{}.entries))
}
-func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
+// Returns the PCData value, and the PC where this value starts.
+// TODO: the start PC is returned only when cache is nil.
+func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
if off == 0 {
- return -1
+ return -1, 0
}
// Check the cache. This speeds up walks of deep stacks, which
@@ -729,7 +743,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
// fail in the first clause.
ent := &cache.entries[x][i]
if ent.off == off && ent.targetpc == targetpc {
- return ent.val
+ return ent.val, 0
}
}
}
@@ -739,11 +753,12 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
print("runtime: no module data for ", hex(f.entry), "\n")
throw("no module data")
}
- return -1
+ return -1, 0
}
datap := f.datap
p := datap.pclntable[off:]
pc := f.entry
+ prevpc := pc
val := int32(-1)
for {
var ok bool
@@ -770,14 +785,15 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
}
}
- return val
+ return val, prevpc
}
+ prevpc = pc
}
// If there was a table, it should have covered all program counters.
// If not, something is wrong.
if panicking != 0 || !strict {
- return -1
+ return -1, 0
}
print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
@@ -795,7 +811,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
}
throw("invalid runtime symbol table")
- return -1
+ return -1, 0
}
func cfuncname(f funcInfo) *byte {
@@ -833,9 +849,9 @@ func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int
if !f.valid() {
return "?", 0
}
- fileno := int(pcvalue(f, f.pcfile, targetpc, nil, strict))
- line = pcvalue(f, f.pcln, targetpc, nil, strict)
- if fileno == -1 || line == -1 || fileno >= len(datap.filetab) {
+ fileno, _ := pcvalue(f, f.pcfile, targetpc, nil, strict)
+ line, _ = pcvalue(f, f.pcln, targetpc, nil, strict)
+ if fileno == -1 || line == -1 || int(fileno) >= len(datap.filetab) {
// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
return "?", 0
}
@@ -848,7 +864,7 @@ func funcline(f funcInfo, targetpc uintptr) (file string, line int32) {
}
func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 {
- x := pcvalue(f, f.pcsp, targetpc, cache, true)
+ x, _ := pcvalue(f, f.pcsp, targetpc, cache, true)
if x&(sys.PtrSize-1) != 0 {
print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
}
@@ -882,14 +898,25 @@ func pcdatavalue(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache)
if table < 0 || table >= f.npcdata {
return -1
}
- return pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
+ r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
+ return r
}
func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
if table < 0 || table >= f.npcdata {
return -1
}
- return pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
+ r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
+ return r
+}
+
+// Like pcdatavalue, but also return the start PC of this PCData value.
+// It doesn't take a cache.
+func pcdatavalue2(f funcInfo, table int32, targetpc uintptr) (int32, uintptr) {
+ if table < 0 || table >= f.npcdata {
+ return -1, 0
+ }
+ return pcvalue(f, pcdatastart(f, table), targetpc, nil, true)
}
func funcdata(f funcInfo, i uint8) unsafe.Pointer {