@@ -33,6 +33,7 @@ package arm64
33
33
import (
34
34
"cmd/internal/obj"
35
35
"cmd/internal/objabi"
36
+ "encoding/binary"
36
37
"fmt"
37
38
"log"
38
39
"math"
@@ -1099,133 +1100,57 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
1099
1100
c := ctxt7 {ctxt : ctxt , newprog : newprog , cursym : cursym , autosize : int32 (p .To .Offset & 0xffffffff ), extrasize : int32 (p .To .Offset >> 32 )}
1100
1101
p .To .Offset &= 0xffffffff // extrasize is no longer needed
1101
1102
1102
- bflag := 1
1103
+ // Process literal pool and allocate initial program counter for each Prog, before
1104
+ // generating branch veneers.
1103
1105
pc := int64 (0 )
1104
1106
p .Pc = pc
1105
- var m int
1106
- var o * Optab
1107
1107
for p = p .Link ; p != nil ; p = p .Link {
1108
1108
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 ))
1133
1111
}
1134
1112
1135
- c .cursym .Size = pc
1136
-
1137
1113
/*
1138
1114
* if any procedure is large enough to
1139
1115
* generate a large SBRA branch, then
1140
1116
* generate extra passes putting branches
1141
1117
* around jmps to fix. this is rare.
1142
1118
*/
1143
- for bflag != 0 {
1144
- bflag = 0
1119
+ changed := true
1120
+ for changed {
1121
+ changed = false
1145
1122
pc = 0
1146
1123
for p = c .cursym .Func ().Text .Link ; p != nil ; p = p .Link {
1147
1124
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 ))
1191
1127
}
1192
1128
}
1193
1129
1194
- pc += - pc & (funcAlign - 1 )
1195
- c .cursym .Size = pc
1196
-
1197
1130
/*
1198
1131
* lay out the code, emitting code and data relocations.
1199
1132
*/
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
+
1205
1135
for p := c .cursym .Func ().Text .Link ; p != nil ; p = p .Link {
1206
1136
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 :
1213
1139
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 ++ {
1215
1141
// 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 )
1226
1143
}
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 ]... )
1227
1150
}
1228
1151
}
1152
+ buf .finish ()
1153
+ c .cursym .Size = int64 (len (c .cursym .P ))
1229
1154
1230
1155
// Mark nonpreemptible instruction sequences.
1231
1156
// 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) {
1244
1169
}
1245
1170
}
1246
1171
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
+
1247
1258
// isUnsafePoint returns whether p is an unsafe point.
1248
1259
func (c * ctxt7 ) isUnsafePoint (p * obj.Prog ) bool {
1249
1260
// 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) {
3456
3467
}
3457
3468
}
3458
3469
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
+
3460
3473
var os [5 ]uint32
3461
3474
o1 := uint32 (0 )
3462
3475
o2 := uint32 (0 )
@@ -5896,6 +5909,8 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
5896
5909
out [2 ] = o3
5897
5910
out [3 ] = o4
5898
5911
out [4 ] = o5
5912
+
5913
+ return int (o .size (c .ctxt , p ) / 4 )
5899
5914
}
5900
5915
5901
5916
func (c * ctxt7 ) addrRelocType (p * obj.Prog ) objabi.RelocType {
0 commit comments