Skip to content

[libc] Make hdrgen deduce header type lists from function signatures #127251

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

Merged
merged 2 commits into from
Feb 14, 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
57 changes: 50 additions & 7 deletions libc/utils/hdrgen/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,68 @@
#
# ==-------------------------------------------------------------------------==#

import re
from type import Type


# These are the keywords that appear in C type syntax but are not part of the
# include file name. This is all of the modifiers, qualifiers, and base types,
# but not "struct".
KEYWORDS = [
"_Atomic",
"_Complex",
"_Float16",
"_Noreturn",
"__restrict",
"accum",
"char",
"const",
"double",
"float",
"fract",
"int",
"long",
"short",
"signed",
"unsigned",
"void",
"volatile",
]
NONIDENTIFIER = re.compile("[^a-zA-Z0-9_]+")


class Function:
def __init__(
self, return_type, name, arguments, standards, guard=None, attributes=[]
):
assert return_type
self.return_type = return_type
self.name = name
self.arguments = [
arg if isinstance(arg, str) else arg["type"] for arg in arguments
]
assert all(self.arguments)
self.standards = standards
self.guard = guard
self.attributes = attributes or ""
self.attributes = attributes or []

def signature_types(self):
def collapse(type_string):
assert type_string
# Split into words at nonidentifier characters (`*`, `[`, etc.),
# filter out keywords and numbers, and then rejoin with "_".
return "_".join(
word
for word in NONIDENTIFIER.split(type_string)
if word and not word.isdecimal() and word not in KEYWORDS
)

all_types = [self.return_type] + self.arguments
return {
Type(string) for string in filter(None, (collapse(t) for t in all_types))
}

def __str__(self):
attributes_str = " ".join(self.attributes)
attrs_str = "".join(f"{attr} " for attr in self.attributes)
arguments_str = ", ".join(self.arguments) if self.arguments else "void"
if attributes_str == "":
result = f"{self.return_type} {self.name}({arguments_str})"
else:
result = f"{attributes_str} {self.return_type} {self.name}({arguments_str})"
return result
return attrs_str + f"{self.return_type} {self.name}({arguments_str})"
57 changes: 45 additions & 12 deletions libc/utils/hdrgen/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,33 @@
#
# ==-------------------------------------------------------------------------==#

from functools import reduce
from pathlib import PurePosixPath


STDINT_SIZES = [
"16",
"32",
"64",
"8",
"least16",
"least32",
"least64",
"least8",
"max",
"ptr",
]

COMPILER_HEADER_TYPES = (
{
"bool": "<stdbool.h>",
"va_list": "<stdarg.h>",
}
| {f"int{size}_t": "<stdint.h>" for size in STDINT_SIZES}
| {f"uint{size}_t": "<stdint.h>" for size in STDINT_SIZES}
)


class HeaderFile:
def __init__(self, name):
self.template_file = None
Expand All @@ -34,19 +58,25 @@ def add_object(self, object):
def add_function(self, function):
self.functions.append(function)

def includes(self):
return sorted(
{
PurePosixPath("llvm-libc-macros") / macro.header
for macro in self.macros
if macro.header is not None
}
| {
PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h"
for typ in self.types
}
def all_types(self):
return reduce(
lambda a, b: a | b,
[f.signature_types() for f in self.functions],
set(self.types),
)

def includes(self):
return {
PurePosixPath("llvm-libc-macros") / macro.header
for macro in self.macros
if macro.header is not None
} | {
COMPILER_HEADER_TYPES.get(
typ.type_name, PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h"
)
for typ in self.all_types()
}

def public_api(self):
# Python 3.12 has .relative_to(dir, walk_up=True) for this.
path_prefix = PurePosixPath("../" * (len(PurePosixPath(self.name).parents) - 1))
Expand All @@ -56,7 +86,10 @@ def relpath(file):

content = [
f"#include {file}"
for file in sorted(f'"{relpath(file)!s}"' for file in self.includes())
for file in sorted(
file if isinstance(file, str) else f'"{relpath(file)!s}"'
for file in self.includes()
)
]

for macro in self.macros:
Expand Down
2 changes: 1 addition & 1 deletion libc/utils/hdrgen/tests/expected_output/test_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

#include "__llvm-libc-common.h"
#include "llvm-libc-macros/float16-macros.h"
#include "llvm-libc-types/float128.h"

#include "llvm-libc-macros/test_more-macros.h"
#include "llvm-libc-macros/test_small-macros.h"
#include "llvm-libc-types/float128.h"
#include "llvm-libc-types/type_a.h"
#include "llvm-libc-types/type_b.h"

Expand Down
3 changes: 0 additions & 3 deletions libc/utils/hdrgen/tests/input/subdir/test.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
header: subdir/test.h
header_template: test.h.def
types:
- type_name: type_a
- type_name: type_b
functions:
- name: func
return_type: type_a
Expand Down
1 change: 0 additions & 1 deletion libc/utils/hdrgen/tests/input/test_small.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "__llvm-libc-common.h"
#include "llvm-libc-macros/float16-macros.h"
#include "llvm-libc-types/float128.h"

%%public_api()

Expand Down
7 changes: 7 additions & 0 deletions libc/utils/hdrgen/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@

class Type:
def __init__(self, type_name):
assert type_name
self.type_name = type_name

def __eq__(self, other):
return self.type_name == other.type_name

def __hash__(self):
return self.type_name.__hash__()
Loading