// We use REGTMP as a scratch register during call injection,
// so instruction sequences that use REGTMP are unsafe to
// preempt asynchronously.
- obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, nil)
+ obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable)
}
-// Return whether p is an unsafe point.
+// isUnsafePoint returns whether p is an unsafe point.
func (c *ctxt0) isUnsafePoint(p *obj.Prog) bool {
- if p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP {
- return true
+ // If p explicitly uses REGTMP, it's unsafe to preempt, because the
+ // preemption sequence clobbers REGTMP.
+ return p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP
+}
+
+// isRestartable returns whether p is a multi-instruction sequence that,
+// if preempted, can be restarted.
+func (c *ctxt0) isRestartable(p *obj.Prog) bool {
+ if c.isUnsafePoint(p) {
+ return false
}
- // Most of the multi-instruction sequence uses REGTMP, except
- // ones marked safe.
+ // If p is a multi-instruction sequence with uses REGTMP inserted by
+ // the assembler in order to materialize a large constant/offset, we
+ // can restart p (at the start of the instruction sequence), recompute
+ // the content of REGTMP, upon async preemption. Currently, all cases
+ // of assembler-inserted REGTMP fall into this category.
+ // If p doesn't use REGTMP, it can be simply preempted, so we don't
+ // mark it.
o := c.oplook(p)
return o.size > 4 && o.flag&NOTUSETMP == 0
}