feat(asm): emit the riscv64 local-exec TLS sequence
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
f8dbd4f017
commit
f2892e4f59
6 files changed
+114
-29
No files matched your search
@@ -24,6 +24,8 @@ const (
|
|||||||
rRISCVPCRELHI20 = 23 // R_RISCV_PCREL_HI20
|
rRISCVPCRELHI20 = 23 // R_RISCV_PCREL_HI20
|
||||||
rRISCVPCRELLO12I = 24 // R_RISCV_PCREL_LO12_I
|
rRISCVPCRELLO12I = 24 // R_RISCV_PCREL_LO12_I
|
||||||
rRISCVPCRELLO12S = 25 // R_RISCV_PCREL_LO12_S
|
rRISCVPCRELLO12S = 25 // R_RISCV_PCREL_LO12_S
|
||||||
|
rRISCVTPRELHI20 = 29 // R_RISCV_TPREL_HI20
|
||||||
|
rRISCVTPRELLO12I = 30 // R_RISCV_TPREL_LO12_I
|
||||||
// R_RISCV_32 (debug/elf 1): the absolute 32-bit address of a symbol,
|
// R_RISCV_32 (debug/elf 1): the absolute 32-bit address of a symbol,
|
||||||
// the R_ADDR shape a 4-byte DATA field carries. R_RISCV_64 (2) lives
|
// the R_ADDR shape a 4-byte DATA field carries. R_RISCV_64 (2) lives
|
||||||
// with the DWARF fixup constants as rRISCVAbs64.
|
// with the DWARF fixup constants as rRISCVAbs64.
|
||||||
@@ -128,6 +130,15 @@ func (img *Image) ELFRISCVObject() ([]byte, error) {
|
|||||||
)
|
)
|
||||||
case RelRISCVJal:
|
case RelRISCVJal:
|
||||||
relas = append(relas, elfRela{off: uint64(fn.Offset + r.Off), typ: rRISCVJAL, sym: idx, addend: r.Addend})
|
relas = append(relas, elfRela{off: uint64(fn.Offset + r.Off), typ: rRISCVJAL, sym: idx, addend: r.Addend})
|
||||||
|
case RelRISCVTLSLE:
|
||||||
|
// The local-exec pair splits into the TPREL HI20 on the LUI
|
||||||
|
// and the TPREL LO12_I on the ADDIW, both against the symbol
|
||||||
|
// (a thread offset, not PC-relative, so the LO12 needs no
|
||||||
|
// label indirection).
|
||||||
|
relas = append(relas,
|
||||||
|
elfRela{off: uint64(fn.Offset + r.Off), typ: rRISCVTPRELHI20, sym: idx, addend: r.Addend},
|
||||||
|
elfRela{off: uint64(fn.Offset + r.Off + 4), typ: rRISCVTPRELLO12I, sym: idx, addend: r.Addend},
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("relocation kind %v unsupported in ELF emission", r.Kind)
|
return nil, fmt.Errorf("relocation kind %v unsupported in ELF emission", r.Kind)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -30,6 +30,8 @@ func (img *Image) GOObjectRISCV(pkgPath, srcPath string) ([]byte, error) {
|
|||||||
return relocRISCVPcrelStype, 8
|
return relocRISCVPcrelStype, 8
|
||||||
case RelRISCVJal:
|
case RelRISCVJal:
|
||||||
return relocRISCVJal, 4
|
return relocRISCVJal, 4
|
||||||
|
case RelRISCVTLSLE:
|
||||||
|
return relocRISCVTLSLE, 8
|
||||||
default:
|
default:
|
||||||
return relocRISCVPcrelItype, 8
|
return relocRISCVPcrelItype, 8
|
||||||
}
|
}
|
||||||
@@ -38,11 +40,13 @@ func (img *Image) GOObjectRISCV(pkgPath, srcPath string) ([]byte, error) {
|
|||||||
|
|
||||||
// RISC-V relocation types (cmd/internal/objabi). The Go linker applies
|
// RISC-V relocation types (cmd/internal/objabi). The Go linker applies
|
||||||
// R_RISCV_PCREL_ITYPE/STYPE to an AUIPC + I/S-type instruction pair as a
|
// R_RISCV_PCREL_ITYPE/STYPE to an AUIPC + I/S-type instruction pair as a
|
||||||
// single 8-byte field; R_RISCV_JAL covers a single 4-byte J-type instruction.
|
// single 8-byte field; R_RISCV_JAL covers a single 4-byte J-type instruction;
|
||||||
|
// R_RISCV_TLS_LE covers the LUI + I-type pair of a local-exec TLS reference.
|
||||||
const (
|
const (
|
||||||
relocRISCVJal = 59 // R_RISCV_JAL
|
relocRISCVJal = 59 // R_RISCV_JAL
|
||||||
relocRISCVPcrelItype = 62 // R_RISCV_PCREL_ITYPE
|
relocRISCVPcrelItype = 62 // R_RISCV_PCREL_ITYPE
|
||||||
relocRISCVPcrelStype = 63 // R_RISCV_PCREL_STYPE
|
relocRISCVPcrelStype = 63 // R_RISCV_PCREL_STYPE
|
||||||
|
relocRISCVTLSLE = 65 // R_RISCV_TLS_LE
|
||||||
)
|
)
|
||||||
|
|
||||||
// toolchainObjectPreambleRISCV returns the "go object ...\n!\n" header
|
// toolchainObjectPreambleRISCV returns the "go object ...\n!\n" header
|
||||||
|
|||||||
+35
-1
@@ -98,6 +98,7 @@ const (
|
|||||||
RelRISCVPCRELIType // R_RISCV_PCREL_ITYPE (AUIPC + I-type pair)
|
RelRISCVPCRELIType // R_RISCV_PCREL_ITYPE (AUIPC + I-type pair)
|
||||||
RelRISCVPCRELSType // R_RISCV_PCREL_STYPE (AUIPC + S-type pair)
|
RelRISCVPCRELSType // R_RISCV_PCREL_STYPE (AUIPC + S-type pair)
|
||||||
RelRISCVJal // R_RISCV_JAL (J-type call)
|
RelRISCVJal // R_RISCV_JAL (J-type call)
|
||||||
|
RelRISCVTLSLE // R_RISCV_TLS_LE (LUI + I-type local-exec pair)
|
||||||
RelLoong64AddrHi // R_LOONG64_ADDR_HI (pcalau12i)
|
RelLoong64AddrHi // R_LOONG64_ADDR_HI (pcalau12i)
|
||||||
RelLoong64AddrLo // R_LOONG64_ADDR_LO (addi.d/ld/st)
|
RelLoong64AddrLo // R_LOONG64_ADDR_LO (addi.d/ld/st)
|
||||||
RelArm64Addr // R_ADDRARM64 (ADRP + ADD pair)
|
RelArm64Addr // R_ADDRARM64 (ADRP + ADD pair)
|
||||||
@@ -335,6 +336,35 @@ func AssembleFile(f *ast.File, opts ...AssembleOption) (*Image, error) {
|
|||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// riscvTLSSymbols names the symbols the file declares with the TLSBSS flag
|
||||||
|
// (or its legacy numeric constant 256 from textflag.h): the assembler gives
|
||||||
|
// their SB references the local-exec TLS sequence.
|
||||||
|
func riscvTLSSymbols(f *ast.File) map[string]bool {
|
||||||
|
var tls map[string]bool
|
||||||
|
for _, d := range f.Decls {
|
||||||
|
gd, ok := d.(*ast.Globl)
|
||||||
|
if !ok || gd.Name == nil || gd.Name.Pseudo != "SB" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, fl := range gd.Flags {
|
||||||
|
isTLS := fl == "TLSBSS"
|
||||||
|
if !isTLS {
|
||||||
|
if n, err := strconv.Atoi(fl); err == nil && n&256 != 0 {
|
||||||
|
isTLS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isTLS {
|
||||||
|
if tls == nil {
|
||||||
|
tls = map[string]bool{}
|
||||||
|
}
|
||||||
|
tls[gd.Name.Name] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tls
|
||||||
|
}
|
||||||
|
|
||||||
// AssembleFileRISCV assembles every TEXT function of a parsed RISC-V file
|
// AssembleFileRISCV assembles every TEXT function of a parsed RISC-V file
|
||||||
// and lays out its static symbols (GLOBL/DATA) in a data section behind the
|
// and lays out its static symbols (GLOBL/DATA) in a data section behind the
|
||||||
// code. SB references in the code are encoded as AUIPC pairs with zero
|
// code. SB references in the code are encoded as AUIPC pairs with zero
|
||||||
@@ -344,6 +374,10 @@ func AssembleFileRISCV(f *ast.File) (*Image, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// The symbols the file declares TLSBSS resolve through the local-exec
|
||||||
|
// sequence (LUI + ADDIW + ADD of TP), exactly as the toolchain routes
|
||||||
|
// every SB reference whose symbol carries the STLSBSS type.
|
||||||
|
tlsSyms := riscvTLSSymbols(f)
|
||||||
// The pooled $i64 constants the wide MOV immediate loads refer to join
|
// The pooled $i64 constants the wide MOV immediate loads refer to join
|
||||||
// the declared data as read-only symbols, deduplicated across the file
|
// the declared data as read-only symbols, deduplicated across the file
|
||||||
// (the toolchain synthesises the same symbols into its rodata).
|
// (the toolchain synthesises the same symbols into its rodata).
|
||||||
@@ -355,7 +389,7 @@ func AssembleFileRISCV(f *ast.File) (*Image, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
code, labels, relocs, lines, spadj, lits, err := assembleRISCV(t)
|
code, labels, relocs, lines, spadj, lits, err := assembleRISCV(t, tlsSyms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s: %w", t.Name.Name, err)
|
return nil, fmt.Errorf("%s: %w", t.Name.Name, err)
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-9
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
// assembleRISCV assembles a RISC-V TEXT function body into machine code.
|
// assembleRISCV assembles a RISC-V TEXT function body into machine code.
|
||||||
// It handles the full RV64IMAFDC instruction set including RVC compression.
|
// It handles the full RV64IMAFDC instruction set including RVC compression.
|
||||||
func assembleRISCV(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, []RiscvLiteral, error) {
|
func assembleRISCV(t *ast.Text, tlsSyms map[string]bool) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, []RiscvLiteral, error) {
|
||||||
fi := riscvComputeFrame(t)
|
fi := riscvComputeFrame(t)
|
||||||
prologue := riscvPrologue(fi)
|
prologue := riscvPrologue(fi)
|
||||||
guardLen, err := riscvGuardLen(fi)
|
guardLen, err := riscvGuardLen(fi)
|
||||||
@@ -66,7 +66,7 @@ func assembleRISCV(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, [
|
|||||||
}
|
}
|
||||||
recs = append(recs, instrRec{instr: s, pad: pendingPad})
|
recs = append(recs, instrRec{instr: s, pad: pendingPad})
|
||||||
pendingPad = 0
|
pendingPad = 0
|
||||||
pos += riscvInstrSize(s, fi)
|
pos += riscvInstrSize(s, fi, tlsSyms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ func assembleRISCV(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, [
|
|||||||
pc := len(prologue)
|
pc := len(prologue)
|
||||||
for i := range recs {
|
for i := range recs {
|
||||||
branchLike := isBranchLike(recs[i].instr.Mnemonic.Text) || riscvIsCondBranch(recs[i].instr.Mnemonic.Text)
|
branchLike := isBranchLike(recs[i].instr.Mnemonic.Text) || riscvIsCondBranch(recs[i].instr.Mnemonic.Text)
|
||||||
code, err := encodeRISCVInstr(recs[i].instr, pc, offsets, fi, nil, nil, lits) // no relocs in Pass 2
|
code, err := encodeRISCVInstr(recs[i].instr, pc, offsets, fi, nil, nil, lits, tlsSyms) // no relocs in Pass 2
|
||||||
if err != nil && !(branchLike && riscvIsRangeError(err)) {
|
if err != nil && !(branchLike && riscvIsRangeError(err)) {
|
||||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", recs[i].instr.Mnemonic.Text, err)
|
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", recs[i].instr.Mnemonic.Text, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
code = make([]byte, riscvInstrSize(recs[i].instr, fi))
|
code = make([]byte, riscvInstrSize(recs[i].instr, fi, tlsSyms))
|
||||||
}
|
}
|
||||||
recs[i].code = code
|
recs[i].code = code
|
||||||
pc += len(code)
|
pc += len(code)
|
||||||
@@ -252,7 +252,7 @@ func assembleRISCV(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, [
|
|||||||
code = r.code
|
code = r.code
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
code, err = encodeRISCVInstr(r.instr, pc, offsets, fi, &relocs, pcRelPcs, lits)
|
code, err = encodeRISCVInstr(r.instr, pc, offsets, fi, &relocs, pcRelPcs, lits, tlsSyms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -376,7 +376,7 @@ func riscvFenceFlag(op *ast.Operand) (uint32, bool) {
|
|||||||
// Most instructions are 4 bytes; MOV with a large immediate and I-type
|
// Most instructions are 4 bytes; MOV with a large immediate and I-type
|
||||||
// arithmetic with a large immediate expand to several (possibly compressed)
|
// arithmetic with a large immediate expand to several (possibly compressed)
|
||||||
// instructions.
|
// instructions.
|
||||||
func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
|
func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo, tlsSyms map[string]bool) int {
|
||||||
mnem := instr.Mnemonic.Text
|
mnem := instr.Mnemonic.Text
|
||||||
ops := instr.Operands
|
ops := instr.Operands
|
||||||
mnem = riscvNormalisePseudo(mnem)
|
mnem = riscvNormalisePseudo(mnem)
|
||||||
@@ -401,6 +401,14 @@ func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
|
|||||||
return 8
|
return 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// A TLSBSS symbol's memory reference takes the 16-byte local-exec
|
||||||
|
// sequence (LUI + ADDIW + ADD of TP + the access).
|
||||||
|
tlsRef := func(op *ast.Operand) bool {
|
||||||
|
return op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" && tlsSyms[op.Addr.Sym.Name]
|
||||||
|
}
|
||||||
|
if tlsRef(ops[0]) || tlsRef(ops[1]) {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
// MOV $sym(SB), rd → 8 bytes (AUIPC + ADDI).
|
// MOV $sym(SB), rd → 8 bytes (AUIPC + ADDI).
|
||||||
if isImmOperand(ops[0]) && ops[0].Imm.Sym != nil && ops[0].Imm.Sym.Pseudo == "SB" {
|
if isImmOperand(ops[0]) && ops[0].Imm.Sym != nil && ops[0].Imm.Sym.Pseudo == "SB" {
|
||||||
return 8
|
return 8
|
||||||
@@ -1088,7 +1096,7 @@ func riscvCheckJumpOffset(target string, off int32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encodeRISCVInstr encodes a single RISC-V instruction.
|
// encodeRISCVInstr encodes a single RISC-V instruction.
|
||||||
func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscvFrameInfo, relocs *[]Reloc, pcRelPcs map[*ast.Instr]int, lits *riscvLiterals) ([]byte, error) {
|
func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscvFrameInfo, relocs *[]Reloc, pcRelPcs map[*ast.Instr]int, lits *riscvLiterals, tlsSyms map[string]bool) ([]byte, error) {
|
||||||
mnem := instr.Mnemonic.Text
|
mnem := instr.Mnemonic.Text
|
||||||
ops := instr.Operands
|
ops := instr.Operands
|
||||||
mnem = riscvNormalisePseudo(mnem)
|
mnem = riscvNormalisePseudo(mnem)
|
||||||
@@ -1269,7 +1277,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
|||||||
// (MOVB/MOVH/MOVW and unsigned forms) select the access width, and
|
// (MOVB/MOVH/MOVW and unsigned forms) select the access width, and
|
||||||
// MOVD/MOVF address the FP registers.
|
// MOVD/MOVF address the FP registers.
|
||||||
case "MOV", "MOVB", "MOVBU", "MOVH", "MOVHU", "MOVW", "MOVWU", "MOVF", "MOVD":
|
case "MOV", "MOVB", "MOVBU", "MOVH", "MOVHU", "MOVW", "MOVWU", "MOVF", "MOVD":
|
||||||
return encodeRISCVMov(instr, fi, relocs, lits)
|
return encodeRISCVMov(instr, fi, relocs, lits, tlsSyms)
|
||||||
|
|
||||||
// JALR: indirect jump/call. Plan 9: JALR rs1, rd or JALR offset(rs1).
|
// JALR: indirect jump/call. Plan 9: JALR rs1, rd or JALR offset(rs1).
|
||||||
case "JALR":
|
case "JALR":
|
||||||
@@ -1915,7 +1923,7 @@ func isImmOperand(op *ast.Operand) bool {
|
|||||||
// - MOV Rs, (Rd) register-relative store
|
// - MOV Rs, (Rd) register-relative store
|
||||||
// - MOV Rs, Rd register-to-register move (ADDI $0)
|
// - MOV Rs, Rd register-to-register move (ADDI $0)
|
||||||
// - MOV $imm, Rd load immediate (ADDI or LUI+ADDIW)
|
// - MOV $imm, Rd load immediate (ADDI or LUI+ADDIW)
|
||||||
func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits *riscvLiterals) ([]byte, error) {
|
func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits *riscvLiterals, tlsSyms map[string]bool) ([]byte, error) {
|
||||||
ops := instr.Operands
|
ops := instr.Operands
|
||||||
if len(ops) != 2 {
|
if len(ops) != 2 {
|
||||||
return nil, fmt.Errorf("MOV expects 2 operands, got %d", len(ops))
|
return nil, fmt.Errorf("MOV expects 2 operands, got %d", len(ops))
|
||||||
@@ -2006,10 +2014,16 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits *
|
|||||||
// Memory → register (load).
|
// Memory → register (load).
|
||||||
if isMemOperand(src) && !isMemOperand(dst) {
|
if isMemOperand(src) && !isMemOperand(dst) {
|
||||||
rd := regFromOperand(dst)
|
rd := regFromOperand(dst)
|
||||||
|
// MOV sym(SB), rd, load from static data. A TLSBSS symbol takes
|
||||||
|
// the local-exec sequence: LUI + ADDIW carry the offset against TP,
|
||||||
|
// the ADD folds the thread pointer in, the access reads through TMP.
|
||||||
if src.Addr.Sym != nil && src.Addr.Sym.Pseudo == "SB" {
|
if src.Addr.Sym != nil && src.Addr.Sym.Pseudo == "SB" {
|
||||||
if rd < 0 {
|
if rd < 0 {
|
||||||
return nil, fmt.Errorf("MOV sym(SB): invalid destination register")
|
return nil, fmt.Errorf("MOV sym(SB): invalid destination register")
|
||||||
}
|
}
|
||||||
|
if tlsSyms[src.Addr.Sym.Name] {
|
||||||
|
return riscvTLSBytes(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), false), false, rd, src.Addr.Sym, relocs), nil
|
||||||
|
}
|
||||||
return encodeRISCVSBLoad(src.Addr.Sym, rd, relocs), nil
|
return encodeRISCVSBLoad(src.Addr.Sym, rd, relocs), nil
|
||||||
}
|
}
|
||||||
rs1, off := memFromOperandWithFrame(src, fi)
|
rs1, off := memFromOperandWithFrame(src, fi)
|
||||||
@@ -2022,10 +2036,15 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits *
|
|||||||
// Register → memory (store).
|
// Register → memory (store).
|
||||||
if !isMemOperand(src) && isMemOperand(dst) {
|
if !isMemOperand(src) && isMemOperand(dst) {
|
||||||
rs2 := regFromOperand(src)
|
rs2 := regFromOperand(src)
|
||||||
|
// MOV rd, sym(SB), store to static data. A TLSBSS symbol takes
|
||||||
|
// the local-exec sequence with the store through TMP.
|
||||||
if dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB" {
|
if dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB" {
|
||||||
if rs2 < 0 {
|
if rs2 < 0 {
|
||||||
return nil, fmt.Errorf("MOV rd, sym(SB): invalid source register")
|
return nil, fmt.Errorf("MOV rd, sym(SB): invalid source register")
|
||||||
}
|
}
|
||||||
|
if tlsSyms[dst.Addr.Sym.Name] {
|
||||||
|
return riscvTLSBytes(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), true), true, rs2, dst.Addr.Sym, relocs), nil
|
||||||
|
}
|
||||||
return encodeRISCVSBStore(dst.Addr.Sym, rs2, relocs), nil
|
return encodeRISCVSBStore(dst.Addr.Sym, rs2, relocs), nil
|
||||||
}
|
}
|
||||||
rs1, off := memFromOperandWithFrame(dst, fi)
|
rs1, off := memFromOperandWithFrame(dst, fi)
|
||||||
@@ -2497,6 +2516,23 @@ func encodeRISCVSBFPLoad(name string, rd int, double bool, relocs *[]Reloc) []by
|
|||||||
return append(wordLE(auipc), wordLE(fl)...)
|
return append(wordLE(auipc), wordLE(fl)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// riscvTLSBytes emits the toolchain's local-exec TLS sequence for an SB
|
||||||
|
// reference to a TLSBSS symbol: LUI TMP + ADDIW TMP (the 8-byte
|
||||||
|
// R_RISCV_TLS_LE field the linker patches as the offset from the thread
|
||||||
|
// pointer), ADD TMP, TP, TMP, then the access at zero offset through TMP.
|
||||||
|
func riscvTLSBytes(enc riscvEnc, store bool, reg int, sym *ast.Symbol, relocs *[]Reloc) []byte {
|
||||||
|
if relocs != nil {
|
||||||
|
*relocs = append(*relocs, Reloc{Off: 0, After: 8, Name: sym.Name, Kind: RelRISCVTLSLE, Addend: sym.Offset})
|
||||||
|
}
|
||||||
|
out := wordLE(riscvUType(riscvEnc{0x37, 0x0, 0x00}, 31, 0)) // LUI X31, hi
|
||||||
|
out = append(out, wordLE(riscvIType(riscvEnc{0x1b, 0x0, 0x00}, 31, 31, 0))...) // ADDIW X31, X31, lo
|
||||||
|
out = append(out, wordLE(riscvRType(riscvInstrTable["ADD"], 31, 31, 4))...) // ADD X31, X31, X4(TP)
|
||||||
|
if store {
|
||||||
|
return append(out, wordLE(riscvSType(enc, 31, reg, 0))...)
|
||||||
|
}
|
||||||
|
return append(out, wordLE(riscvIType(enc, reg, 31, 0))...)
|
||||||
|
}
|
||||||
|
|
||||||
// RiscvLiteral is one pooled 64-bit constant: a MOV whose immediate sits
|
// RiscvLiteral is one pooled 64-bit constant: a MOV whose immediate sits
|
||||||
// beyond both the 32-bit span and the shift sequences loads its bits from a
|
// beyond both the 32-bit span and the shift sequences loads its bits from a
|
||||||
// read-only data symbol named like the toolchain's $i64 pool.
|
// read-only data symbol named like the toolchain's $i64 pool.
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func TestRISCVCompressedRange(t *testing.T) {
|
|||||||
asmOne := func(t *testing.T, stmt string) error {
|
asmOne := func(t *testing.T, stmt string) error {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·c(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·c(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
||||||
_, _, _, _, _, _, err := assembleRISCV(fn)
|
_, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, s := range []string{
|
for _, s := range []string{
|
||||||
|
|||||||
+17
-17
@@ -35,7 +35,7 @@ func firstTextRISCV(t *testing.T, src string) *ast.Text {
|
|||||||
// assembleRISCVHelper assembles one TEXT function and returns its code bytes.
|
// assembleRISCVHelper assembles one TEXT function and returns its code bytes.
|
||||||
func assembleRISCVHelper(t *testing.T, fn *ast.Text) []byte {
|
func assembleRISCVHelper(t *testing.T, fn *ast.Text) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ TEXT ·wide(SB), NOSPLIT, $0
|
|||||||
AUIPC $524288, X10
|
AUIPC $524288, X10
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err == nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err == nil {
|
||||||
t.Error("AUIPC $524288: expected the 20-bit range error, got none")
|
t.Error("AUIPC $524288: expected the 20-bit range error, got none")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -868,7 +868,7 @@ TEXT ·argfp(SB), NOSPLIT, $0
|
|||||||
MOV $arg(FP), X10
|
MOV $arg(FP), X10
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -891,7 +891,7 @@ TEXT ·book(SB), NOSPLIT, $0-8
|
|||||||
MOV X10, ret+0(FP)
|
MOV X10, ret+0(FP)
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -915,7 +915,7 @@ TEXT ·slots(SB), NOSPLIT, $0-0
|
|||||||
JMP -3(PC)
|
JMP -3(PC)
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -943,7 +943,7 @@ TEXT ·wide(SB), NOSPLIT, $0-0
|
|||||||
MOV $0x000fffffffffffda, X5
|
MOV $0x000fffffffffffda, X5
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1003,7 +1003,7 @@ TEXT ·calltest(SB), NOSPLIT, $0
|
|||||||
CALL ext(SB)
|
CALL ext(SB)
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
code, _, relocs, _, _, _, err := assembleRISCV(fn)
|
code, _, relocs, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("assemble: %v", err)
|
t.Fatalf("assemble: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1032,7 +1032,7 @@ TEXT ·calllocal(SB), NOSPLIT, $0
|
|||||||
sub:
|
sub:
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
_, _, _, _, _, _, err := assembleRISCV(fn)
|
_, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error for CALL to local label, got nil")
|
t.Error("expected error for CALL to local label, got nil")
|
||||||
}
|
}
|
||||||
@@ -1066,7 +1066,7 @@ func encodeOneInstrRISCV(t *testing.T, src string, pc int, offsets map[string]in
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\n"+src)
|
fn := firstTextRISCV(t, "#include \"textflag.h\"\n"+src)
|
||||||
instr := fn.Body[0].(*ast.Instr)
|
instr := fn.Body[0].(*ast.Instr)
|
||||||
return encodeRISCVInstr(instr, pc, offsets, riscvFrameInfo{}, nil, nil, nil)
|
return encodeRISCVInstr(instr, pc, offsets, riscvFrameInfo{}, nil, nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRISCVBranchJumpRange checks that displacements beyond the B-type span
|
// TestRISCVBranchJumpRange checks that displacements beyond the B-type span
|
||||||
@@ -1115,7 +1115,7 @@ func TestRISCVBranchFarBody(t *testing.T) {
|
|||||||
}
|
}
|
||||||
sb.WriteString("done:\n\tRET\n")
|
sb.WriteString("done:\n\tRET\n")
|
||||||
fn := firstTextRISCV(t, sb.String())
|
fn := firstTextRISCV(t, sb.String())
|
||||||
out, _, _, _, _, _, err := assembleRISCV(fn)
|
out, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1141,7 +1141,7 @@ TEXT ·csrhi(SB), NOSPLIT, $0
|
|||||||
CSRRW $4096, X10, X11
|
CSRRW $4096, X10, X11
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err == nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err == nil {
|
||||||
t.Error("expected an out-of-range error for CSR $4096, got none")
|
t.Error("expected an out-of-range error for CSR $4096, got none")
|
||||||
}
|
}
|
||||||
fn = firstTextRISCV(t, `#include "textflag.h"
|
fn = firstTextRISCV(t, `#include "textflag.h"
|
||||||
@@ -1149,7 +1149,7 @@ TEXT ·csrmax(SB), NOSPLIT, $0
|
|||||||
CSRRW $4095, X10, X11
|
CSRRW $4095, X10, X11
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err != nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err != nil {
|
||||||
t.Errorf("CSR $4095 must assemble: %v", err)
|
t.Errorf("CSR $4095 must assemble: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1166,7 +1166,7 @@ func TestRISCV_Imm64Rejected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, src := range cases {
|
for _, src := range cases {
|
||||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·wide(SB), NOSPLIT, $0\n\t"+src+"\n\tRET\n")
|
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·wide(SB), NOSPLIT, $0\n\t"+src+"\n\tRET\n")
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err == nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err == nil {
|
||||||
t.Errorf("%s: expected an out-of-range error, got none", src)
|
t.Errorf("%s: expected an out-of-range error, got none", src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1179,7 +1179,7 @@ TEXT ·edge(SB), NOSPLIT, $0
|
|||||||
SUB $0x80000000, X12, X13
|
SUB $0x80000000, X12, X13
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err != nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err != nil {
|
||||||
t.Errorf("int32-span immediates must assemble: %v", err)
|
t.Errorf("int32-span immediates must assemble: %v", err)
|
||||||
}
|
}
|
||||||
// Beyond the span the MOV forms materialise the constant like the
|
// Beyond the span the MOV forms materialise the constant like the
|
||||||
@@ -1189,7 +1189,7 @@ TEXT ·pool(SB), NOSPLIT, $0
|
|||||||
MOV $0x123456789, X10
|
MOV $0x123456789, X10
|
||||||
RET
|
RET
|
||||||
`)
|
`)
|
||||||
if _, _, _, _, _, _, err := assembleRISCV(fn); err != nil {
|
if _, _, _, _, _, _, err := assembleRISCV(fn, nil); err != nil {
|
||||||
t.Errorf("MOV with a 64-bit immediate must assemble: %v", err)
|
t.Errorf("MOV with a 64-bit immediate must assemble: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1424,7 +1424,7 @@ func TestRISCV_rawDataRange(t *testing.T) {
|
|||||||
asmOne := func(t *testing.T, stmt string) ([]byte, error) {
|
asmOne := func(t *testing.T, stmt string) ([]byte, error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·w(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·w(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
||||||
code, _, _, _, _, _, err := assembleRISCV(fn)
|
code, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
t.Run("word bounds", func(t *testing.T) {
|
t.Run("word bounds", func(t *testing.T) {
|
||||||
@@ -1471,7 +1471,7 @@ func TestRISCV_shiftImmediateRange(t *testing.T) {
|
|||||||
asmOne := func(t *testing.T, stmt string) error {
|
asmOne := func(t *testing.T, stmt string) error {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·s(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·s(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
||||||
_, _, _, _, _, _, err := assembleRISCV(fn)
|
_, _, _, _, _, _, err := assembleRISCV(fn, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, s := range []string{
|
for _, s := range []string{
|
||||||
|
|||||||
Reference in new issue
Block a user