]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm: allow def/ref of func ABI when compiling runtime
authorThan McIntosh <thanm@google.com>
Wed, 7 Oct 2020 16:31:05 +0000 (12:31 -0400)
committerThan McIntosh <thanm@google.com>
Mon, 19 Oct 2020 11:25:35 +0000 (11:25 +0000)
Function symbols defined and referenced by assembly source currently
always default to ABI0; this patch adds preliminary support for
accepting an explicit ABI selector clause for func defs/refs. This
functionality is currently only enabled when compiling runtime-related
packages (runtime, syscall, reflect). Examples:

  TEXT ·DefinedAbi0Symbol<ABI0>(SB),NOSPLIT,$0
        RET

  TEXT ·DefinedAbi1Symbol<ABIInternal>(SB),NOSPLIT,$0
        CALL    ·AbiZerolSym<ABI0>(SB)
...
        JMP     ·AbiInternalSym<ABIInternal>(SB)
        RET

Also included is a small change to the code in the compiler that reads
the symabis file emitted by the assembler.

New behavior is currently gated under GOEXPERIMENT=regabi.

Updates #27539, #40724.

Change-Id: Ia22221fe26df0fa002191cfb13bdfaaa38d7df38
Reviewed-on: https://go-review.googlesource.com/c/go/+/260477
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Than McIntosh <thanm@google.com>

src/cmd/asm/internal/asm/endtoend_test.go
src/cmd/asm/internal/asm/expr_test.go
src/cmd/asm/internal/asm/line_test.go
src/cmd/asm/internal/asm/operand_test.go
src/cmd/asm/internal/asm/parse.go
src/cmd/asm/internal/asm/pseudo_test.go
src/cmd/asm/main.go
src/cmd/compile/internal/gc/main.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/util.go
src/cmd/internal/objabi/path.go

