Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/declextract: rework consts handling #5682

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type Include struct {
}

func (n *Include) Info() (Pos, string, string) {
return n.Pos, tok2str[tokInclude], ""
return n.Pos, tok2str[tokInclude], n.File.Value
}

type Incdir struct {
Expand Down
38 changes: 38 additions & 0 deletions pkg/compiler/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,44 @@ func CollectUnused(desc *ast.Description, target *targets.Target, eh ast.ErrorHa
return nodes, nil
}

// CollectUnusedConsts returns unused defines/includes. This is used only for auto-generated descriptions.
func CollectUnusedConsts(desc *ast.Description, target *targets.Target, includeUse map[string]string,
eh ast.ErrorHandler) ([]ast.Node, error) {
comp := createCompiler(desc, target, eh)
comp.typecheck()
if comp.errors > 0 {
return nil, errors.New("typecheck failed")
}

var unused []ast.Node
for file, info := range comp.extractConsts() {
if !comp.fileMeta(ast.Pos{File: file}).Automatic {
continue
}
usedDefines := make(map[string]bool)
usedIncludes := make(map[string]bool)
for _, c := range info.Consts {
if c.Used {
usedDefines[c.Name] = true
usedIncludes[includeUse[c.Name]] = true
}
}
for _, decl := range comp.desc.Nodes {
switch n := decl.(type) {
case *ast.Define:
if n.Pos.File == file && !usedDefines[n.Name.Name] {
unused = append(unused, n)
}
case *ast.Include:
if n.Pos.File == file && !usedIncludes[n.File.Value] {
unused = append(unused, n)
}
}
}
}
return unused, nil
}

func (comp *compiler) collectUnused() []ast.Node {
var unused []ast.Node

Expand Down
13 changes: 9 additions & 4 deletions pkg/compiler/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type ConstInfo struct {
type Const struct {
Name string
Pos ast.Pos
Used bool // otherwise only defined
}

func ExtractConsts(desc *ast.Description, target *targets.Target, eh ast.ErrorHandler) map[string]*ConstInfo {
Expand Down Expand Up @@ -86,7 +87,7 @@ func (comp *compiler) extractConsts() map[string]*ConstInfo {
comp.error(pos, "redefining builtin const %v", name)
}
info.defines[name] = v
comp.addConst(ctx, pos, name)
ctx.addConst(pos, name, false)
case *ast.Call:
if comp.target.HasCallNumber(n.CallName) {
comp.addConst(ctx, pos, comp.target.SyscallPrefix+n.CallName)
Expand Down Expand Up @@ -178,10 +179,10 @@ func (comp *compiler) addConst(ctx *constContext, pos ast.Pos, name string) {
if _, isFlag := comp.intFlags[name]; isFlag {
return
}
ctx.addConst(pos, name)
ctx.addConst(pos, name, true)
for _, instantions := range ctx.instantionStack {
for _, pos1 := range instantions {
ctx.addConst(pos1, name)
ctx.addConst(pos1, name, true)
}
}
}
Expand All @@ -193,11 +194,15 @@ type constInfo struct {
incdirArray []string
}

func (ctx *constContext) addConst(pos ast.Pos, name string) {
func (ctx *constContext) addConst(pos ast.Pos, name string, used bool) {
info := ctx.getConstInfo(pos)
if c := info.consts[name]; c != nil && c.Used {
used = true
}
info.consts[name] = &Const{
Pos: pos,
Name: name,
Used: used,
}
}

Expand Down
64 changes: 50 additions & 14 deletions pkg/declextract/declextract.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, trace io.Writer) (
[]byte, []*Interface, error) {
[]byte, []*Interface, map[string]string, error) {
ctx := &context{
Output: out,
probe: probe,
Expand All @@ -29,15 +29,15 @@ func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string,
}
ctx.processFunctions()
ctx.processTypingFacts()
ctx.processIncludes()
includeUse := ctx.processConsts()
ctx.processEnums()
ctx.processStructs()
ctx.processSyscalls()
ctx.processIouring()

ctx.serialize()
ctx.finishInterfaces()
return ctx.descriptions.Bytes(), ctx.interfaces, errors.Join(ctx.errs...)
return ctx.descriptions.Bytes(), ctx.interfaces, includeUse, errors.Join(ctx.errs...)
}

type context struct {
Expand All @@ -47,13 +47,20 @@ type context struct {
structs map[string]*Struct
funcs map[string]*Function
facts map[string]*typingNode
includes []string
defines []define
uniqualizer map[string]int
interfaces []*Interface
descriptions *bytes.Buffer
debugTrace io.Writer
errs []error
}

type define struct {
Name string
Value string
}

func (ctx *context) error(msg string, args ...any) {
ctx.errs = append(ctx.errs, fmt.Errorf(msg, args...))
}
Expand All @@ -68,14 +75,7 @@ func (ctx *context) trace(msg string, args ...any) {
}
}

func (ctx *context) processIncludes() {
// These additional includes must be at the top, because other kernel headers
// are broken and won't compile without these additional ones included first.
ctx.Includes = append([]string{
"vdso/bits.h",
"linux/types.h",
"net/netlink.h",
}, ctx.Includes...)
func (ctx *context) processConsts() map[string]string {
replaces := map[string]string{
// Arches may use some includes from asm-generic and some from arch/arm.
// If the arch used for extract used asm-generic for a header,
Expand All @@ -84,11 +84,47 @@ func (ctx *context) processIncludes() {
"include/uapi/asm-generic/ioctls.h": "asm/ioctls.h",
"include/uapi/asm-generic/sockios.h": "asm/sockios.h",
}
for i, inc := range ctx.Includes {
if replace := replaces[inc]; replace != "" {
ctx.Includes[i] = replace
defineDedup := make(map[string]bool)
includeUse := make(map[string]string)
for _, ci := range ctx.Consts {
if strings.Contains(ci.Filename, "/uapi/") && !strings.Contains(ci.Filename, "arch/x86/") &&
strings.HasSuffix(ci.Filename, ".h") {
filename := ci.Filename
if replace := replaces[filename]; replace != "" {
filename = replace
}
ctx.includes = append(ctx.includes, filename)
includeUse[ci.Name] = filename
continue
}
// Remove duplicate defines (even with different values). Unfortunately we get few of these.
// There are some syscall numbers (presumably for 32/64 bits), and some macros that
// are defined in different files to different values (e.g. WMI_DATA_BE_SVC).
// Ideally we somehow rename defines (chosing one random value is never correct).
// But for now this helps to prevent compilation errors.
if defineDedup[ci.Name] {
continue
}
defineDedup[ci.Name] = true
ctx.defines = append(ctx.defines, define{
Name: ci.Name,
Value: fmt.Sprint(ci.Value),
})
}
ctx.includes = sortAndDedupSlice(ctx.includes)
ctx.defines = sortAndDedupSlice(ctx.defines)
// These additional includes must be at the top, because other kernel headers
// are broken and won't compile without these additional ones included first.
ctx.includes = append([]string{
"vdso/bits.h",
"linux/types.h",
"net/netlink.h",
}, ctx.includes...)
// Also pretend they are used.
includeUse["__NR_read"] = "vdso/bits.h"
includeUse["__NR_write"] = "linux/types.h"
includeUse["__NR_close"] = "net/netlink.h"
return includeUse
}

func (ctx *context) processEnums() {
Expand Down
20 changes: 9 additions & 11 deletions pkg/declextract/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import (

type Output struct {
Functions []*Function `json:"functions,omitempty"`
Includes []string `json:"includes,omitempty"`
Defines []*Define `json:"defines,omitempty"`
Consts []*ConstInfo `json:"consts,omitempty"`
Enums []*Enum `json:"enums,omitempty"`
Structs []*Struct `json:"structs,omitempty"`
Syscalls []*Syscall `json:"syscalls,omitempty"`
Expand All @@ -36,9 +35,10 @@ type Function struct {
facts map[string]*typingNode
}

type Define struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
type ConstInfo struct {
Name string `json:"name"`
Filename string `json:"filename"`
Value int64 `json:"value"`
}

type Field struct {
Expand Down Expand Up @@ -199,8 +199,7 @@ type EntityGlobalAddr struct {

func (out *Output) Merge(other *Output) {
out.Functions = append(out.Functions, other.Functions...)
out.Includes = append(out.Includes, other.Includes...)
out.Defines = append(out.Defines, other.Defines...)
out.Consts = append(out.Consts, other.Consts...)
out.Enums = append(out.Enums, other.Enums...)
out.Structs = append(out.Structs, other.Structs...)
out.Syscalls = append(out.Syscalls, other.Syscalls...)
Expand All @@ -212,8 +211,7 @@ func (out *Output) Merge(other *Output) {

func (out *Output) SortAndDedup() {
out.Functions = sortAndDedupSlice(out.Functions)
out.Includes = sortAndDedupSlice(out.Includes)
out.Defines = sortAndDedupSlice(out.Defines)
out.Consts = sortAndDedupSlice(out.Consts)
out.Enums = sortAndDedupSlice(out.Enums)
out.Structs = sortAndDedupSlice(out.Structs)
out.Syscalls = sortAndDedupSlice(out.Syscalls)
Expand All @@ -229,8 +227,8 @@ func (out *Output) SetSourceFile(file string, updatePath func(string) string) {
for _, fn := range out.Functions {
fn.File = updatePath(fn.File)
}
for i, inc := range out.Includes {
out.Includes[i] = updatePath(inc)
for _, ci := range out.Consts {
ci.Filename = updatePath(ci.Filename)
}
for _, call := range out.Syscalls {
call.SourceFile = file
Expand Down
4 changes: 2 additions & 2 deletions pkg/declextract/serialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ func (ctx *context) fmt(msg string, args ...any) {
}

func (ctx *context) serializeIncludes() {
for _, inc := range ctx.Includes {
for _, inc := range ctx.includes {
ctx.fmt("include <%s>\n", inc)
}
ctx.fmt("\n")
}

func (ctx *context) serializeDefines() {
for _, def := range ctx.Defines {
for _, def := range ctx.defines {
ctx.fmt("define %v %v\n", def.Name, def.Value)
}
ctx.fmt("\n")
Expand Down
Loading
Loading