@@ -1254,29 +1254,6 @@ func progMentionsR15(p *obj.Prog) bool {
1254
1254
return addrMentionsR15 (& p .From ) || addrMentionsR15 (& p .To ) || isR15 (p .Reg ) || addrMentionsR15 (p .GetFrom3 ())
1255
1255
}
1256
1256
1257
- // progOverwritesR15 reports whether p writes to R15 and does not depend on
1258
- // the previous value of R15.
1259
- func progOverwritesR15 (p * obj.Prog ) bool {
1260
- if ! (p .To .Type == obj .TYPE_REG && isR15 (p .To .Reg )) {
1261
- // Not writing to R15.
1262
- return false
1263
- }
1264
- if (p .As == AXORL || p .As == AXORQ ) && p .From .Type == obj .TYPE_REG && isR15 (p .From .Reg ) {
1265
- // These look like uses of R15, but aren't, so we must detect these
1266
- // before the use check below.
1267
- return true
1268
- }
1269
- if addrMentionsR15 (& p .From ) || isR15 (p .Reg ) || addrMentionsR15 (p .GetFrom3 ()) {
1270
- // use before overwrite
1271
- return false
1272
- }
1273
- if p .As == AMOVL || p .As == AMOVQ || p .As == APOPQ {
1274
- return true
1275
- // TODO: MOVB might be ok if we only ever use R15B.
1276
- }
1277
- return false
1278
- }
1279
-
1280
1257
func addrUsesGlobal (a * obj.Addr ) bool {
1281
1258
if a == nil {
1282
1259
return false
@@ -1296,6 +1273,114 @@ func progUsesGlobal(p *obj.Prog) bool {
1296
1273
return addrUsesGlobal (& p .From ) || addrUsesGlobal (& p .To ) || addrUsesGlobal (p .GetFrom3 ())
1297
1274
}
1298
1275
1276
+ type rwMask int
1277
+
1278
+ const (
1279
+ readFrom rwMask = 1 << iota
1280
+ readTo
1281
+ readReg
1282
+ readFrom3
1283
+ writeFrom
1284
+ writeTo
1285
+ writeReg
1286
+ writeFrom3
1287
+ )
1288
+
1289
+ // progRW returns a mask describing the effects of the instruction p.
1290
+ // Note: this isn't exhaustively accurate. It is only currently used for detecting
1291
+ // reads/writes to R15, so SSE register behavior isn't fully correct, and
1292
+ // other weird cases (e.g. writes to DX by CLD) also aren't captured.
1293
+ func progRW (p * obj.Prog ) rwMask {
1294
+ var m rwMask
1295
+ // Default for most instructions
1296
+ if p .From .Type != obj .TYPE_NONE {
1297
+ m |= readFrom
1298
+ }
1299
+ if p .To .Type != obj .TYPE_NONE {
1300
+ // Most x86 instructions update the To value
1301
+ m |= readTo | writeTo
1302
+ }
1303
+ if p .Reg != 0 {
1304
+ m |= readReg
1305
+ }
1306
+ if p .GetFrom3 () != nil {
1307
+ m |= readFrom3
1308
+ }
1309
+
1310
+ // Lots of exceptions to the above defaults.
1311
+ name := p .As .String ()
1312
+ if strings .HasPrefix (name , "MOV" ) || strings .HasPrefix (name , "PMOV" ) {
1313
+ // MOV instructions don't read To.
1314
+ m &^= readTo
1315
+ }
1316
+ switch p .As {
1317
+ case APOPW , APOPL , APOPQ ,
1318
+ ALEAL , ALEAQ ,
1319
+ AIMUL3W , AIMUL3L , AIMUL3Q ,
1320
+ APEXTRB , APEXTRW , APEXTRD , APEXTRQ , AVPEXTRB , AVPEXTRW , AVPEXTRD , AVPEXTRQ , AEXTRACTPS ,
1321
+ ABSFW , ABSFL , ABSFQ , ABSRW , ABSRL , ABSRQ , APOPCNTW , APOPCNTL , APOPCNTQ , ALZCNTW , ALZCNTL , ALZCNTQ ,
1322
+ ASHLXL , ASHLXQ , ASHRXL , ASHRXQ , ASARXL , ASARXQ :
1323
+ // These instructions are pure writes to To. They don't use its old value.
1324
+ m &^= readTo
1325
+ case AXORL , AXORQ :
1326
+ // Register-clearing idiom doesn't read previous value.
1327
+ if p .From .Type == obj .TYPE_REG && p .To .Type == obj .TYPE_REG && p .From .Reg == p .To .Reg {
1328
+ m &^= readFrom | readTo
1329
+ }
1330
+ case AMULXL , AMULXQ :
1331
+ // These are write-only to both To and From3.
1332
+ m &^= readTo | readFrom3
1333
+ m |= writeFrom3
1334
+ }
1335
+ return m
1336
+ }
1337
+
1338
+ // progReadsR15 reports whether p reads the register R15.
1339
+ func progReadsR15 (p * obj.Prog ) bool {
1340
+ m := progRW (p )
1341
+ if m & readFrom != 0 && p .From .Type == obj .TYPE_REG && isR15 (p .From .Reg ) {
1342
+ return true
1343
+ }
1344
+ if m & readTo != 0 && p .To .Type == obj .TYPE_REG && isR15 (p .To .Reg ) {
1345
+ return true
1346
+ }
1347
+ if m & readReg != 0 && isR15 (p .Reg ) {
1348
+ return true
1349
+ }
1350
+ if m & readFrom3 != 0 && p .GetFrom3 ().Type == obj .TYPE_REG && isR15 (p .GetFrom3 ().Reg ) {
1351
+ return true
1352
+ }
1353
+ // reads of the index registers
1354
+ if p .From .Type == obj .TYPE_MEM && (isR15 (p .From .Reg ) || isR15 (p .From .Index )) {
1355
+ return true
1356
+ }
1357
+ if p .To .Type == obj .TYPE_MEM && (isR15 (p .To .Reg ) || isR15 (p .To .Index )) {
1358
+ return true
1359
+ }
1360
+ if f3 := p .GetFrom3 (); f3 != nil && f3 .Type == obj .TYPE_MEM && (isR15 (f3 .Reg ) || isR15 (f3 .Index )) {
1361
+ return true
1362
+ }
1363
+ return false
1364
+ }
1365
+
1366
+ // progWritesR15 reports whether p writes the register R15.
1367
+ func progWritesR15 (p * obj.Prog ) bool {
1368
+ m := progRW (p )
1369
+ if m & writeFrom != 0 && p .From .Type == obj .TYPE_REG && isR15 (p .From .Reg ) {
1370
+ return true
1371
+ }
1372
+ if m & writeTo != 0 && p .To .Type == obj .TYPE_REG && isR15 (p .To .Reg ) {
1373
+ return true
1374
+ }
1375
+ if m & writeReg != 0 && isR15 (p .Reg ) {
1376
+ return true
1377
+ }
1378
+ if m & writeFrom3 != 0 && p .GetFrom3 ().Type == obj .TYPE_REG && isR15 (p .GetFrom3 ().Reg ) {
1379
+ return true
1380
+ }
1381
+ return false
1382
+ }
1383
+
1299
1384
func errorCheck (ctxt * obj.Link , s * obj.LSym ) {
1300
1385
// When dynamic linking, R15 is used to access globals. Reject code that
1301
1386
// uses R15 after a global variable access.
@@ -1320,22 +1405,22 @@ func errorCheck(ctxt *obj.Link, s *obj.LSym) {
1320
1405
for len (work ) > 0 {
1321
1406
p := work [len (work )- 1 ]
1322
1407
work = work [:len (work )- 1 ]
1408
+ if progReadsR15 (p ) {
1409
+ pos := ctxt .PosTable .Pos (p .Pos )
1410
+ ctxt .Diag ("%s:%s: when dynamic linking, R15 is clobbered by a global variable access and is used here: %v" , path .Base (pos .Filename ()), pos .LineNumber (), p )
1411
+ break // only report one error
1412
+ }
1413
+ if progWritesR15 (p ) {
1414
+ // R15 is overwritten by this instruction. Its value is not junk any more.
1415
+ continue
1416
+ }
1323
1417
if q := p .To .Target (); q != nil && q .Mark & markBit == 0 {
1324
1418
q .Mark |= markBit
1325
1419
work = append (work , q )
1326
1420
}
1327
1421
if p .As == obj .AJMP || p .As == obj .ARET {
1328
1422
continue // no fallthrough
1329
1423
}
1330
- if progMentionsR15 (p ) {
1331
- if progOverwritesR15 (p ) {
1332
- // R15 is overwritten by this instruction. Its value is not junk any more.
1333
- continue
1334
- }
1335
- pos := ctxt .PosTable .Pos (p .Pos )
1336
- ctxt .Diag ("%s:%s: when dynamic linking, R15 is clobbered by a global variable access and is used here: %v" , path .Base (pos .Filename ()), pos .LineNumber (), p )
1337
- break // only report one error
1338
- }
1339
1424
if q := p .Link ; q != nil && q .Mark & markBit == 0 {
1340
1425
q .Mark |= markBit
1341
1426
work = append (work , q )
0 commit comments