This is an archive of the discontinued LLVM Phabricator instance.

[TableGen] Introduce function and lambda
Needs ReviewPublic

Authored by wangpc on Apr 21 2023, 3:37 AM.

Details

Summary

Earlier discussion: D147131

For function, it is just a grammar sugar of class-based subroutines.
For lambda, it is a anonymous function with captured variables.

Grammar:
function

function name(type arg0, ...): type {
  assert xxx, "";
  defvar xxx = xxx;
  return xxx;
}

lambda

function(type arg0, ...): type {
  ...
}

Only assert, defvar and return are allowed. Each function
should return a value.

Function types are represented as function<rettype[, argtype]*>,
and rettype is mandatory. Function types can be used in field,
arguments or foreach iterator.

Recursive function is supported and we can also return a function
(or lambda) value in a function.

The function can be called with funcname(args). However, there
is a grammar ambiguity between DAG and function call. To distinguish
them, calls inside DAGs should add a single quote before arguments,
like funcname'(args).

Please see llvm/docs/TableGen/ProgRef.rst for more details.

More usage: D149017

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptApr 21 2023, 3:37 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
pcwang-thead requested review of this revision.Apr 21 2023, 3:37 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 21 2023, 3:37 AM

Rename existed function.

Rename existed function.

tra added a comment.Apr 21 2023, 11:39 AM

This is really nice!

Now that we have functions, I would really love to see some sort of dump function which would print the value it's given when it's evaluated. This would make debugging tablegen files so much easier.

llvm/docs/TableGen/ProgRef.rst
205–210

The list of keywords should be sorted.

302

Do we need to define some sort of void type?

llvm/test/TableGen/function.td
7

I'd also add a few more test cases:

  • return an immediate value.
  • return an already-defined record or its field.
  • Check that a local defvar has priority over an already defined record with the same name.
32

Can we ignore the value of a function call? E.g. one use case would be to use a function to consolidate various assertion checks without actually returning anything useful otherwise.

382

Do we limit recursion depth or prevent runaway evaluation otherwise?

  • Support function calls inside DAG.
  • Add more tests.
  • Update document.
pcwang-thead marked 5 inline comments as done.EditedApr 23 2023, 1:37 AM

This is really nice!

Now that we have functions, I would really love to see some sort of dump function which would print the value it's given when it's evaluated. This would make debugging tablegen files so much easier.

Yeah, this is a good advice. But I think it won't be implemented as function, but something like assert statements. I will put it to my future work list. :-)

llvm/docs/TableGen/ProgRef.rst
302

All statements of TableGen(except assert) should produce a value. It may be worthwhile to add void, but I may add it to future work list.

llvm/test/TableGen/function.td
32

Same as above reply.
All statements of TableGen (except assert) should produce a value. It could be hard to implement what you suggest, but I may have a try later.

382

Currently no. It would make the implementation hard to understand if we try to limit recursion depth.

pcwang-thead edited the summary of this revision. (Show Details)Apr 23 2023, 1:38 AM
pcwang-thead marked 3 inline comments as done.

Based on D149016.

pcwang-thead retitled this revision from [WIP][TableGen] Introduce function and lambda to [TableGen] Introduce function and lambda.Apr 24 2023, 2:27 AM
pcwang-thead edited the summary of this revision. (Show Details)

Fix function type conversion error (return type is covariant and argument types are contravariant).

pcwang-thead edited the summary of this revision. (Show Details)Apr 24 2023, 4:36 AM
hliao added a subscriber: hliao.Apr 24 2023, 10:27 PM

Just some doc review, will think about the rest if I get time.

llvm/docs/TableGen/ProgRef.rst
710

This would benefit from being split, perhaps into bullet points.

  • required arguments
  • optional arguments
  • required must precede optional
  • default values are evaluated left to right

Then it's clearer that this is a set of rules that one can apply in order (roughly).

712

It would be useful to provide an example function just above this. Then after this statement, an example of what "can be used in arguments as values" looks like.

716

