cmd/asm: allow def/ref of func ABI when compiling runtime

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>
This commit is contained in:
Than McIntosh 2020-10-07 12:31:05 -04:00
parent 9499a2e108
commit dd58239dd2
11 changed files with 207 additions and 65 deletions

View file

@ -31,7 +31,7 @@ func testEndToEnd(t *testing.T, goarch, file string) {
architecture, ctxt := setArch(goarch) architecture, ctxt := setArch(goarch)
architecture.Init(ctxt) architecture.Init(ctxt)
lexer := lex.NewLexer(input) lexer := lex.NewLexer(input)
parser := NewParser(ctxt, architecture, lexer) parser := NewParser(ctxt, architecture, lexer, false)
pList := new(obj.Plist) pList := new(obj.Plist)
var ok bool var ok bool
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. 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") input := filepath.Join("testdata", file+".s")
architecture, ctxt := setArch(goarch) architecture, ctxt := setArch(goarch)
lexer := lex.NewLexer(input) lexer := lex.NewLexer(input)
parser := NewParser(ctxt, architecture, lexer) parser := NewParser(ctxt, architecture, lexer, false)
pList := new(obj.Plist) pList := new(obj.Plist)
var ok bool var ok bool
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.

View file

@ -57,7 +57,7 @@ var exprTests = []exprTest{
} }
func TestExpr(t *testing.T) { 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 { for i, test := range exprTests {
p.start(lex.Tokenize(test.input)) p.start(lex.Tokenize(test.input))
result := int64(p.expr()) result := int64(p.expr())
@ -113,7 +113,7 @@ func TestBadExpr(t *testing.T) {
} }
func runBadTest(i int, test badExprTest, t *testing.T) (err error) { 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)) p.start(lex.Tokenize(test.input))
return tryParse(t, func() { return tryParse(t, func() {
p.expr() p.expr()

View file

@ -39,7 +39,7 @@ func testBadInstParser(t *testing.T, goarch string, tests []badInstTest) {
for i, test := range tests { for i, test := range tests {
arch, ctxt := setArch(goarch) arch, ctxt := setArch(goarch)
tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil) tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil)
parser := NewParser(ctxt, arch, tokenizer) parser := NewParser(ctxt, arch, tokenizer, false)
err := tryParse(t, func() { err := tryParse(t, func() {
parser.Parse() parser.Parse()

View file

@ -28,7 +28,7 @@ func setArch(goarch string) (*arch.Arch, *obj.Link) {
func newParser(goarch string) *Parser { func newParser(goarch string) *Parser {
architecture, ctxt := setArch(goarch) architecture, ctxt := setArch(goarch)
return NewParser(ctxt, architecture, nil) return NewParser(ctxt, architecture, nil, false)
} }
// tryParse executes parse func in panicOnError=true context. // 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)) parser.start(lex.Tokenize(test.input))
addr := obj.Addr{} addr := obj.Addr{}
parser.operand(&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 { if result != test.output {
t.Errorf("fail at %s: got %s; expected %s\n", test.input, 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") parser := newParser("amd64")
testOperandParser(t, parser, amd64OperandTests) testOperandParser(t, parser, amd64OperandTests)
testBadOperandParser(t, parser, amd64BadOperandTests) testBadOperandParser(t, parser, amd64BadOperandTests)
parser.compilingRuntime = true
testOperandParser(t, parser, amd64RuntimeOperandTests)
testBadOperandParser(t, parser, amd64BadOperandRuntimeTests)
} }
func Test386OperandParser(t *testing.T) { func Test386OperandParser(t *testing.T) {
@ -141,7 +149,7 @@ func TestFuncAddress(t *testing.T) {
parser := newParser(sub.arch) parser := newParser(sub.arch)
for _, test := range sub.tests { for _, test := range sub.tests {
parser.start(lex.Tokenize(test.input)) parser.start(lex.Tokenize(test.input))
name, ok := parser.funcAddress() name, _, ok := parser.funcAddress()
isFuncSym := strings.HasSuffix(test.input, "(SB)") && isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
// Ignore static symbols. // 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. {"[):[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{ var amd64BadOperandTests = []badOperandTest{
{"[", "register list: expected ']', found EOF"}, {"[", "register list: expected ']', found EOF"},
{"[4", "register list: bad low register in `[4`"}, {"[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-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"},
{"[X0,X3]", "register list: expected '-' after `[X0`, found ','"}, {"[X0,X3]", "register list: expected '-' after `[X0`, found ','"},
{"[X0,X1,X2,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{ var x86OperandTests = []operandTest{

View file

@ -43,6 +43,7 @@ type Parser struct {
lastProg *obj.Prog lastProg *obj.Prog
dataAddr map[string]int64 // Most recent address for DATA for this symbol. dataAddr map[string]int64 // Most recent address for DATA for this symbol.
isJump bool // Instruction being assembled is a jump. isJump bool // Instruction being assembled is a jump.
compilingRuntime bool
errorWriter io.Writer errorWriter io.Writer
} }
@ -51,7 +52,7 @@ type Patch struct {
label string 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{ return &Parser{
ctxt: ctxt, ctxt: ctxt,
arch: ar, arch: ar,
@ -59,6 +60,7 @@ func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader) *Parser {
labels: make(map[string]*obj.Prog), labels: make(map[string]*obj.Prog),
dataAddr: make(map[string]int64), dataAddr: make(map[string]int64),
errorWriter: os.Stderr, 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]. // Defines text symbol in operands[0].
if len(operands) > 0 { if len(operands) > 0 {
p.start(operands[0]) p.start(operands[0])
if name, ok := p.funcAddress(); ok { if name, abi, ok := p.funcAddress(); ok {
fmt.Fprintf(w, "def %s ABI0\n", name) fmt.Fprintf(w, "def %s %s\n", name, abi)
} }
} }
return return
@ -329,8 +331,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
// Search for symbol references. // Search for symbol references.
for _, op := range operands { for _, op := range operands {
p.start(op) p.start(op)
if name, ok := p.funcAddress(); ok { if name, abi, ok := p.funcAddress(); ok {
fmt.Fprintf(w, "ref %s ABI0\n", name) 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 '*': case '*':
a.Type = obj.TYPE_INDIR a.Type = obj.TYPE_INDIR
} }
// Weirdness with statics: Might now have "<>".
isStatic := false // Parse optional <> (indicates a static symbol) or
if p.peek() == '<' { // <ABIxxx> (selecting text symbol with specific ABI).
isStatic = true doIssueError := true
p.next() isStatic, abi := p.symRefAttrs(name, doIssueError)
p.get('>')
}
if p.peek() == '+' || p.peek() == '-' { if p.peek() == '+' || p.peek() == '-' {
a.Offset = int64(p.expr()) a.Offset = int64(p.expr())
} }
if isStatic { if isStatic {
a.Sym = p.ctxt.LookupStatic(name) a.Sym = p.ctxt.LookupStatic(name)
} else { } else {
a.Sym = p.ctxt.Lookup(name) a.Sym = p.ctxt.LookupABI(name, abi)
} }
if p.peek() == scanner.EOF { if p.peek() == scanner.EOF {
if prefix == 0 && p.isJump { 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 // funcAddress parses an external function address. This is a
// constrained form of the operand syntax that's always SB-based, // constrained form of the operand syntax that's always SB-based,
// non-static, and has at most a simple integer offset: // non-static, and has at most a simple integer offset:
// //
// [$|*]sym[+Int](SB) // [$|*]sym[<abi>][+Int](SB)
func (p *Parser) funcAddress() (string, bool) { func (p *Parser) funcAddress() (string, obj.ABI, bool) {
switch p.peek() { switch p.peek() {
case '$', '*': case '$', '*':
// Skip prefix. // Skip prefix.
@ -838,25 +887,32 @@ func (p *Parser) funcAddress() (string, bool) {
tok := p.next() tok := p.next()
name := tok.String() name := tok.String()
if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) { 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() tok = p.next()
if tok.ScanToken == '+' { if tok.ScanToken == '+' {
if p.next().ScanToken != scanner.Int { if p.next().ScanToken != scanner.Int {
return "", false return "", obj.ABI0, false
} }
tok = p.next() tok = p.next()
} }
if tok.ScanToken != '(' { if tok.ScanToken != '(' {
return "", false return "", obj.ABI0, false
} }
if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" { 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 { 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. // registerIndirect parses the general form of a register indirection.

View file

@ -37,6 +37,7 @@ func TestErroneous(t *testing.T) {
{"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467. {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
{"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468. {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
{"TEXT", "@B(SB),0,$0", "expected '(', found B"}, // Issue 23580. {"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", "", "expect two operands for FUNCDATA"},
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"}, {"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
{"DATA", "", "expect two operands for DATA"}, {"DATA", "", "expect two operands for DATA"},

View file

@ -52,6 +52,7 @@ func main() {
case "all", "ret": case "all", "ret":
ctxt.Retpoline = true ctxt.Retpoline = true
} }
compilingRuntime := objabi.IsRuntimePackagePath(*flags.Importpath)
ctxt.Bso = bufio.NewWriter(os.Stdout) ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush() defer ctxt.Bso.Flush()
@ -74,7 +75,7 @@ func main() {
var failedFile string var failedFile string
for _, f := range flag.Args() { for _, f := range flag.Args() {
lexer := lex.NewLexer(f) lexer := lex.NewLexer(f)
parser := asm.NewParser(ctxt, architecture, lexer) parser := asm.NewParser(ctxt, architecture, lexer, compilingRuntime)
ctxt.DiagFunc = func(format string, args ...interface{}) { ctxt.DiagFunc = func(format string, args ...interface{}) {
diag = true diag = true
log.Printf(format, args...) log.Printf(format, args...)

View file

@ -968,9 +968,10 @@ func readSymABIs(file, myimportpath string) {
if len(parts) != 3 { if len(parts) != 3 {
log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0]) log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
} }
sym, abi := parts[1], parts[2] sym, abistr := parts[1], parts[2]
if abi != "ABI0" { // Only supported external ABI right now abi, valid := obj.ParseABI(abistr)
log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi) if !valid {
log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
} }
// If the symbol is already prefixed with // If the symbol is already prefixed with
@ -983,9 +984,9 @@ func readSymABIs(file, myimportpath string) {
// Record for later. // Record for later.
if parts[0] == "def" { if parts[0] == "def" {
symabiDefs[sym] = obj.ABI0 symabiDefs[sym] = abi
} else { } else {
symabiRefs[sym] = obj.ABI0 symabiRefs[sym] = abi
} }
default: default:
log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0]) log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])

View file

@ -502,6 +502,20 @@ const (
ABICount 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. // Attribute is a set of symbol attributes.
type Attribute uint32 type Attribute uint32

View file

@ -210,13 +210,30 @@ func (ctxt *Link) CanReuseProgs() bool {
return ctxt.Debugasm == 0 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 { func Dconv(p *Prog, a *Addr) string {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
WriteDconv(buf, p, a) writeDconv(buf, p, a, false)
return buf.String() 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) { 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 { switch a.Type {
default: default:
fmt.Fprintf(w, "type=%d", a.Type) fmt.Fprintf(w, "type=%d", a.Type)
@ -250,7 +267,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
case TYPE_BRANCH: case TYPE_BRANCH:
if a.Sym != nil { 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 { } else if a.Target() != nil {
fmt.Fprint(w, a.Target().Pc) fmt.Fprint(w, a.Target().Pc)
} else { } else {
@ -259,7 +276,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
case TYPE_INDIR: case TYPE_INDIR:
io.WriteString(w, "*") io.WriteString(w, "*")
a.WriteNameTo(w) a.writeNameTo(w, abiDetail)
case TYPE_MEM: case TYPE_MEM:
a.WriteNameTo(w) a.WriteNameTo(w)
@ -299,7 +316,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
case TYPE_ADDR: case TYPE_ADDR:
io.WriteString(w, "$") io.WriteString(w, "$")
a.WriteNameTo(w) a.writeNameTo(w, abiDetail)
case TYPE_SHIFT: case TYPE_SHIFT:
v := int(a.Offset) v := int(a.Offset)
@ -335,6 +352,11 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
} }
func (a *Addr) WriteNameTo(w io.Writer) { func (a *Addr) WriteNameTo(w io.Writer) {
a.writeNameTo(w, false)
}
func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
switch a.Name { switch a.Name {
default: default:
fmt.Fprintf(w, "name=%d", a.Name) fmt.Fprintf(w, "name=%d", a.Name)
@ -356,7 +378,7 @@ func (a *Addr) WriteNameTo(w io.Writer) {
reg = Rconv(int(a.Reg)) reg = Rconv(int(a.Reg))
} }
if a.Sym != nil { 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 { } else {
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
} }
@ -596,3 +618,10 @@ func Bool2int(b bool) int {
} }
return i return i
} }
func abiDecorate(a *Addr, abiDetail bool) string {
if !abiDetail || a.Sym == nil {
return ""
}
return fmt.Sprintf("<%s>", a.Sym.ABI())
}

View file

@ -39,3 +39,25 @@ func PathToPrefix(s string) string {
return string(p) 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
}