feat(asm): the TLS access pair in the toolchain's one-instruction form
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+10
-7
@@ -15,13 +15,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
branch optimiser does; symbol immediates (`MOVQ $sym(SB), AX`)
|
branch optimiser does; symbol immediates (`MOVQ $sym(SB), AX`)
|
||||||
assemble to the toolchain's RIP-relative LEA with an R_PCREL
|
assemble to the toolchain's RIP-relative LEA with an R_PCREL
|
||||||
relocation; negated constant expressions in operands (`ADJSP
|
relocation; negated constant expressions in operands (`ADJSP
|
||||||
$-(REGS - 8)`, the shape the cgo ABI macros write) fold; and `gasm
|
$-(REGS - 8)`, the shape the cgo ABI macros write) fold; the TLS
|
||||||
asm` predefines the `GOARCH_<arch>` and `GOOS_<goos>` macros the go
|
access pair assembles as the toolchain's one-instruction form (the
|
||||||
command passes to `go tool asm`, so GOROOT headers' `#ifdef
|
bare `MOVQ TLS, r` load nops out and `off(r)(TLS*1)` folds to the
|
||||||
GOARCH_amd64` platform blocks (`go_tls.h`'s `get_tls` and friends)
|
segment-prefixed absolute whose disp32 carries the R_TLSLE
|
||||||
select as intended. The GOROOT corpus measure moves to 261 of 353
|
relocation, per-GOOS); and `gasm asm` predefines the `GOARCH_<arch>`
|
||||||
files assembling for every target architecture (73.9 %), 87.5 % of
|
and `GOOS_<goos>` macros the go command passes to `go tool asm`, so
|
||||||
the real-code corpus, from 70.8 % and 82.2 %.
|
GOROOT headers' `#ifdef GOARCH_amd64` platform blocks (`go_tls.h`'s
|
||||||
|
`get_tls` and friends) select as intended. The GOROOT corpus measure
|
||||||
|
moves to 267 of 353 files assembling for every target architecture
|
||||||
|
(75.6 %), 89.4 % of the real-code corpus, from 70.8 % and 82.2 %.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|||||||
+95
-1
@@ -38,10 +38,25 @@ func Assemble(t *ast.Text) ([]byte, map[string]int, error) {
|
|||||||
// rejects SB operands outright (single-function assembly cannot resolve
|
// rejects SB operands outright (single-function assembly cannot resolve
|
||||||
// them). When allowExternal is set, a reference to a symbol no GLOBL in the
|
// them). When allowExternal is set, a reference to a symbol no GLOBL in the
|
||||||
// file defines is recorded as an external relocation instead of failing
|
// file defines is recorded as an external relocation instead of failing
|
||||||
// the object-file emitters resolve it at link time.
|
// the object-file emitters resolve it at link time. goos selects the TLS
|
||||||
|
// access form: the empty default behaves as linux.
|
||||||
type linkInfo struct {
|
type linkInfo struct {
|
||||||
symbols map[string]bool
|
symbols map[string]bool
|
||||||
allowExternal bool
|
allowExternal bool
|
||||||
|
goos string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsOneInsn reports the one-instruction TLS form, obj6.go's
|
||||||
|
// CanUse1InsnTLS for the GOOS gasm supports: the bare TLS load nops out and
|
||||||
|
// the (TLS*1) index folds to a segment-absolute access. Windows and plan9
|
||||||
|
// keep the two-instruction form; shared linux does too, which gasm's raw
|
||||||
|
// path does not model and therefore does not select.
|
||||||
|
func (l *linkInfo) tlsOneInsn() bool {
|
||||||
|
switch l.goos {
|
||||||
|
case "", "linux", "freebsd":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// sbPatch is a function-relative static-symbol relocation: the disp32 field
|
// sbPatch is a function-relative static-symbol relocation: the disp32 field
|
||||||
@@ -834,6 +849,15 @@ func encodeNormal(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch
|
|||||||
}
|
}
|
||||||
return e.out, ps, nil, nil
|
return e.out, ps, nil, nil
|
||||||
}
|
}
|
||||||
|
// MOVQ/MOVL TLS, r: the bare TLS load. The toolchain's progedit nops
|
||||||
|
// it out on the one-instruction TLS systems (linux and freebsd, not
|
||||||
|
// shared) and encodes the segment-prefixed load elsewhere; get_tls(r),
|
||||||
|
// the macro GOROOT's go_tls.h defines, expands to exactly this
|
||||||
|
// statement, and the toolchain's pairing pass removes it whenever the
|
||||||
|
// following instruction's (TLS*1) index folds.
|
||||||
|
if (mnemUpper == "MOVQ" || mnemUpper == "MOVL") && len(s.Operands) == 2 && isBareTLS(s.Operands[0]) {
|
||||||
|
return encodeTLSBaseLoad(s, fi, link)
|
||||||
|
}
|
||||||
_, size := splitSize(mnemUpper)
|
_, size := splitSize(mnemUpper)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size = 8
|
size = 8
|
||||||
@@ -853,10 +877,67 @@ func encodeNormal(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch
|
|||||||
ps := make([]sbPatch, len(e.patches))
|
ps := make([]sbPatch, len(e.patches))
|
||||||
for i, p := range e.patches {
|
for i, p := range e.patches {
|
||||||
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend}
|
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend}
|
||||||
|
if p.tls {
|
||||||
|
ps[i].kind = RelTLSLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return e.out, ps, e.floatPoolList(), nil
|
return e.out, ps, e.floatPoolList(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isBareTLS reports whether the operand is the bare TLS pseudo-register
|
||||||
|
// load source, the expansion of go_tls.h's get_tls(r) macro.
|
||||||
|
func isBareTLS(op *ast.Operand) bool {
|
||||||
|
return op.Kind == ast.OpAddr && op.Addr.Sym != nil &&
|
||||||
|
op.Addr.Sym.Pseudo == "" && op.Addr.Sym.Name == "TLS" &&
|
||||||
|
op.Addr.Base == "" && op.Addr.Index == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeTLSBaseLoad assembles MOVQ/MOVL TLS, r. On the one-instruction TLS
|
||||||
|
// systems (linux and freebsd outside -shared, obj6.go's CanUse1InsnTLS) the
|
||||||
|
// statement nops out: the following (TLS*1) access folds to a direct
|
||||||
|
// segment-absolute load. The two-instruction systems keep the segment load,
|
||||||
|
// nine bytes with the R_TLSLE patch site at the disp32.
|
||||||
|
func encodeTLSBaseLoad(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch, []floatPoolEntry, error) {
|
||||||
|
_, size := splitSize(strings.ToUpper(s.Mnemonic.Text))
|
||||||
|
if size == 0 {
|
||||||
|
size = 8
|
||||||
|
}
|
||||||
|
dst, err := operandFromAST("MOVQ", s.Operands[1], 8, fi, link)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
reg, ok := dst.(Reg)
|
||||||
|
if !ok || reg.isVec() {
|
||||||
|
return nil, nil, nil, fmt.Errorf("TLS: destination must be a general register")
|
||||||
|
}
|
||||||
|
if link == nil || link.tlsOneInsn() {
|
||||||
|
return nil, nil, nil, nil // noped out
|
||||||
|
}
|
||||||
|
seg := byte(0x64) // FS
|
||||||
|
if link.goos == "windows" {
|
||||||
|
seg = 0x65 // GS
|
||||||
|
}
|
||||||
|
e := &enc{}
|
||||||
|
i := &instr{
|
||||||
|
prefix: seg,
|
||||||
|
rexW: size == 8,
|
||||||
|
rexR: reg.idx >= 8,
|
||||||
|
opcode: []byte{0x8B},
|
||||||
|
modrm: 0x04 | (reg.idx&7)<<3,
|
||||||
|
sib: 0x25,
|
||||||
|
disp: le32(0),
|
||||||
|
tls: true,
|
||||||
|
}
|
||||||
|
if err := e.emit(i); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
ps := make([]sbPatch, len(e.patches))
|
||||||
|
for i, p := range e.patches {
|
||||||
|
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend, kind: RelTLSLE}
|
||||||
|
}
|
||||||
|
return e.out, ps, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// encodeBookkeeping accepts-and-ignores FUNCDATA and PCDATA at the statement
|
// encodeBookkeeping accepts-and-ignores FUNCDATA and PCDATA at the statement
|
||||||
// level, before operand conversion: the toolchain's shapes are FUNCDATA
|
// level, before operand conversion: the toolchain's shapes are FUNCDATA
|
||||||
// $n, sym(SB) and PCDATA $n, $m, and neither contributes a byte to the
|
// $n, sym(SB) and PCDATA $n, $m, and neither contributes a byte to the
|
||||||
@@ -1108,6 +1189,19 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
|||||||
}
|
}
|
||||||
m := Mem{Base: base, Disp: a.Offset, HasBase: true, Size: size}
|
m := Mem{Base: base, Disp: a.Offset, HasBase: true, Size: size}
|
||||||
if a.Index != "" {
|
if a.Index != "" {
|
||||||
|
if a.Index == "TLS" {
|
||||||
|
// off(base)(TLS*1): the thread-local annotation. The
|
||||||
|
// one-instruction TLS form folds it to off(TLS), the
|
||||||
|
// segment-prefixed absolute whose disp32 carries an
|
||||||
|
// R_TLS_LE patch site; the base register disappears
|
||||||
|
// from the encoding, exactly as the toolchain's
|
||||||
|
// progedit rewrites the address.
|
||||||
|
seg := byte(0x64) // FS on linux, freebsd, plan9
|
||||||
|
if link != nil && link.goos == "windows" {
|
||||||
|
seg = 0x65 // GS
|
||||||
|
}
|
||||||
|
return TLSMem{Disp: a.Offset, Size: size, Seg: seg}, nil
|
||||||
|
}
|
||||||
idx, ok := ParseReg(a.Index)
|
idx, ok := ParseReg(a.Index)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ type encPatch struct {
|
|||||||
off int
|
off int
|
||||||
name string
|
name string
|
||||||
addend int64
|
addend int64
|
||||||
|
tls bool // a TLS slot offset: the patch is R_TLSLE with no symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *enc) encode(mnem string, ops []Operand) error {
|
func (e *enc) encode(mnem string, ops []Operand) error {
|
||||||
@@ -578,6 +579,7 @@ type instr struct {
|
|||||||
disp []byte
|
disp []byte
|
||||||
imm []byte
|
imm []byte
|
||||||
sb *sbRef // static-symbol displacement in disp, awaiting resolution
|
sb *sbRef // static-symbol displacement in disp, awaiting resolution
|
||||||
|
tls bool // the displacement is a TLS slot offset, patched R_TLSLE
|
||||||
}
|
}
|
||||||
|
|
||||||
// sbRef records that an instruction's displacement refers to a static symbol
|
// sbRef records that an instruction's displacement refers to a static symbol
|
||||||
@@ -620,6 +622,9 @@ func (e *enc) emit(i *instr) error {
|
|||||||
if i.sb != nil {
|
if i.sb != nil {
|
||||||
e.patches = append(e.patches, encPatch{off: len(e.out), name: i.sb.name, addend: i.sb.addend})
|
e.patches = append(e.patches, encPatch{off: len(e.out), name: i.sb.name, addend: i.sb.addend})
|
||||||
}
|
}
|
||||||
|
if i.tls {
|
||||||
|
e.patches = append(e.patches, encPatch{off: len(e.out), tls: true})
|
||||||
|
}
|
||||||
e.out = append(e.out, i.disp...)
|
e.out = append(e.out, i.disp...)
|
||||||
e.out = append(e.out, i.imm...)
|
e.out = append(e.out, i.imm...)
|
||||||
return nil
|
return nil
|
||||||
@@ -674,6 +679,16 @@ func setRMReg(i *instr, regField int, rexR, regForced bool, rm Operand, opSize i
|
|||||||
i.disp = le32(0)
|
i.disp = le32(0)
|
||||||
i.sb = &sbRef{name: r.name, addend: r.addend}
|
i.sb = &sbRef{name: r.name, addend: r.addend}
|
||||||
return nil
|
return nil
|
||||||
|
case TLSMem:
|
||||||
|
// off(TLS): the segment-prefixed absolute access, mod=00 with the
|
||||||
|
// SIB escape's disp32 absolute form. The displacement is the TLS
|
||||||
|
// slot offset, patched by the linker's TLS relocation.
|
||||||
|
i.prefix = r.Seg
|
||||||
|
i.modrm = 0x04 | regField<<3
|
||||||
|
i.sib = 0x25
|
||||||
|
i.disp = le32(r.Disp)
|
||||||
|
i.tls = true
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid r/m operand %T", rm)
|
return fmt.Errorf("invalid r/m operand %T", rm)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -307,12 +307,12 @@ func TestStackGuardBytesLOONG64(t *testing.T) {
|
|||||||
func TestStackGuardGOObjInternalCall(t *testing.T) {
|
func TestStackGuardGOObjInternalCall(t *testing.T) {
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
src string
|
src string
|
||||||
assemble func(*ast.File) (*Image, error)
|
assemble func(*ast.File, ...AssembleOption) (*Image, error)
|
||||||
}{
|
}{
|
||||||
{"g_amd64.s", AssembleFile},
|
{"g_amd64.s", AssembleFile},
|
||||||
{"g_arm64.s", AssembleFileARM64},
|
{"g_arm64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileARM64(f) }},
|
||||||
{"g_riscv64.s", AssembleFileRISCV},
|
{"g_riscv64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileRISCV(f) }},
|
||||||
{"g_loong64.s", AssembleFileLOONG64},
|
{"g_loong64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileLOONG64(f) }},
|
||||||
} {
|
} {
|
||||||
f, errs := parser.Parse(tt.src, "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
f, errs := parser.Parse(tt.src, "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
|||||||
@@ -192,6 +192,19 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
|||||||
}
|
}
|
||||||
return e.emit(i)
|
return e.emit(i)
|
||||||
|
|
||||||
|
case TLSMem:
|
||||||
|
if !dstIsReg {
|
||||||
|
return fmt.Errorf("MOV: two memory operands")
|
||||||
|
}
|
||||||
|
// MOV r, off(TLS): the segment-prefixed absolute load, reg=dst,
|
||||||
|
// rm=src(tlsMem) through the SIB escape; the disp32 is the TLS slot
|
||||||
|
// offset with its R_TLSLE patch site.
|
||||||
|
i := newInstr(size, []byte{movRR(size)})
|
||||||
|
if err := setRM(i, dstReg, src, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.emit(i)
|
||||||
|
|
||||||
case Imm:
|
case Imm:
|
||||||
if dstIsReg {
|
if dstIsReg {
|
||||||
v := int64(src)
|
v := int64(src)
|
||||||
|
|||||||
+16
-1
@@ -150,6 +150,18 @@ func (img *Image) Bytes() []byte {
|
|||||||
return append(out, img.Data...)
|
return append(out, img.Data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssembleOption adjusts the file-level assembly context.
|
||||||
|
type AssembleOption func(*linkInfo)
|
||||||
|
|
||||||
|
// WithGOOS selects the target operating system for the forms that depend on
|
||||||
|
// it, the TLS access shape above all: linux and freebsd take the
|
||||||
|
// one-instruction form, windows and plan9 keep the two-instruction load.
|
||||||
|
func WithGOOS(goos string) AssembleOption {
|
||||||
|
return func(l *linkInfo) {
|
||||||
|
l.goos = goos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssembleFile assembles every TEXT function of a parsed file and lays out
|
// AssembleFile assembles every TEXT function of a parsed file and lays out
|
||||||
// its static symbols (GLOBL/DATA) in a data section behind the code. Each
|
// its static symbols (GLOBL/DATA) in a data section behind the code. Each
|
||||||
// reference to a file-local static symbol becomes a RIP-relative load whose
|
// reference to a file-local static symbol becomes a RIP-relative load whose
|
||||||
@@ -157,7 +169,7 @@ func (img *Image) Bytes() []byte {
|
|||||||
// GLOBL defines is recorded as an external relocation (Externals) with its
|
// GLOBL defines is recorded as an external relocation (Externals) with its
|
||||||
// displacement left zero, the object-file emitters resolve it at link
|
// displacement left zero, the object-file emitters resolve it at link
|
||||||
// time, while the raw image (Bytes) cannot represent it.
|
// time, while the raw image (Bytes) cannot represent it.
|
||||||
func AssembleFile(f *ast.File) (*Image, error) {
|
func AssembleFile(f *ast.File, opts ...AssembleOption) (*Image, error) {
|
||||||
dataSyms, err := collectData(f)
|
dataSyms, err := collectData(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -174,6 +186,9 @@ func AssembleFile(f *ast.File) (*Image, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
link := &linkInfo{symbols: known, allowExternal: true}
|
link := &linkInfo{symbols: known, allowExternal: true}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(link)
|
||||||
|
}
|
||||||
poolSeen := map[string]bool{}
|
poolSeen := map[string]bool{}
|
||||||
|
|
||||||
img := &Image{Symbols: map[string]int{}, SourcePath: f.Path}
|
img := &Image{Symbols: map[string]int{}, SourcePath: f.Path}
|
||||||
|
|||||||
@@ -36,6 +36,18 @@ type FloatImm struct {
|
|||||||
|
|
||||||
func (FloatImm) isOperand() {}
|
func (FloatImm) isOperand() {}
|
||||||
|
|
||||||
|
// TLSMem is a thread-local access, the source form off(base)(TLS*1) with the
|
||||||
|
// base dropped: the toolchain's one-instruction TLS rewrite assembles it as
|
||||||
|
// the segment-prefixed absolute whose disp32 carries an R_TLS_LE patch site
|
||||||
|
// (the linker fills the TLS slot offset).
|
||||||
|
type TLSMem struct {
|
||||||
|
Disp int64
|
||||||
|
Size int
|
||||||
|
Seg byte // the segment override: FS (0x64) or GS (0x65) on windows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TLSMem) isOperand() {}
|
||||||
|
|
||||||
// Mem is a memory operand of the form disp(base)(index*scale).
|
// Mem is a memory operand of the form disp(base)(index*scale).
|
||||||
type Mem struct {
|
type Mem struct {
|
||||||
Base Reg
|
Base Reg
|
||||||
|
|||||||
+2
-2
@@ -671,7 +671,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
t.fail(path, errs[0])
|
t.fail(path, errs[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := assembleFile(tg.a, f); err != nil {
|
if _, err := assembleFile(tg.a, f, goos); err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, err)
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
@@ -701,7 +701,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
err = errs[0] // a parse failure is a failure for every target
|
err = errs[0] // a parse failure is a failure for every target
|
||||||
} else {
|
} else {
|
||||||
_, err = assembleFile(tg.a, f)
|
_, err = assembleFile(tg.a, f, goos)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ func disSource(path string, target arch.Arch) int {
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
img, err := assembleFile(target, f)
|
img, err := assembleFile(target, f, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "gasm dis: %v\n", err)
|
fmt.Fprintf(os.Stderr, "gasm dis: %v\n", err)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
+5
-5
@@ -582,7 +582,7 @@ naming the package.
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := assembleFile(targetArch, f)
|
img, err := assembleFile(targetArch, f, goos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
||||||
return 1
|
return 1
|
||||||
@@ -813,10 +813,10 @@ func platformPredefinesFor(goarch string, fileGoos string) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assembleFile assembles a parsed file for the given architecture and returns the image.
|
// assembleFile assembles a parsed file for the given architecture and returns the image.
|
||||||
func assembleFile(targetArch arch.Arch, f *ast.File) (*asm.Image, error) {
|
func assembleFile(targetArch arch.Arch, f *ast.File, goos string) (*asm.Image, error) {
|
||||||
switch targetArch {
|
switch targetArch {
|
||||||
case arch.AMD64:
|
case arch.AMD64:
|
||||||
return asm.AssembleFile(f)
|
return asm.AssembleFile(f, asm.WithGOOS(goos))
|
||||||
case arch.RISCV:
|
case arch.RISCV:
|
||||||
return asm.AssembleFileRISCV(f)
|
return asm.AssembleFileRISCV(f)
|
||||||
case arch.ARM64:
|
case arch.ARM64:
|
||||||
@@ -847,7 +847,7 @@ func assemblePath(path string, forced arch.Arch, dirs includeDirs) (*asm.Image,
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return nil, fmt.Errorf("parse errors")
|
return nil, fmt.Errorf("parse errors")
|
||||||
}
|
}
|
||||||
return assembleFile(target, f)
|
return assembleFile(target, f, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// printByteDiff shows the first few byte differences between two code blocks.
|
// printByteDiff shows the first few byte differences between two code blocks.
|
||||||
@@ -943,7 +943,7 @@ func cmdVerifyNonJIT(path string, targetArch arch.Arch, groundTruth, profile boo
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
img, err := assembleFile(targetArch, f)
|
img, err := assembleFile(targetArch, f, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
Vendored
+7
@@ -34,3 +34,10 @@ TEXT ·Frame(SB), NOSPLIT, $0
|
|||||||
ADJSP $-(64 - 8)
|
ADJSP $-(64 - 8)
|
||||||
POPFQ
|
POPFQ
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
// func Tls() int64
|
||||||
|
TEXT ·Tls(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ TLS, BX
|
||||||
|
MOVQ 0(BX)(TLS*1), AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|||||||
Reference in New Issue
Block a user