Usually this would be written as "Recursive functions are" (though you could recurse with only one function, so it's not wrong).

718

Space between Lambda and the brackets.

726

This could be clearer because it reads like the captured variables are somehow anonymous.

How about: "is an anonymous function that contains all captured variables".

733

What is the implication of this priority list? If for example I have a foo in the inner scope, it will capture that over the foo in the outer scope?

An example would help. Something like this after the list. "By applying those priorities we can see that in the example below, a is taken from....."

737

"of the lambda"

745

Extra . here, remove that.

747

captued -> captured

749

Capitalise the C on call

765

This "when emitting" doesn't make sense to me, but as stated, I know nothing about parsers :)

What does "emitting" mean here?

1554

You don't need the "but" here.

1555

"using a function", though "to a function" isn't wrong just not usual.

2118

...or instead of the example being above, link to this from the docs.

  • Rebase.
  • Update doc.
pcwang-thead marked 14 inline comments as done.

Update doc.

Ping. Can anyone review the code please? :-)

craig.topper added inline comments.May 25 2023, 10:24 PM
llvm/include/llvm/TableGen/Record.h
1669

Are there 3 available bits on a 32-bit host?

llvm/lib/TableGen/Record.cpp
2824

Why does this need to be a std::string?

tra added inline comments.May 26 2023, 2:42 PM
llvm/docs/TableGen/ProgRef.rst
302

function that returns

303

may have zero or more arguments with *argtype*

303–304

I'm not sure we need it. I think it's implied that any valid type is a... valid type. It would make sense to list restrictions, if we had any, but given that there are none, I think we can just skip it.

305

compatible is not defined anywhere in the preceeding text. You may want to add a definition at the beginning of the section.

534–535

Do we have anything else, other than function reference which we would consider to be a callable?
If callable == function reference, perhaps we can rephrase it like that.

E.g. "*value* must evaluate to a function reference."

687

"defines a function"

tra added inline comments.May 26 2023, 3:22 PM
llvm/include/llvm/TableGen/Record.h
1765

We don't seem to set it anywhere to anything other than __return__.
Can we just return the right name from getReturnValueName() directly:

StringInit *getReturnValueName() const {
    return StringInit::get(getRecords(), "__return__");
  }
llvm/lib/TableGen/Record.cpp
2824

We may not need it at all.

tra added a comment.May 26 2023, 3:28 PM

Regarding the docs -- jlebar@ suggested getting ChatGPT to help with editing the docs.

Q; I'm writing documentation for new functionality added to LLVM's tablegen tool. Please edit this documentation for grammar, clarity and precision.

+ <snippet of the doc section to be edited.>

It does appear to do a pretty decent job fixing the wording nits.

  • Remove static field ReturnValueName.
  • Split TyAndKind into Ty and Kind.
  • Update doc.
  • Rebase
pcwang-thead marked 10 inline comments as done.Jun 1 2023, 2:47 AM

Regarding the docs -- jlebar@ suggested getting ChatGPT to help with editing the docs.

Q; I'm writing documentation for new functionality added to LLVM's tablegen tool. Please edit this documentation for grammar, clarity and precision.

+ <snippet of the doc section to be edited.>

It does appear to do a pretty decent job fixing the wording nits.

Thanks! I will have a try!

llvm/docs/TableGen/ProgRef.rst
709

Are multiple optional arguments supported? They are for classes that are used as subroutines.

If so, can we document how to pass optional arguments that are not first in the list of optional arguments? For example some languages allow f(int a, optional int b = 2, optional int c = 3); f(3, c=4);. Does it work like this or do we need to pass b if we want to pass c?

llvm/include/llvm/TableGen/Record.h
1710

/// Is this a return value?

llvm/test/TableGen/lambda.td
26

Do we need to test that argument's take precedence over captures?

int x = 4;
function<int, int> f = function (int x): int { return x; }
f(5); // Should evaluate to 5, not 4.

In the tests below, there is no int b that exists to be captured.

llvm/docs/TableGen/ProgRef.rst
709

Yes, they are the same as class-based subroutines.

Currently, we should pass optional argument b if we want to pass optional argument c as what you said. This is the same as arguments of classes. I have thought about something like f(3, c: 4) since we have something like cond: value in bang operator !cond already. I will post this implementation in a few days.

wangpc commandeered this revision.Jun 15 2023, 9:06 AM
wangpc added a reviewer: pcwang-thead.
wangpc removed a reviewer: pcwang-thead.