index decf5391db5d7a032b6b17c88d0d2c89911cbde1..989b7a5405419895e075aba258975a2ba9b8601c 100644 (file)
@@ -31,7 +31,7 @@ func testEndToEnd(t *testing.T, goarch, file string) {
        architecture, ctxt := setArch(goarch)
        architecture.Init(ctxt)
        lexer := lex.NewLexer(input)
-       parser := NewParser(ctxt, architecture, lexer)
+       parser := NewParser(ctxt, architecture, lexer, false)
        pList := new(obj.Plist)
        var ok bool
        testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
@@ -273,7 +273,7 @@ func testErrors(t *testing.T, goarch, file string) {
        input := filepath.Join("testdata", file+".s")
        architecture, ctxt := setArch(goarch)
        lexer := lex.NewLexer(input)
-       parser := NewParser(ctxt, architecture, lexer)
+       parser := NewParser(ctxt, architecture, lexer, false)
        pList := new(obj.Plist)
        var ok bool
        testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
index 1251594349fc01da5e074395e811a5e8da85c74d..e9c92df1f3aa1e9e35838e9f480e00c1345418d1 100644 (file)
@@ -57,7 +57,7 @@ var exprTests = []exprTest{
 }
 
 func TestExpr(t *testing.T) {
-       p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser.
+       p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser.
        for i, test := range exprTests {
                p.start(lex.Tokenize(test.input))
                result := int64(p.expr())
@@ -113,7 +113,7 @@ func TestBadExpr(t *testing.T) {
 }
 
 func runBadTest(i int, test badExprTest, t *testing.T) (err error) {
-       p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser.
+       p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser.
        p.start(lex.Tokenize(test.input))
        return tryParse(t, func() {
                p.expr()
index 01b058bd956dca47b27dfce33582dbb31eb246b7..da857ced3a79bfda5ee949908dc505a34f25cba8 100644 (file)
@@ -39,7 +39,7 @@ func testBadInstParser(t *testing.T, goarch string, tests []badInstTest) {
        for i, test := range tests {
                arch, ctxt := setArch(goarch)
                tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil)
-               parser := NewParser(ctxt, arch, tokenizer)
+               parser := NewParser(ctxt, arch, tokenizer, false)
 
                err := tryParse(t, func() {
                        parser.Parse()
index f187d0b16603c0d9ce861ef0586f127923f83f70..2e83e176b297cb5840944f7c3a6787370644ac97 100644 (file)
@@ -28,7 +28,7 @@ func setArch(goarch string) (*arch.Arch, *obj.Link) {
 
 func newParser(goarch string) *Parser {
        architecture, ctxt := setArch(goarch)
-       return NewParser(ctxt, architecture, nil)
+       return NewParser(ctxt, architecture, nil, false)
 }
 
 // tryParse executes parse func in panicOnError=true context.
@@ -75,7 +75,12 @@ func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) {
                parser.start(lex.Tokenize(test.input))
                addr := obj.Addr{}
                parser.operand(&addr)
-               result := obj.Dconv(&emptyProg, &addr)
+               var result string
+               if parser.compilingRuntime {
+                       result = obj.DconvWithABIDetail(&emptyProg, &addr)
+               } else {
+                       result = obj.Dconv(&emptyProg, &addr)
+               }
                if result != test.output {
                        t.Errorf("fail at %s: got %s; expected %s\n", test.input, result, test.output)
                }
@@ -86,6 +91,9 @@ func TestAMD64OperandParser(t *testing.T) {
        parser := newParser("amd64")
        testOperandParser(t, parser, amd64OperandTests)
        testBadOperandParser(t, parser, amd64BadOperandTests)
+       parser.compilingRuntime = true
+       testOperandParser(t, parser, amd64RuntimeOperandTests)
+       testBadOperandParser(t, parser, amd64BadOperandRuntimeTests)
 }
 
 func Test386OperandParser(t *testing.T) {
@@ -141,7 +149,7 @@ func TestFuncAddress(t *testing.T) {
                        parser := newParser(sub.arch)
                        for _, test := range sub.tests {
                                parser.start(lex.Tokenize(test.input))
-                               name, ok := parser.funcAddress()
+                               name, _, ok := parser.funcAddress()
 
                                isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
                                        // Ignore static symbols.
@@ -298,6 +306,11 @@ var amd64OperandTests = []operandTest{
        {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
 }
 
+var amd64RuntimeOperandTests = []operandTest{
+       {"$bar<ABI0>(SB)", "$bar<ABI0>(SB)"},
+       {"$foo<ABIInternal>(SB)", "$foo<ABIInternal>(SB)"},
+}
+
 var amd64BadOperandTests = []badOperandTest{
        {"[", "register list: expected ']', found EOF"},
        {"[4", "register list: bad low register in `[4`"},
@@ -311,6 +324,11 @@ var amd64BadOperandTests = []badOperandTest{
        {"[X0-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"},
        {"[X0,X3]", "register list: expected '-' after `[X0`, found ','"},
        {"[X0,X1,X2,X3]", "register list: expected '-' after `[X0`, found ','"},
+       {"$foo<ABI0>", "ABI selector only permitted when compiling runtime, reference was to \"foo\""},
+}
+
+var amd64BadOperandRuntimeTests = []badOperandTest{
+       {"$foo<bletch>", "malformed ABI selector \"bletch\" in reference to \"foo\""},
 }
 
 var x86OperandTests = []operandTest{
index d9dbd92cb020339f28896af1284c11cd8891914d..154cf9c7a7854bf2cfe56ed1ef041286a1d04fc3 100644 (file)
@@ -25,25 +25,26 @@ import (
 )
 
 type Parser struct {
-       lex           lex.TokenReader
-       lineNum       int   // Line number in source file.
-       errorLine     int   // Line number of last error.
-       errorCount    int   // Number of errors.
-       sawCode       bool  // saw code in this file (as opposed to comments and blank lines)
-       pc            int64 // virtual PC; count of Progs; doesn't advance for GLOBL or DATA.
-       input         []lex.Token
-       inputPos      int
-       pendingLabels []string // Labels to attach to next instruction.
-       labels        map[string]*obj.Prog
-       toPatch       []Patch
-       addr          []obj.Addr
-       arch          *arch.Arch
-       ctxt          *obj.Link
-       firstProg     *obj.Prog
-       lastProg      *obj.Prog
-       dataAddr      map[string]int64 // Most recent address for DATA for this symbol.
-       isJump        bool             // Instruction being assembled is a jump.
-       errorWriter   io.Writer
+       lex              lex.TokenReader
+       lineNum          int   // Line number in source file.
+       errorLine        int   // Line number of last error.
+       errorCount       int   // Number of errors.
+       sawCode          bool  // saw code in this file (as opposed to comments and blank lines)
+       pc               int64 // virtual PC; count of Progs; doesn't advance for GLOBL or DATA.
+       input            []lex.Token
+       inputPos         int
+       pendingLabels    []string // Labels to attach to next instruction.
+       labels           map[string]*obj.Prog
+       toPatch          []Patch
+       addr             []obj.Addr
+       arch             *arch.Arch
+       ctxt             *obj.Link
+       firstProg        *obj.Prog
+       lastProg         *obj.Prog
+       dataAddr         map[string]int64 // Most recent address for DATA for this symbol.
+       isJump           bool             // Instruction being assembled is a jump.
+       compilingRuntime bool
+       errorWriter      io.Writer
 }
 
 type Patch struct {
@@ -51,14 +52,15 @@ type Patch struct {
        label string
 }
 
-func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader) *Parser {
+func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader, compilingRuntime bool) *Parser {
        return &Parser{
-               ctxt:        ctxt,
-               arch:        ar,
-               lex:         lexer,
-               labels:      make(map[string]*obj.Prog),
-               dataAddr:    make(map[string]int64),
-               errorWriter: os.Stderr,
+               ctxt:             ctxt,
+               arch:             ar,
+               lex:              lexer,
+               labels:           make(map[string]*obj.Prog),
+               dataAddr:         make(map[string]int64),
+               errorWriter:      os.Stderr,
+               compilingRuntime: compilingRuntime,
        }
 }
 
@@ -310,8 +312,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
                // Defines text symbol in operands[0].
                if len(operands) > 0 {
                        p.start(operands[0])
-                       if name, ok := p.funcAddress(); ok {
-                               fmt.Fprintf(w, "def %s ABI0\n", name)
+                       if name, abi, ok := p.funcAddress(); ok {
+                               fmt.Fprintf(w, "def %s %s\n", name, abi)
                        }
                }
                return
@@ -329,8 +331,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
        // Search for symbol references.
        for _, op := range operands {
                p.start(op)
-               if name, ok := p.funcAddress(); ok {
-                       fmt.Fprintf(w, "ref %s ABI0\n", name)
+               if name, abi, ok := p.funcAddress(); ok {
+                       fmt.Fprintf(w, "ref %s %s\n", name, abi)
                }
        }
 }
@@ -765,20 +767,19 @@ func (p *Parser) symbolReference(a *obj.Addr, name string, prefix rune) {
        case '*':
                a.Type = obj.TYPE_INDIR
        }
-       // Weirdness with statics: Might now have "<>".
-       isStatic := false
-       if p.peek() == '<' {
-               isStatic = true
-               p.next()
-               p.get('>')
-       }
+
+       // Parse optional <> (indicates a static symbol) or
+       // <ABIxxx> (selecting text symbol with specific ABI).
+       doIssueError := true
+       isStatic, abi := p.symRefAttrs(name, doIssueError)
+
        if p.peek() == '+' || p.peek() == '-' {
                a.Offset = int64(p.expr())
        }
        if isStatic {
                a.Sym = p.ctxt.LookupStatic(name)
        } else {
-               a.Sym = p.ctxt.Lookup(name)
+               a.Sym = p.ctxt.LookupABI(name, abi)
        }
        if p.peek() == scanner.EOF {
                if prefix == 0 && p.isJump {
@@ -823,12 +824,60 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr
        }
 }
 
+// symRefAttrs parses an optional function symbol attribute clause for
+// the function symbol 'name', logging an error for a malformed
+// attribute clause if 'issueError' is true. The return value is a
+// (boolean, ABI) pair indicating that the named symbol is either
+// static or a particular ABI specification.
+//
+// The expected form of the attribute clause is:
+//
+// empty,           yielding (false, obj.ABI0)
+// "<>",            yielding (true,  obj.ABI0)
+// "<ABI0>"         yielding (false, obj.ABI0)
+// "<ABIInternal>"  yielding (false, obj.ABIInternal)
+//
+// Anything else beginning with "<" logs an error if issueError is
+// true, otherwise returns (false, obj.ABI0).
+//
+func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) {
+       abi := obj.ABI0
+       isStatic := false
+       if p.peek() != '<' {
+               return isStatic, abi
+       }
+       p.next()
+       tok := p.peek()
+       if tok == '>' {
+               isStatic = true
+       } else if tok == scanner.Ident {
+               abistr := p.get(scanner.Ident).String()
+               if !p.compilingRuntime {
+                       if issueError {
+                               p.errorf("ABI selector only permitted when compiling runtime, reference was to %q", name)
+                       }
+               } else {
+                       theabi, valid := obj.ParseABI(abistr)
+                       if !valid {
+                               if issueError {
+                                       p.errorf("malformed ABI selector %q in reference to %q",
+                                               abistr, name)
+                               }
+                       } else {
+                               abi = theabi
+                       }
+               }
+       }
+       p.get('>')
+       return isStatic, abi
+}
+
 // funcAddress parses an external function address. This is a
 // constrained form of the operand syntax that's always SB-based,
 // non-static, and has at most a simple integer offset:
 //
-//    [$|*]sym[+Int](SB)
-func (p *Parser) funcAddress() (string, bool) {
+//    [$|*]sym[<abi>][+Int](SB)
+func (p *Parser) funcAddress() (string, obj.ABI, bool) {
        switch p.peek() {
        case '$', '*':
                // Skip prefix.
@@ -838,25 +887,32 @@ func (p *Parser) funcAddress() (string, bool) {
        tok := p.next()
        name := tok.String()
        if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) {
-               return "", false
+               return "", obj.ABI0, false
+       }
+       // Parse optional <> (indicates a static symbol) or
+       // <ABIxxx> (selecting text symbol with specific ABI).
+       noErrMsg := false
+       isStatic, abi := p.symRefAttrs(name, noErrMsg)
+       if isStatic {
+               return "", obj.ABI0, false // This function rejects static symbols.
        }
        tok = p.next()
        if tok.ScanToken == '+' {
                if p.next().ScanToken != scanner.Int {
-                       return "", false
+                       return "", obj.ABI0, false
                }
                tok = p.next()
        }
        if tok.ScanToken != '(' {
-               return "", false
+               return "", obj.ABI0, false
        }
        if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" {
-               return "", false
+               return "", obj.ABI0, false
        }
        if p.next().ScanToken != ')' || p.peek() != scanner.EOF {
-               return "", false
+               return "", obj.ABI0, false
        }
-       return name, true
+       return name, abi, true
 }
 
 // registerIndirect parses the general form of a register indirection.
index 100bef91cfd79cd9f5105214a8f4c6f3127d47e2..622ee25ce7159bca3951e7f8da7f617e1169ec2c 100644 (file)
@@ -37,6 +37,7 @@ func TestErroneous(t *testing.T) {
                {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
                {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"},          // Issue 12468.
                {"TEXT", "@B(SB),0,$0", "expected '(', found B"},             // Issue 23580.
+               {"TEXT", "foo<ABIInternal>(SB),0", "ABI selector only permitted when compiling runtime, reference was to \"foo\""},
                {"FUNCDATA", "", "expect two operands for FUNCDATA"},
                {"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
                {"DATA", "", "expect two operands for DATA"},
index fd079a2ccd32ae79dbef9165ae00645616cce919..01c963ac729f89737e938b5b921a4f0bc9e947c1 100644 (file)
@@ -52,6 +52,7 @@ func main() {
        case "all", "ret":
                ctxt.Retpoline = true
        }
+       compilingRuntime := objabi.IsRuntimePackagePath(*flags.Importpath)
 
        ctxt.Bso = bufio.NewWriter(os.Stdout)
        defer ctxt.Bso.Flush()
@@ -74,7 +75,7 @@ func main() {
        var failedFile string
        for _, f := range flag.Args() {
                lexer := lex.NewLexer(f)
-               parser := asm.NewParser(ctxt, architecture, lexer)
+               parser := asm.NewParser(ctxt, architecture, lexer, compilingRuntime)
                ctxt.DiagFunc = func(format string, args ...interface{}) {
                        diag = true
                        log.Printf(format, args...)
index e4e4ce72fdb1693b08313e21fc68d9b4e88ec7b7..2fffe625cdc3eeb74437b09ca2c2d0e15b016e1d 100644 (file)
@@ -968,9 +968,10 @@ func readSymABIs(file, myimportpath string) {
                        if len(parts) != 3 {
                                log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
                        }
-                       sym, abi := parts[1], parts[2]
-                       if abi != "ABI0" { // Only supported external ABI right now
-                               log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi)
+                       sym, abistr := parts[1], parts[2]
+                       abi, valid := obj.ParseABI(abistr)
+                       if !valid {
+                               log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
                        }
 
                        // If the symbol is already prefixed with
@@ -983,9 +984,9 @@ func readSymABIs(file, myimportpath string) {
 
                        // Record for later.
                        if parts[0] == "def" {
-                               symabiDefs[sym] = obj.ABI0
+                               symabiDefs[sym] = abi
                        } else {
-                               symabiRefs[sym] = obj.ABI0
+                               symabiRefs[sym] = abi
                        }
                default:
                        log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
index ae85dbbe4e2b140d93a23c90d9022a4c64719bc3..ad4708138f95d225248b2d414ae3b2b0dc514af0 100644 (file)
@@ -502,6 +502,20 @@ const (
        ABICount
 )
 
+// ParseABI converts from a string representation in 'abistr' to the
+// corresponding ABI value. Second return value is TRUE if the
+// abi string is recognized, FALSE otherwise.
+func ParseABI(abistr string) (ABI, bool) {
+       switch abistr {
+       default:
+               return ABI0, false
+       case "ABI0":
+               return ABI0, true
+       case "ABIInternal":
+               return ABIInternal, true
+       }
+}
+
 // Attribute is a set of symbol attributes.
 type Attribute uint32
 
index a30ccf0564523b09afc4e9be3dcda12823cc3871..21e28807a6d68691f94012095425a87de6c0c643 100644 (file)
@@ -210,13 +210,30 @@ func (ctxt *Link) CanReuseProgs() bool {
        return ctxt.Debugasm == 0
 }
 
+// Dconv accepts an argument 'a' within a prog 'p' and returns a string
+// with a formatted version of the argument.
 func Dconv(p *Prog, a *Addr) string {
        buf := new(bytes.Buffer)
-       WriteDconv(buf, p, a)
+       writeDconv(buf, p, a, false)
        return buf.String()
 }
 
+// DconvDconvWithABIDetail accepts an argument 'a' within a prog 'p'
+// and returns a string with a formatted version of the argument, in
+// which text symbols are rendered with explicit ABI selectors.
+func DconvWithABIDetail(p *Prog, a *Addr) string {
+       buf := new(bytes.Buffer)
+       writeDconv(buf, p, a, true)
+       return buf.String()
+}
+
+// WriteDconv accepts an argument 'a' within a prog 'p'
+// and writes a formatted version of the arg to the writer.
 func WriteDconv(w io.Writer, p *Prog, a *Addr) {
+       writeDconv(w, p, a, false)
+}
+
+func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
        switch a.Type {
        default:
                fmt.Fprintf(w, "type=%d", a.Type)
@@ -250,7 +267,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
 
        case TYPE_BRANCH:
                if a.Sym != nil {
-                       fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
+                       fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail))
                } else if a.Target() != nil {
                        fmt.Fprint(w, a.Target().Pc)
                } else {
@@ -259,7 +276,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
 
        case TYPE_INDIR:
                io.WriteString(w, "*")
-               a.WriteNameTo(w)
+               a.writeNameTo(w, abiDetail)
 
        case TYPE_MEM:
                a.WriteNameTo(w)
@@ -299,7 +316,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
 
        case TYPE_ADDR:
                io.WriteString(w, "$")
-               a.WriteNameTo(w)
+               a.writeNameTo(w, abiDetail)
 
        case TYPE_SHIFT:
                v := int(a.Offset)
@@ -335,6 +352,11 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
 }
 
 func (a *Addr) WriteNameTo(w io.Writer) {
+       a.writeNameTo(w, false)
+}
+
+func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
+
        switch a.Name {
        default:
                fmt.Fprintf(w, "name=%d", a.Name)
@@ -356,7 +378,7 @@ func (a *Addr) WriteNameTo(w io.Writer) {
                        reg = Rconv(int(a.Reg))
                }
                if a.Sym != nil {
-                       fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
+                       fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg)
                } else {
                        fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
                }
@@ -596,3 +618,10 @@ func Bool2int(b bool) int {
        }
        return i
 }
+
+func abiDecorate(a *Addr, abiDetail bool) string {
+       if !abiDetail || a.Sym == nil {
+               return ""
+       }
+       return fmt.Sprintf("<%s>", a.Sym.ABI())
+}
index 2a42179a3686b20e0b757d6cd52e02967d9dff69..fd1c9981c69d0f9b1220d7e37de1e08068d4584a 100644 (file)
@@ -39,3 +39,25 @@ func PathToPrefix(s string) string {
 
        return string(p)
 }
+
+// IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it
+// belongs to the collection of "runtime-related" packages, including
+// "runtime" itself, "reflect", "syscall", and the
+// "runtime/internal/*" packages. The compiler and/or assembler in
+// some cases need to be aware of when they are building such a
+// package, for example to enable features such as ABI selectors in
+// assembly sources.
+func IsRuntimePackagePath(pkgpath string) bool {
+       rval := false
+       switch pkgpath {
+       case "runtime":
+               rval = true
+       case "reflect":
+               rval = true
+       case "syscall":
+               rval = true
+       default:
+               rval = strings.HasPrefix(pkgpath, "runtime/internal")
+       }
+       return rval
+}