diff --git a/attr.go b/attr.go new file mode 100644 index 0000000..af3d8f0 --- /dev/null +++ b/attr.go @@ -0,0 +1,96 @@ +package avo + +import ( + "fmt" + "math/bits" + "strings" +) + +// Attribute represents TEXT or DATA flags. +type Attribute uint16 + +// Reference: https://github.com/golang/go/blob/c043fc4f655ce34f67a0e7fe2833139f6313a3f0/src/runtime/textflag.h#L11-L34 +// +// // 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 +// +const ( + NOPROF Attribute = 1 << iota + DUPOK + NOSPLIT + RODATA + NOPTR + WRAPPER + NEEDCTXT + _ + TLSBSS + NOFRAME + REFLECTMETHOD +) + +// 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 { + parts, rest := a.split() + if len(parts) == 0 || rest != 0 { + parts = append(parts, fmt.Sprintf("%d", rest)) + } + return strings.Join(parts, "|") +} + +// ContainsTextFlags() returns whether the Asm() representation requires macros in "textflags.h". +func (a Attribute) ContainsTextFlags() bool { + flags, _ := a.split() + return len(flags) > 0 +} + +// split splits a into known flags and any remaining bits. +func (a Attribute) split() ([]string, Attribute) { + var flags []string + var rest Attribute + for a != 0 { + i := uint(bits.TrailingZeros16(uint16(a))) + bit := Attribute(1) << uint(i) + if flag := attrname[bit]; flag != "" { + flags = append(flags, flag) + } else { + rest |= bit + } + a ^= bit + } + 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", +} diff --git a/attr_test.go b/attr_test.go new file mode 100644 index 0000000..6a0d596 --- /dev/null +++ b/attr_test.go @@ -0,0 +1,42 @@ +package avo + +import "testing" + +func TestAttributeAsm(t *testing.T) { + cases := []struct { + Attribute Attribute + Expect string + }{ + {0, "0"}, + {32768, "32768"}, + {1, "NOPROF"}, + {DUPOK, "DUPOK"}, + {RODATA | NOSPLIT, "NOSPLIT|RODATA"}, + {WRAPPER | 16384 | NOPTR, "NOPTR|WRAPPER|16384"}, + {NEEDCTXT + NOFRAME + TLSBSS + REFLECTMETHOD, "NEEDCTXT|TLSBSS|NOFRAME|REFLECTMETHOD"}, + } + for _, c := range cases { + got := c.Attribute.Asm() + if got != c.Expect { + t.Errorf("Attribute(%d).Asm() = %#v; expect %#v", c.Attribute, got, c.Expect) + } + } +} + +func TestAttributeContainsTextFlags(t *testing.T) { + cases := []struct { + Attribute Attribute + Expect bool + }{ + {0, false}, + {32768, false}, + {1, true}, + {DUPOK, true}, + {WRAPPER | 16384 | NOPTR, true}, + } + for _, c := range cases { + if c.Attribute.ContainsTextFlags() != c.Expect { + t.Errorf("%s: ContainsTextFlags() expected %#v", c.Attribute.Asm(), c.Expect) + } + } +}