Skip to content

Commit e68c027

Browse files
committed
cmd/internal/obj/riscv,cmd/link: add support for internal cgo linking on riscv64
Make it possible to internally link cgo on riscv64, which also adds support for SDYNIMPORT calls without external linking being required. This reduces the time of an ./all.bash run on a Sifive Hifive Unleashed by approximately 20% (~140 minutes down to ~110 minutes). Change-Id: I43f1348de31672718ae8676cc82f6fdc1dfee054 Reviewed-on: https://go-review.googlesource.com/c/go/+/431104 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Run-TryBot: Joel Sing <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent 70a8a41 commit e68c027

File tree

22 files changed

+572
-82
lines changed

22 files changed

+572
-82
lines changed

src/cmd/dist/build.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,7 @@ func setup() {
576576
func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool {
577577
if cgoEnabled {
578578
switch goarch {
579-
case "loong64",
580-
"mips", "mipsle", "mips64", "mips64le",
581-
"riscv64":
579+
case "loong64", "mips", "mipsle", "mips64", "mips64le":
582580
// Internally linking cgo is incomplete on some architectures.
583581
// https://golang.org/issue/14449
584582
return true

src/cmd/internal/obj/riscv/cpu.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -619,14 +619,26 @@ var unaryDst = map[obj.As]bool{
619619

620620
// Instruction encoding masks.
621621
const (
622-
// JTypeImmMask is a mask including only the immediate portion of
623-
// J-type instructions.
624-
JTypeImmMask = 0xfffff000
622+
// BTypeImmMask is a mask including only the immediate portion of
623+
// B-type instructions.
624+
BTypeImmMask = 0xfe000f80
625+
626+
// CBTypeImmMask is a mask including only the immediate portion of
627+
// CB-type instructions.
628+
CBTypeImmMask = 0x1c7c
629+
630+
// CJTypeImmMask is a mask including only the immediate portion of
631+
// CJ-type instructions.
632+
CJTypeImmMask = 0x1f7c
625633

626634
// ITypeImmMask is a mask including only the immediate portion of
627635
// I-type instructions.
628636
ITypeImmMask = 0xfff00000
629637

638+
// JTypeImmMask is a mask including only the immediate portion of
639+
// J-type instructions.
640+
JTypeImmMask = 0xfffff000
641+
630642
// STypeImmMask is a mask including only the immediate portion of
631643
// S-type instructions.
632644
STypeImmMask = 0xfe000f80

src/cmd/internal/obj/riscv/obj.go

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,12 @@ func validateRaw(ctxt *obj.Link, ins *instruction) {
11811181
}
11821182
}
11831183

1184+
// extractBitAndShift extracts the specified bit from the given immediate,
1185+
// before shifting it to the requested position and returning it.
1186+
func extractBitAndShift(imm uint32, bit, pos int) uint32 {
1187+
return ((imm >> bit) & 1) << pos
1188+
}
1189+
11841190
// encodeR encodes an R-type RISC-V instruction.
11851191
func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
11861192
enc := encode(as)
@@ -1272,6 +1278,11 @@ func encodeSF(ins *instruction) uint32 {
12721278
return encodeS(ins.as, regI(ins.rd), regF(ins.rs1), uint32(ins.imm))
12731279
}
12741280

1281+
// encodeBImmediate encodes an immediate for a B-type RISC-V instruction.
1282+
func encodeBImmediate(imm uint32) uint32 {
1283+
return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7
1284+
}
1285+
12751286
// encodeB encodes a B-type RISC-V instruction.
12761287
func encodeB(ins *instruction) uint32 {
12771288
imm := immI(ins.as, ins.imm, 13)
@@ -1281,7 +1292,7 @@ func encodeB(ins *instruction) uint32 {
12811292
if enc == nil {
12821293
panic("encodeB: could not encode instruction")
12831294
}
1284-
return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7 | enc.opcode
1295+
return encodeBImmediate(imm) | rs2<<20 | rs1<<15 | enc.funct3<<12 | enc.opcode
12851296
}
12861297

12871298
// encodeU encodes a U-type RISC-V instruction.
@@ -1315,6 +1326,37 @@ func encodeJ(ins *instruction) uint32 {
13151326
return encodeJImmediate(imm) | rd<<7 | enc.opcode
13161327
}
13171328

1329+
// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
1330+
func encodeCBImmediate(imm uint32) uint32 {
1331+
// Bit order - [8|4:3|7:6|2:1|5]
1332+
bits := extractBitAndShift(imm, 8, 7)
1333+
bits |= extractBitAndShift(imm, 4, 6)
1334+
bits |= extractBitAndShift(imm, 3, 5)
1335+
bits |= extractBitAndShift(imm, 7, 4)
1336+
bits |= extractBitAndShift(imm, 6, 3)
1337+
bits |= extractBitAndShift(imm, 2, 2)
1338+
bits |= extractBitAndShift(imm, 1, 1)
1339+
bits |= extractBitAndShift(imm, 5, 0)
1340+
return (bits>>5)<<10 | (bits&0x1f)<<2
1341+
}
1342+
1343+
// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
1344+
func encodeCJImmediate(imm uint32) uint32 {
1345+
// Bit order - [11|4|9:8|10|6|7|3:1|5]
1346+
bits := extractBitAndShift(imm, 11, 10)
1347+
bits |= extractBitAndShift(imm, 4, 9)
1348+
bits |= extractBitAndShift(imm, 9, 8)
1349+
bits |= extractBitAndShift(imm, 8, 7)
1350+
bits |= extractBitAndShift(imm, 10, 6)
1351+
bits |= extractBitAndShift(imm, 6, 5)
1352+
bits |= extractBitAndShift(imm, 7, 4)
1353+
bits |= extractBitAndShift(imm, 3, 3)
1354+
bits |= extractBitAndShift(imm, 2, 2)
1355+
bits |= extractBitAndShift(imm, 1, 1)
1356+
bits |= extractBitAndShift(imm, 5, 0)
1357+
return bits << 2
1358+
}
1359+
13181360
func encodeRawIns(ins *instruction) uint32 {
13191361
// Treat the raw value specially as a 32-bit unsigned integer.
13201362
// Nobody wants to enter negative machine code.
@@ -1324,14 +1366,34 @@ func encodeRawIns(ins *instruction) uint32 {
13241366
return uint32(ins.imm)
13251367
}
13261368

1327-
func EncodeJImmediate(imm int64) (int64, error) {
1328-
if !immIFits(imm, 21) {
1329-
return 0, fmt.Errorf("immediate %#x does not fit in 21 bits", imm)
1369+
func EncodeBImmediate(imm int64) (int64, error) {
1370+
if !immIFits(imm, 13) {
1371+
return 0, fmt.Errorf("immediate %#x does not fit in 13 bits", imm)
13301372
}
13311373
if imm&1 != 0 {
13321374
return 0, fmt.Errorf("immediate %#x is not a multiple of two", imm)
13331375
}
1334-
return int64(encodeJImmediate(uint32(imm))), nil
1376+
return int64(encodeBImmediate(uint32(imm))), nil
1377+
}
1378+
1379+
func EncodeCBImmediate(imm int64) (int64, error) {
1380+
if !immIFits(imm, 9) {
1381+
return 0, fmt.Errorf("immediate %#x does not fit in 9 bits", imm)
1382+
}
1383+
if imm&1 != 0 {
1384+
return 0, fmt.Errorf("immediate %#x is not a multiple of two", imm)
1385+
}
1386+
return int64(encodeCBImmediate(uint32(imm))), nil
1387+
}
1388+
1389+
func EncodeCJImmediate(imm int64) (int64, error) {
1390+
if !immIFits(imm, 12) {
1391+
return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
1392+
}
1393+
if imm&1 != 0 {
1394+
return 0, fmt.Errorf("immediate %#x is not a multiple of two", imm)
1395+
}
1396+
return int64(encodeCJImmediate(uint32(imm))), nil
13351397
}
13361398

13371399
func EncodeIImmediate(imm int64) (int64, error) {
@@ -1341,6 +1403,16 @@ func EncodeIImmediate(imm int64) (int64, error) {
13411403
return imm << 20, nil
13421404
}
13431405

1406+
func EncodeJImmediate(imm int64) (int64, error) {
1407+
if !immIFits(imm, 21) {
1408+
return 0, fmt.Errorf("immediate %#x does not fit in 21 bits", imm)
1409+
}
1410+
if imm&1 != 0 {
1411+
return 0, fmt.Errorf("immediate %#x is not a multiple of two", imm)
1412+
}
1413+
return int64(encodeJImmediate(uint32(imm))), nil
1414+
}
1415+
13441416
func EncodeSImmediate(imm int64) (int64, error) {
13451417
if !immIFits(imm, 12) {
13461418
return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)

src/cmd/internal/objabi/reloctype.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,33 @@ const (
285285
// LUI + I-type instruction sequence.
286286
R_RISCV_TLS_LE
287287

288+
// R_RISCV_GOT_HI20 resolves the high 20 bits of a 32-bit PC-relative GOT
289+
// address.
290+
R_RISCV_GOT_HI20
291+
292+
// R_RISCV_PCREL_HI20 resolves the high 20 bits of a 32-bit PC-relative
293+
// address.
294+
R_RISCV_PCREL_HI20
295+
296+
// R_RISCV_PCREL_LO12_I resolves the low 12 bits of a 32-bit PC-relative
297+
// address using an I-type instruction.
298+
R_RISCV_PCREL_LO12_I
299+
300+
// R_RISCV_PCREL_LO12_S resolves the low 12 bits of a 32-bit PC-relative
301+
// address using an S-type instruction.
302+
R_RISCV_PCREL_LO12_S
303+
304+
// R_RISCV_BRANCH resolves a 12-bit PC-relative branch offset.
305+
R_RISCV_BRANCH
306+
307+
// R_RISCV_RVC_BRANCH resolves an 8-bit PC-relative offset for a CB-type
308+
// instruction.
309+
R_RISCV_RVC_BRANCH
310+
311+
// R_RISCV_RVC_JUMP resolves an 11-bit PC-relative offset for a CJ-type
312+
// instruction.
313+
R_RISCV_RVC_JUMP
314+
288315
// R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses.
289316
// TODO(mundaym): remove once variants can be serialized - see issue 14218.
290317
R_PCRELDBL

src/cmd/internal/objabi/reloctype_string.go

Lines changed: 25 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/link/internal/amd64/asm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
577577
return -1
578578
}
579579

580-
func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
580+
func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
581581
if plt.Size() == 0 {
582582
// pushq got+8(IP)
583583
plt.AddUint8(0xff)

src/cmd/link/internal/arm/asm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
304304
return true
305305
}
306306

307-
func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
307+
func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
308308
if plt.Size() == 0 {
309309
// str lr, [sp, #-4]!
310310
plt.AddUint32(ctxt.Arch, 0xe52de004)

src/cmd/link/internal/arm64/asm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sy
10911091
return loader.ExtReloc{}, false
10921092
}
10931093

1094-
func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
1094+
func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
10951095
if plt.Size() == 0 {
10961096
// stp x16, x30, [sp, #-16]!
10971097
// identifying information

src/cmd/link/internal/ld/data.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -582,19 +582,17 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
582582
case 1:
583583
P[off] = byte(int8(o))
584584
case 2:
585-
if o != int64(int16(o)) {
586-
st.err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o)
585+
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int16(o)) {
586+
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
587+
} else if o != int64(int16(o)) && o != int64(uint16(o)) {
588+
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
587589
}
588590
target.Arch.ByteOrder.PutUint16(P[off:], uint16(o))
589591
case 4:
590-
if rt == objabi.R_PCREL || rt == objabi.R_CALL {
591-
if o != int64(int32(o)) {
592-
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
593-
}
594-
} else {
595-
if o != int64(int32(o)) && o != int64(uint32(o)) {
596-
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
597-
}
592+
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int32(o)) {
593+
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
594+
} else if o != int64(int32(o)) && o != int64(uint32(o)) {
595+
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
598596
}
599597
target.Arch.ByteOrder.PutUint32(P[off:], uint32(o))
600598
case 8:

src/cmd/link/internal/ld/elf.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ type ELFArch struct {
208208

209209
Reloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
210210
RelocSize uint32 // size of an ELF relocation record, must match Reloc1.
211-
SetupPLT func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
211+
SetupPLT func(ctxt *Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
212212

213213
// DynamicReadOnly can be set to true to make the .dynamic
214214
// section read-only. By default it is writable.
@@ -1585,7 +1585,7 @@ func (ctxt *Link) doelf() {
15851585
// S390X uses .got instead of .got.plt
15861586
gotplt = got
15871587
}
1588-
thearch.ELF.SetupPLT(ctxt, plt, gotplt, dynamic.Sym())
1588+
thearch.ELF.SetupPLT(ctxt, ctxt.loader, plt, gotplt, dynamic.Sym())
15891589

15901590
/*
15911591
* .dynamic table

src/cmd/link/internal/ld/pcln.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"internal/buildcfg"
1616
"os"
1717
"path/filepath"
18+
"strings"
1819
)
1920

2021
const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go
@@ -99,6 +100,19 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
99100
}
100101

101102
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
103+
if ctxt.Target.IsRISCV64() {
104+
// Avoid adding local symbols to the pcln table - RISC-V
105+
// linking generates a very large number of these, particularly
106+
// for HI20 symbols (which we need to load in order to be able
107+
// to resolve relocations). Unnecessarily including all of
108+
// these symbols quickly blows out the size of the pcln table
109+
// and overflows hash buckets.
110+
symName := ctxt.loader.SymName(s)
111+
if symName == "" || strings.HasPrefix(symName, ".L") {
112+
return false
113+
}
114+
}
115+
102116
// We want to generate func table entries only for the "lowest
103117
// level" symbols, not containers of subsymbols.
104118
return !container.Has(s)

0 commit comments

Comments
 (0)