Skip to content

Commit 4736088

Browse files
committed
cmd/internal/obj/arm64: mark unsafe points
For async preemption, we will be using REGTMP as a temporary register in injected call on ARM64, which will clobber it. So any code that uses REGTMP is not safe for async preemption. In the assembler backend, we expand a Prog to multiple machine instructions and use REGTMP as a temporary register if necessary. These need to be marked unsafe. In fact, most of the multi-instruction Progs use REGTMP, so we mark all of them, except ones that are whitelisted. Change-Id: I6e97805a13950e3b693fb606d77834940ac3722e Reviewed-on: https://go-review.googlesource.com/c/go/+/203460 Run-TryBot: Cherry Zhang <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent e5ce13c commit 4736088

File tree

3 files changed

+106
-12
lines changed

3 files changed

+106
-12
lines changed

src/cmd/internal/obj/arm64/asm7.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,10 @@ func MOVCONST(d int64, s int, rt int) uint32 {
258258
}
259259

260260
const (
261-
LFROM = 1 << 0
262-
LTO = 1 << 1
261+
// Optab.flag
262+
LFROM = 1 << 0 // p.From uses constant pool
263+
LTO = 1 << 1 // p.To uses constant pool
264+
NOTUSETMP = 1 << 2 // p expands to multiple instructions, but does NOT use REGTMP
263265
)
264266

265267
var optab = []Optab{
@@ -383,10 +385,10 @@ var optab = []Optab{
383385
{AMOVD, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
384386
{AMOVW, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
385387
{AMOVD, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0},
386-
{AMOVW, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, 0, 0},
387-
{AMOVD, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, 0, 0},
388-
{AMOVD, C_MOVCON3, C_NONE, C_NONE, C_REG, 12, 12, 0, 0, 0},
389-
{AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, 0, 0},
388+
{AMOVW, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0},
389+
{AMOVD, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0},
390+
{AMOVD, C_MOVCON3, C_NONE, C_NONE, C_REG, 12, 12, 0, NOTUSETMP, 0},
391+
{AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, NOTUSETMP, 0},
390392

391393
{AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0},
392394
{AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0},
@@ -420,15 +422,15 @@ var optab = []Optab{
420422
{ALSL, C_REG, C_REG, C_NONE, C_REG, 9, 4, 0, 0, 0},
421423
{ASVC, C_VCON, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0},
422424
{ASVC, C_NONE, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0},
423-
{ADWORD, C_NONE, C_NONE, C_NONE, C_VCON, 11, 8, 0, 0, 0},
424-
{ADWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 11, 8, 0, 0, 0},
425-
{ADWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 11, 8, 0, 0, 0},
426-
{ADWORD, C_NONE, C_NONE, C_NONE, C_LACON, 11, 8, 0, 0, 0},
425+
{ADWORD, C_NONE, C_NONE, C_NONE, C_VCON, 11, 8, 0, NOTUSETMP, 0},
426+
{ADWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 11, 8, 0, NOTUSETMP, 0},
427+
{ADWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 11, 8, 0, NOTUSETMP, 0},
428+
{ADWORD, C_NONE, C_NONE, C_NONE, C_LACON, 11, 8, 0, NOTUSETMP, 0},
427429
{AWORD, C_NONE, C_NONE, C_NONE, C_LCON, 14, 4, 0, 0, 0},
428430
{AWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 14, 4, 0, 0, 0},
429431
{AWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 14, 4, 0, 0, 0},
430-
{AMOVW, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, 0, 0},
431-
{AMOVD, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, 0, 0},
432+
{AMOVW, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0},
433+
{AMOVD, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0},
432434
{AMOVB, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
433435
{AMOVBU, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
434436
{AMOVH, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0},
@@ -1022,6 +1024,23 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
10221024
psz += 4
10231025
}
10241026
}
1027+
1028+
// Mark nonpreemptible instruction sequences.
1029+
// We use REGTMP as a scratch register during call injection,
1030+
// so instruction sequences that use REGTMP are unsafe to
1031+
// preempt asynchronously.
1032+
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint)
1033+
}
1034+
1035+
// Return whether p is an unsafe point.
1036+
func (c *ctxt7) isUnsafePoint(p *obj.Prog) bool {
1037+
if p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP {
1038+
return true
1039+
}
1040+
// Most of the multi-instruction sequence uses REGTMP, except
1041+
// ones marked safe.
1042+
o := c.oplook(p)
1043+
return o.size > 4 && o.flag&NOTUSETMP == 0
10251044
}
10261045

10271046
/*
@@ -3069,6 +3088,8 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
30693088
}
30703089

30713090
case 12: /* movT $vcon, reg */
3091+
// NOTE: this case does not use REGTMP. If it ever does,
3092+
// remove the NOTUSETMP flag in optab.
30723093
num := c.omovlconst(p.As, p, &p.From, int(p.To.Reg), os[:])
30733094
if num == 0 {
30743095
c.ctxt.Diag("invalid constant: %v", p)
@@ -4017,6 +4038,8 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
40174038
o1 = c.opldpstp(p, o, v, uint32(r), uint32(p.From.Reg), uint32(p.From.Offset), 0)
40184039

40194040
case 68: /* movT $vconaddr(SB), reg -> adrp + add + reloc */
4041+
// NOTE: this case does not use REGTMP. If it ever does,
4042+
// remove the NOTUSETMP flag in optab.
40204043
if p.As == AMOVW {
40214044
c.ctxt.Diag("invalid load of 32-bit address: %v", p)
40224045
}

src/cmd/internal/obj/arm64/obj7.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
599599
// Store link register before decrementing SP, so if a signal comes
600600
// during the execution of the function prologue, the traceback
601601
// code will not see a half-updated stack frame.
602+
// This sequence is not async preemptible, as if we open a frame
603+
// at the current SP, it will clobber the saved LR.
604+
q = c.ctxt.StartUnsafePoint(q, c.newprog)
605+
602606
q = obj.Appendp(q, c.newprog)
603607
q.Pos = p.Pos
604608
q.As = ASUB
@@ -624,6 +628,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
624628
q1.To.Type = obj.TYPE_REG
625629
q1.To.Reg = REGSP
626630
q1.Spadj = c.autosize
631+
632+
q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
627633
} else {
628634
// small frame, update SP and save LR in a single MOVD.W instruction
629635
q1 = obj.Appendp(q, c.newprog)

src/cmd/internal/obj/plist.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,68 @@ func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
206206

207207
return pcdata
208208
}
209+
210+
// StartUnsafePoint generates PCDATA Progs after p to mark the
211+
// beginning of an unsafe point. The unsafe point starts immediately
212+
// after p.
213+
// It returns the last Prog generated.
214+
func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog {
215+
pcdata := Appendp(p, newprog)
216+
pcdata.As = APCDATA
217+
pcdata.From.Type = TYPE_CONST
218+
pcdata.From.Offset = objabi.PCDATA_StackMapIndex
219+
pcdata.To.Type = TYPE_CONST
220+
pcdata.To.Offset = -2 // pcdata -2 marks unsafe point
221+
222+
// TODO: register map?
223+
224+
return pcdata
225+
}
226+
227+
// EndUnsafePoint generates PCDATA Progs after p to mark the end of an
228+
// unsafe point, restoring the stack map index to oldval.
229+
// The unsafe point ends right after p.
230+
// It returns the last Prog generated.
231+
func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog {
232+
pcdata := Appendp(p, newprog)
233+
pcdata.As = APCDATA
234+
pcdata.From.Type = TYPE_CONST
235+
pcdata.From.Offset = objabi.PCDATA_StackMapIndex
236+
pcdata.To.Type = TYPE_CONST
237+
pcdata.To.Offset = oldval
238+
239+
// TODO: register map?
240+
241+
return pcdata
242+
}
243+
244+
// MarkUnsafePoints inserts PCDATAs to mark nonpreemptible instruction
245+
// sequences, based on isUnsafePoint predicate. p0 is the start of the
246+
// instruction stream.
247+
func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint func(*Prog) bool) {
248+
prev := p0
249+
oldval := int64(-1) // entry pcdata
250+
for p := prev.Link; p != nil; p, prev = p.Link, p {
251+
if p.As == APCDATA && p.From.Offset == objabi.PCDATA_StackMapIndex {
252+
oldval = p.To.Offset
253+
continue
254+
}
255+
if oldval == -2 {
256+
continue // already unsafe
257+
}
258+
if isUnsafePoint(p) {
259+
q := ctxt.StartUnsafePoint(prev, newprog)
260+
q.Pc = p.Pc
261+
q.Link = p
262+
// Advance to the end of unsafe point.
263+
for p.Link != nil && isUnsafePoint(p.Link) {
264+
p = p.Link
265+
}
266+
if p.Link == nil {
267+
break // Reached the end, don't bother marking the end
268+
}
269+
p = ctxt.EndUnsafePoint(p, newprog, oldval)
270+
p.Pc = p.Link.Pc
271+
}
272+
}
273+
}

0 commit comments

Comments
 (0)