m4b/faerie

Permit duplicate declarations?

sunfishcode opened this issue · 7 comments

I'm porting some code to the new declare etc. APIs, and I'm finding it inconvenient that I can't declare an import more than once. Of course, I could always maintain my own HashMap or whatever to keep track of what I've already declared, but since Artifact already has one, declarations, it'd be nice if it could help me out.

m4b commented

Yea, I wondered about this.

This could also come up with weak symbols.

Should we just permit it for imports? I’m not sure the behavior for anything other than that will be reasonable.

Also what happens if you redeclare the import with a different Decl ? Just overwrite I assume ?

This looks like a good start.

In some situations I also want to declare a function that's already defined. For example, if I'm compiling some embedded code that defines its own memcpy, and later on the compiler generates a call to memcpy to copy a struct or something.

If the Decl ever changes, it doesn't seem like things can end well. Would an an ArtifactError for that case make sense?

m4b commented

@sunfishcode So reading through your response a couple times, I'm not quite sure what usecase is being motivated.

Could you give some more details/perhaps provide an example?

Specifically, if I'm understanding correctly, I don't understand the usecase (at least for faerie) where you don't already know:

  1. the declarations of every symbol, and
  2. for the non-imported symbols, the exact definition of each symbol.

E.g., if we're generating native object files, the above conditions seem like preconditions for doing so, no? (for JIT 2 can obviously be relaxed, but we're not JITing if we're outputting an object file)

Does that make any sense?

My use case is that I know, up front, the names of all defined symbols, but I don't always know the names of all the symbols that I'm going to end up calling.

For example, suppose I'm compiling this C code:

struct S { char buffer[1024]; };

struct S foo(struct S x) {
    return x;
}

I can know pretty early in the compiler that I'll be defining foo. However, I may not know that I need a memcpy import until quite late. I think this is already supported by the current patch here; I can add a memcpy declaration even after I've started emitting code.

The twist is, in some embedded environments without a libc, people often provide their own definition of memcpy for the compiler to use. That might look like this:

void *memcpy(...) { ... }

struct S { char buffer[1024]; };

struct S foo(struct S x) {
    return x;
}

In this scenario, my list of symbols I'm defining up front is memcpy and foo. But then while compiling foo, my code generator wants to import memcpy. It's possible for me to do my own bookkeeping and know that I already have a memcpy and avoid re-declaring it. However, it seems like faerie could easily do the right thing for me.

Does that make sense?

m4b commented

Thanks for the examples! Yea it’s a bit clearer now, but for the above second example, iiuc, faerie already does this as of referenced commit, yes?

Oh I think I understand now, you also want to redeclare defined symbols ? Or perhaps even better a kind of upsert operation over declarations ?

And in the case the redeclaration differs we can return conflicting declaration perhaps ?

Trying it again, it looks like the referenced commit does handle this. I may have misread it earlier. So I think we're good here!

m4b commented

:( sorry should have added note after the referring commit that it added ability to do duplicate import declarations