Skip to content

Commit 557211c

Browse files
snickolls-armcherrymui
authored andcommitted
cmd/internal/obj/arm64: Add helpers for span7 passes
Adds helper functions for the literal pooling, large branch handling and code emission stages of the span7 assembler pass. This hides the implementation of the current assembler from the general workflow in span7 to make the implementation easier to change in future. Updates #44734 Change-Id: I8859956b23ad4faebeeff6df28051b098ef90fed Reviewed-on: https://go-review.googlesource.com/c/go/+/595755 Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 8cd550a commit 557211c

File tree

1 file changed

+115
-100
lines changed

1 file changed

+115
-100
lines changed

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

Lines changed: 115 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ package arm64
3333
import (
3434
"cmd/internal/obj"
3535
"cmd/internal/objabi"
36+
"encoding/binary"
3637
"fmt"
3738
"log"
3839
"math"
@@ -1099,133 +1100,57 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
10991100
c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset & 0xffffffff), extrasize: int32(p.To.Offset >> 32)}
11001101
p.To.Offset &= 0xffffffff // extrasize is no longer needed
11011102

1102-
bflag := 1
1103+
// Process literal pool and allocate initial program counter for each Prog, before
1104+
// generating branch veneers.
11031105
pc := int64(0)
11041106
p.Pc = pc
1105-
var m int
1106-
var o *Optab
11071107
for p = p.Link; p != nil; p = p.Link {
11081108
p.Pc = pc
1109-
o = c.oplook(p)
1110-
m = o.size(c.ctxt, p)
1111-
if m == 0 {
1112-
switch p.As {
1113-
case obj.APCALIGN, obj.APCALIGNMAX:
1114-
m = obj.AlignmentPadding(int32(pc), p, ctxt, cursym)
1115-
break
1116-
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
1117-
continue
1118-
default:
1119-
c.ctxt.Diag("zero-width instruction\n%v", p)
1120-
}
1121-
}
1122-
pc += int64(m)
1123-
1124-
if o.flag&LFROM != 0 {
1125-
c.addpool(p, &p.From)
1126-
}
1127-
if o.flag&LTO != 0 {
1128-
c.addpool(p, &p.To)
1129-
}
1130-
if c.blitrl != nil {
1131-
c.checkpool(p)
1132-
}
1109+
c.addLiteralsToPool(p)
1110+
pc += int64(c.asmsizeBytes(p))
11331111
}
11341112

1135-
c.cursym.Size = pc
1136-
11371113
/*
11381114
* if any procedure is large enough to
11391115
* generate a large SBRA branch, then
11401116
* generate extra passes putting branches
11411117
* around jmps to fix. this is rare.
11421118
*/
1143-
for bflag != 0 {
1144-
bflag = 0
1119+
changed := true
1120+
for changed {
1121+
changed = false
11451122
pc = 0
11461123
for p = c.cursym.Func().Text.Link; p != nil; p = p.Link {
11471124
p.Pc = pc
1148-
o = c.oplook(p)
1149-
1150-
/* very large branches */
1151-
if (o.flag&BRANCH14BITS != 0 || o.flag&BRANCH19BITS != 0) && p.To.Target() != nil {
1152-
otxt := p.To.Target().Pc - pc
1153-
var toofar bool
1154-
if o.flag&BRANCH14BITS != 0 { // branch instruction encodes 14 bits
1155-
toofar = otxt <= -(1<<15)+10 || otxt >= (1<<15)-10
1156-
} else if o.flag&BRANCH19BITS != 0 { // branch instruction encodes 19 bits
1157-
toofar = otxt <= -(1<<20)+10 || otxt >= (1<<20)-10
1158-
}
1159-
if toofar {
1160-
q := c.newprog()
1161-
q.Link = p.Link
1162-
p.Link = q
1163-
q.As = AB
1164-
q.To.Type = obj.TYPE_BRANCH
1165-
q.To.SetTarget(p.To.Target())
1166-
p.To.SetTarget(q)
1167-
q = c.newprog()
1168-
q.Link = p.Link
1169-
p.Link = q
1170-
q.As = AB
1171-
q.To.Type = obj.TYPE_BRANCH
1172-
q.To.SetTarget(q.Link.Link)
1173-
bflag = 1
1174-
}
1175-
}
1176-
m = o.size(c.ctxt, p)
1177-
1178-
if m == 0 {
1179-
switch p.As {
1180-
case obj.APCALIGN, obj.APCALIGNMAX:
1181-
m = obj.AlignmentPaddingLength(int32(pc), p, ctxt)
1182-
break
1183-
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
1184-
continue
1185-
default:
1186-
c.ctxt.Diag("zero-width instruction\n%v", p)
1187-
}
1188-
}
1189-
1190-
pc += int64(m)
1125+
changed = changed || c.fixUpLongBranch(p)
1126+
pc += int64(c.asmsizeBytes(p))
11911127
}
11921128
}
11931129

1194-
pc += -pc & (funcAlign - 1)
1195-
c.cursym.Size = pc
1196-
11971130
/*
11981131
* lay out the code, emitting code and data relocations.
11991132
*/
1200-
c.cursym.Grow(c.cursym.Size)
1201-
bp := c.cursym.P
1202-
psz := int32(0)
1203-
var i int
1204-
var out [6]uint32
1133+
buf := codeBuffer{&c.cursym.P}
1134+
12051135
for p := c.cursym.Func().Text.Link; p != nil; p = p.Link {
12061136
c.pc = p.Pc
1207-
o = c.oplook(p)
1208-
sz := o.size(c.ctxt, p)
1209-
if sz > 4*len(out) {
1210-
log.Fatalf("out array in span7 is too small, need at least %d for %v", sz/4, p)
1211-
}
1212-
if p.As == obj.APCALIGN || p.As == obj.APCALIGNMAX {
1137+
switch p.As {
1138+
case obj.APCALIGN, obj.APCALIGNMAX:
12131139
v := obj.AlignmentPaddingLength(int32(p.Pc), p, c.ctxt)
1214-
for i = 0; i < int(v/4); i++ {
1140+
for i := 0; i < int(v/4); i++ {
12151141
// emit ANOOP instruction by the padding size
1216-
c.ctxt.Arch.ByteOrder.PutUint32(bp, OP_NOOP)
1217-
bp = bp[4:]
1218-
psz += 4
1219-
}
1220-
} else {
1221-
c.asmout(p, o, out[:])
1222-
for i = 0; i < sz/4; i++ {
1223-
c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
1224-
bp = bp[4:]
1225-
psz += 4
1142+
buf.emit(OP_NOOP)
12261143
}
1144+
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
1145+
continue
1146+
default:
1147+
var out [6]uint32
1148+
count := c.asmout(p, out[:])
1149+
buf.emit(out[:count]...)
12271150
}
12281151
}
1152+
buf.finish()
1153+
c.cursym.Size = int64(len(c.cursym.P))
12291154

12301155
// Mark nonpreemptible instruction sequences.
12311156
// We use REGTMP as a scratch register during call injection,
@@ -1244,6 +1169,92 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
12441169
}
12451170
}
12461171

1172+
type codeBuffer struct {
1173+
data *[]byte
1174+
}
1175+
1176+
func (cb *codeBuffer) pc() int64 {
1177+
return int64(len(*cb.data))
1178+
}
1179+
1180+
// Write a sequence of opcodes into the code buffer.
1181+
func (cb *codeBuffer) emit(op ...uint32) {
1182+
for _, o := range op {
1183+
*cb.data = binary.LittleEndian.AppendUint32(*cb.data, o)
1184+
}
1185+
}
1186+
1187+
// Completes the code buffer for the function by padding the buffer to function alignment
1188+
// with zero values.
1189+
func (cb *codeBuffer) finish() {
1190+
for len(*cb.data)%funcAlign > 0 {
1191+
*cb.data = append(*cb.data, 0)
1192+
}
1193+
}
1194+
1195+
// Return the size of the assembled Prog, in bytes.
1196+
func (c *ctxt7) asmsizeBytes(p *obj.Prog) int {
1197+
switch p.As {
1198+
case obj.APCALIGN, obj.APCALIGNMAX:
1199+
return obj.AlignmentPadding(int32(p.Pc), p, c.ctxt, c.cursym)
1200+
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
1201+
return 0
1202+
default:
1203+
o := c.oplook(p)
1204+
return o.size(c.ctxt, p)
1205+
}
1206+
}
1207+
1208+
// Modify the Prog list if the Prog is a branch with a large offset that cannot be
1209+
// encoded in the instruction. Return true if a modification was made, false if not.
1210+
func (c *ctxt7) fixUpLongBranch(p *obj.Prog) bool {
1211+
var toofar bool
1212+
1213+
o := c.oplook(p)
1214+
1215+
/* very large branches */
1216+
if (o.flag&BRANCH14BITS != 0 || o.flag&BRANCH19BITS != 0) && p.To.Target() != nil {
1217+
otxt := p.To.Target().Pc - p.Pc
1218+
if o.flag&BRANCH14BITS != 0 { // branch instruction encodes 14 bits
1219+
toofar = otxt <= -(1<<15)+10 || otxt >= (1<<15)-10
1220+
} else if o.flag&BRANCH19BITS != 0 { // branch instruction encodes 19 bits
1221+
toofar = otxt <= -(1<<20)+10 || otxt >= (1<<20)-10
1222+
}
1223+
if toofar {
1224+
q := c.newprog()
1225+
q.Link = p.Link
1226+
p.Link = q
1227+
q.As = AB
1228+
q.To.Type = obj.TYPE_BRANCH
1229+
q.To.SetTarget(p.To.Target())
1230+
p.To.SetTarget(q)
1231+
q = c.newprog()
1232+
q.Link = p.Link
1233+
p.Link = q
1234+
q.As = AB
1235+
q.To.Type = obj.TYPE_BRANCH
1236+
q.To.SetTarget(q.Link.Link)
1237+
}
1238+
}
1239+
1240+
return toofar
1241+
}
1242+
1243+
// Adds literal values from the Prog into the literal pool if necessary.
1244+
func (c *ctxt7) addLiteralsToPool(p *obj.Prog) {
1245+
o := c.oplook(p)
1246+
1247+
if o.flag&LFROM != 0 {
1248+
c.addpool(p, &p.From)
1249+
}
1250+
if o.flag&LTO != 0 {
1251+
c.addpool(p, &p.To)
1252+
}
1253+
if c.blitrl != nil {
1254+
c.checkpool(p)
1255+
}
1256+
}
1257+
12471258
// isUnsafePoint returns whether p is an unsafe point.
12481259
func (c *ctxt7) isUnsafePoint(p *obj.Prog) bool {
12491260
// If p explicitly uses REGTMP, it's unsafe to preempt, because the
@@ -3456,7 +3467,9 @@ func (c *ctxt7) checkShiftAmount(p *obj.Prog, a *obj.Addr) {
34563467
}
34573468
}
34583469

3459-
func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
3470+
func (c *ctxt7) asmout(p *obj.Prog, out []uint32) (count int) {
3471+
o := c.oplook(p)
3472+
34603473
var os [5]uint32
34613474
o1 := uint32(0)
34623475
o2 := uint32(0)
@@ -5896,6 +5909,8 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
58965909
out[2] = o3
58975910
out[3] = o4
58985911
out[4] = o5
5912+
5913+
return int(o.size(c.ctxt, p) / 4)
58995914
}
59005915

59015916
func (c *ctxt7) addrRelocType(p *obj.Prog) objabi.RelocType {

0 commit comments

Comments
 (0)