Skip to content

dlopen: Progress towards compiling procedure pointers with C backend#27576

Merged
dlongnecke-cray merged 3 commits intochapel-lang:mainfrom
dlongnecke-cray:dynamic-loading-10
Aug 8, 2025
Merged

dlopen: Progress towards compiling procedure pointers with C backend#27576
dlongnecke-cray merged 3 commits intochapel-lang:mainfrom
dlongnecke-cray:dynamic-loading-10

Conversation

@dlongnecke-cray
Copy link
Contributor

@dlongnecke-cray dlongnecke-cray commented Jul 31, 2025

This branch adds my initial progress towards compiling procedure pointers with the C backend.

Remove faulty code I wrote to try and do GC of function types. As a consequence they may never be GC'd, but that should not result in leaks because they will also never be removed from the AST. I will focus on pruning them more actively in a future effort.

Rearrange how the compiler collects and generates function types in codegen_header. An initial pass over gTypeSymbols collect function types that are used directly (i.e., they have a SymExpr referring to them and isUsed() == true). Later, while collecting FnSymbol*, the types of functions used to construct procedure pointer values will be collected as well.

Represent local proc as void* in C in order to handle a circular dependency issue with struct value types.

Write code to convert FunctionType* to a C function pointer type, but comment it out. It might be useful later.

This branch is followed up by #27588.

Reviewed by @jabraham17. Thanks!

@dlongnecke-cray dlongnecke-cray self-assigned this Jul 31, 2025
Remove some old code doing GC of 'FunctionType'. It's not doing what
I thought it did and could cause 'null' holes in the 'gTypeSymbols'
global vector. I'll worry about GC with '--verify' at a later point
in this effort.

There is context-sensitive code in 'SymExpr::codegen()' which checks
to see how a 'SymExpr' over a 'FnSymbol' is used. If it looks like
it's being used as value, we generate it differently by adding it to
the procedure pointer cache and producing a wide index.

Previously this code was only firing for LLVM. I moved it out into a
more "backend agnostic" block of code.

I added code to generate a C function type from a 'FunctionType', but
currently that code is commented out. I am opting to generate 'wide
proc' as 'int64_t' and 'local proc' as 'void*' in order to work around
circular dependencies in C (e.g., a record contains a procedure
pointer and the procedure pointer accepts that record as a formal).

Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
@dlongnecke-cray dlongnecke-cray changed the title Make progress towards supporting procedure pointers with C backend dlopen: Progress towards compiling procedure pointers with C backend Aug 1, 2025
Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
@jabraham17 jabraham17 self-requested a review August 7, 2025 19:24

// TODO (dlongnecke): The C backend requires a cast to a procedure
// type in order to call, while the LLVM backend does not.
if (gGenInfo->cfile) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider an INT_ASSERT here

Comment on lines -4171 to -4173
// And make sure the LLVM type for the function is generated.
if (chplFnType) chplFnType->codegenDef();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just double checking my understanding, this is no longer needed because we are eagerly generating function types right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after reading the rest of the PR, I think this is the case

Copy link
Contributor Author

@dlongnecke-cray dlongnecke-cray Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, not only that, but in the past we could redundantly generate function types under LLVM because it is idempotent under that backend. EDIT: It still is, but we don't need to redundantly try 😄.

static void codegenFunctionTypeWide(FunctionType* ft) {
if (auto outfile = gGenInfo->cfile) {
// TODO: Should I get the code-generated 'int(64)' type here instead?
fprintf(outfile, "typedef %s %s;\n\n", "int64_t", ft->symbol->cname);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should solve the TODO

Should be dtInt[INT_SIZE_64]->codegen().c.c_str()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably shortcut that as dtInt[INT_SIZE_64]->cname

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that should work, but the //TODO is mainly there because I'm not really sure what the precedent is. Do we care to do that? Because we don't really seem to do it consistently, we'll just generate int64_t and void* etc in other places. What's your preference here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference would be an approach that has both precedent and means that "int64_t" only shows up in our code once for codegen. I pulled that snippet from VarSymbol::codegenDef, but as long as there is precedent I am good with it

GenInfo* info = gGenInfo;

// Collected when considering both types and 'FnSymbol'.
std::set<FunctionType*> fnTypesToCodegen;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider std::unordered_set or llvm::DenseSet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or even llvm::SmallPtrSet, if you expect fnTypesToCodegen to be small

Comment on lines 1830 to 1834
auto it = fnTypesToCodegen.find(ft);
if (it == fnTypesToCodegen.end()) {
// TODO (dlongnecke): Rare case right now, so leave breakpoint...
debuggerBreakHere();
fnTypesToCodegen.emplace_hint(it, ft);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit suggestion

      if (auto it = fnTypesToCodegen.find(ft); it == fnTypesToCodegen.end()) {

Comment on lines 1865 to 1866
auto it = fnTypesToCodegen.find(ft);
if (it == fnTypesToCodegen.end()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit suggestion

        if (auto it = fnTypesToCodegen.find(ft); it == fnTypesToCodegen.end()) {

Signed-off-by: David Longnecker <dlongnecke-cray@users.noreply.github.com>
@dlongnecke-cray dlongnecke-cray merged commit b87f499 into chapel-lang:main Aug 8, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants