Skip to content

Commit

Permalink
attr: generate code from textflag.h (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmcloughlin authored Apr 11, 2021
1 parent e5c9b4e commit c5faaae
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 58 deletions.
59 changes: 1 addition & 58 deletions attr/attr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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",
}
200 changes: 200 additions & 0 deletions attr/make_textflag.go
Original file line number Diff line number Diff line change
@@ -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")
}
39 changes: 39 additions & 0 deletions attr/textflag.h
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions attr/ztextflag.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c5faaae

Please sign in to comment.