From c5faaae5837f9e1591dbf278f2a513912bed939f Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Sat, 10 Apr 2021 23:45:40 -0700 Subject: [PATCH] attr: generate code from textflag.h (#176) --- attr/attr.go | 59 +------------ attr/make_textflag.go | 200 ++++++++++++++++++++++++++++++++++++++++++ attr/textflag.h | 39 ++++++++ attr/ztextflag.go | 57 ++++++++++++ 4 files changed, 297 insertions(+), 58 deletions(-) create mode 100644 attr/make_textflag.go create mode 100644 attr/textflag.h create mode 100644 attr/ztextflag.go diff --git a/attr/attr.go b/attr/attr.go index 016e0a4..301c9f5 100644 --- a/attr/attr.go +++ b/attr/attr.go @@ -10,50 +10,7 @@ import ( // Attribute represents TEXT or DATA flags. type Attribute uint16 -// Reference: https://github.com/golang/go/blob/aafe257390cc9048e8b5df898fabd79a9e0d4c39/src/runtime/textflag.h#L11-L37 -// -// // Don't profile the marked routine. This flag is deprecated. -// #define NOPROF 1 -// // It is ok for the linker to get multiple of these symbols. It will -// // pick one of the duplicates to use. -// #define DUPOK 2 -// // Don't insert stack check preamble. -// #define NOSPLIT 4 -// // Put this data in a read-only section. -// #define RODATA 8 -// // This data contains no pointers. -// #define NOPTR 16 -// // This is a wrapper function and should not count as disabling 'recover'. -// #define WRAPPER 32 -// // This function uses its incoming context register. -// #define NEEDCTXT 64 -// // Allocate a word of thread local storage and store the offset from the -// // thread local base to the thread local storage in this variable. -// #define TLSBSS 256 -// // Do not insert instructions to allocate a stack frame for this function. -// // Only valid on functions that declare a frame size of 0. -// // TODO(mwhudson): only implemented for ppc64x at present. -// #define NOFRAME 512 -// // Function can call reflect.Type.Method or reflect.Type.MethodByName. -// #define REFLECTMETHOD 1024 -// // Function is the top of the call stack. Call stack unwinders should stop -// // at this function. -// #define TOPFRAME 2048 -// -const ( - NOPROF Attribute = 1 << iota - DUPOK - NOSPLIT - RODATA - NOPTR - WRAPPER - NEEDCTXT - _ - TLSBSS - NOFRAME - REFLECTMETHOD - TOPFRAME -) +//go:generate go run make_textflag.go -output ztextflag.go // Asm returns a representation of the attributes in assembly syntax. This may use macros from "textflags.h"; see ContainsTextFlags() to determine if this header is required. func (a Attribute) Asm() string { @@ -86,17 +43,3 @@ func (a Attribute) split() ([]string, Attribute) { } return flags, rest } - -var attrname = map[Attribute]string{ - NOPROF: "NOPROF", - DUPOK: "DUPOK", - NOSPLIT: "NOSPLIT", - RODATA: "RODATA", - NOPTR: "NOPTR", - WRAPPER: "WRAPPER", - NEEDCTXT: "NEEDCTXT", - TLSBSS: "TLSBSS", - NOFRAME: "NOFRAME", - REFLECTMETHOD: "REFLECTMETHOD", - TOPFRAME: "TOPFRAME", -} diff --git a/attr/make_textflag.go b/attr/make_textflag.go new file mode 100644 index 0000000..83d7220 --- /dev/null +++ b/attr/make_textflag.go @@ -0,0 +1,200 @@ +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "go/format" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" +) + +var ( + TextFlagPath = "src/runtime/textflag.h" + TextFlagName = filepath.Base(TextFlagPath) +) + +var ( + download = flag.Bool("download", false, "download new version of "+TextFlagName) + version = flag.String("version", runtime.Version(), "go version to download file from") + textflag = flag.String("textflag", TextFlagName, "path to "+TextFlagName) + output = flag.String("output", "", "path to output file") +) + +func main() { + if err := mainerr(); err != nil { + log.Fatal(err) + } +} + +func mainerr() error { + flag.Parse() + + // Download new version, if requested. + if *download { + if err := DownloadGoSourceFile(*textflag, *version, TextFlagPath); err != nil { + return err + } + log.Printf("downloaded %q from version %s to %q", TextFlagPath, *version, *textflag) + } + + // Parse text flags header. + fs, err := ParseFile(*textflag) + if err != nil { + return err + } + + // Determine output. + w := os.Stdout + if *output != "" { + f, err := os.Create(*output) + if err != nil { + return err + } + defer f.Close() + w = f + } + + // Generate code and format it. + buf := bytes.NewBuffer(nil) + PrintFlagAttributes(buf, fs) + + src, err := format.Source(buf.Bytes()) + if err != nil { + return err + } + + // Write output. + _, err = w.Write(src) + if err != nil { + return err + } + + return nil +} + +// DownloadGoSourceFile downloads a Go source file from a specific version. +func DownloadGoSourceFile(outpath, v, srcpath string) (err error) { + // Download from github. + url := "https://github.com/golang/go/raw/" + v + "/" + srcpath + res, err := http.Get(url) + if err != nil { + return err + } + defer func() { + if errc := res.Body.Close(); errc != nil && err == nil { + err = errc + } + }() + + // Write to file. + buf := bytes.NewBuffer(nil) + fmt.Fprintf(buf, "// Code generated by downloading from %s. DO NOT EDIT.\n\n", url) + + if _, err := io.Copy(buf, res.Body); err != nil { + return err + } + + if err := ioutil.WriteFile(outpath, buf.Bytes(), 0644); err != nil { + return err + } + + return nil +} + +type Flag struct { + Doc []string + Name string + Value int +} + +// Parse text flags header format. +func Parse(r io.Reader) ([]Flag, error) { + var fs []Flag + var doc []string + + s := bufio.NewScanner(r) + for s.Scan() { + line := s.Text() + switch { + case strings.Contains(line, "TODO"): + // skip + + case strings.HasPrefix(line, "// "): + doc = append(doc, strings.TrimPrefix(line, "// ")) + + case strings.HasPrefix(line, "#define"): + fields := strings.Fields(line) + if len(fields) != 3 || fields[0] != "#define" { + return nil, fmt.Errorf("unexpected line format %q", line) + } + v, err := strconv.Atoi(fields[2]) + if err != nil { + return nil, err + } + + fs = append(fs, Flag{ + Doc: doc, + Name: fields[1], + Value: v, + }) + doc = nil + + case line == "" || line == "//": + doc = nil + + default: + return nil, errors.New("unexpected format") + } + } + + if err := s.Err(); err != nil { + return nil, err + } + + return fs, nil +} + +// ParseFile parses text flags header file. +func ParseFile(filename string) ([]Flag, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return Parse(bytes.NewReader(b)) +} + +func PrintFlagAttributes(w io.Writer, fs []Flag) { + _, self, _, _ := runtime.Caller(0) + fmt.Fprintf(w, "// Code generated by %s. DO NOT EDIT.\n\n", filepath.Base(self)) + fmt.Fprintf(w, "package attr\n") + + // Attribute constants. + fmt.Fprintf(w, "\n// Attribute values defined in %s.\n", TextFlagName) + fmt.Fprintf(w, "const (\n") + for _, f := range fs { + for _, line := range f.Doc { + fmt.Fprintf(w, "\t// %s\n", line) + } + fmt.Fprintf(w, "\t%s Attribute = %d\n\n", f.Name, f.Value) + } + fmt.Fprintf(w, ")\n") + + // String map. + fmt.Fprintf(w, "\nvar attrname = map[Attribute]string{\n") + for _, f := range fs { + fmt.Fprintf(w, "\t%s: %q,\n", f.Name, f.Name) + } + fmt.Fprintf(w, "}\n") +} diff --git a/attr/textflag.h b/attr/textflag.h new file mode 100644 index 0000000..7e3547f --- /dev/null +++ b/attr/textflag.h @@ -0,0 +1,39 @@ +// Code generated by downloading from https://github.com/golang/go/raw/go1.16.2/src/runtime/textflag.h. DO NOT EDIT. + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file defines flags attached to various functions +// and data objects. The compilers, assemblers, and linker must +// all agree on these values. +// +// Keep in sync with src/cmd/internal/obj/textflag.go. + +// Don't profile the marked routine. This flag is deprecated. +#define NOPROF 1 +// It is ok for the linker to get multiple of these symbols. It will +// pick one of the duplicates to use. +#define DUPOK 2 +// Don't insert stack check preamble. +#define NOSPLIT 4 +// Put this data in a read-only section. +#define RODATA 8 +// This data contains no pointers. +#define NOPTR 16 +// This is a wrapper function and should not count as disabling 'recover'. +#define WRAPPER 32 +// This function uses its incoming context register. +#define NEEDCTXT 64 +// Allocate a word of thread local storage and store the offset from the +// thread local base to the thread local storage in this variable. +#define TLSBSS 256 +// Do not insert instructions to allocate a stack frame for this function. +// Only valid on functions that declare a frame size of 0. +// TODO(mwhudson): only implemented for ppc64x at present. +#define NOFRAME 512 +// Function can call reflect.Type.Method or reflect.Type.MethodByName. +#define REFLECTMETHOD 1024 +// Function is the top of the call stack. Call stack unwinders should stop +// at this function. +#define TOPFRAME 2048 diff --git a/attr/ztextflag.go b/attr/ztextflag.go new file mode 100644 index 0000000..9330a19 --- /dev/null +++ b/attr/ztextflag.go @@ -0,0 +1,57 @@ +// Code generated by make_textflag.go. DO NOT EDIT. + +package attr + +// Attribute values defined in textflag.h. +const ( + // Don't profile the marked routine. This flag is deprecated. + NOPROF Attribute = 1 + + // It is ok for the linker to get multiple of these symbols. It will + // pick one of the duplicates to use. + DUPOK Attribute = 2 + + // Don't insert stack check preamble. + NOSPLIT Attribute = 4 + + // Put this data in a read-only section. + RODATA Attribute = 8 + + // This data contains no pointers. + NOPTR Attribute = 16 + + // This is a wrapper function and should not count as disabling 'recover'. + WRAPPER Attribute = 32 + + // This function uses its incoming context register. + NEEDCTXT Attribute = 64 + + // Allocate a word of thread local storage and store the offset from the + // thread local base to the thread local storage in this variable. + TLSBSS Attribute = 256 + + // Do not insert instructions to allocate a stack frame for this function. + // Only valid on functions that declare a frame size of 0. + NOFRAME Attribute = 512 + + // Function can call reflect.Type.Method or reflect.Type.MethodByName. + REFLECTMETHOD Attribute = 1024 + + // Function is the top of the call stack. Call stack unwinders should stop + // at this function. + TOPFRAME Attribute = 2048 +) + +var attrname = map[Attribute]string{ + NOPROF: "NOPROF", + DUPOK: "DUPOK", + NOSPLIT: "NOSPLIT", + RODATA: "RODATA", + NOPTR: "NOPTR", + WRAPPER: "WRAPPER", + NEEDCTXT: "NEEDCTXT", + TLSBSS: "TLSBSS", + NOFRAME: "NOFRAME", + REFLECTMETHOD: "REFLECTMETHOD", + TOPFRAME: "TOPFRAME", +}