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

implement cir.initlist #1121

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft

Conversation

HerrCai0907
Copy link
Contributor

@HerrCai0907 HerrCai0907 commented Nov 13, 2024

I don't finish all work about cir.initlist. But I want to get some feedback about the cir design to make sure I am in correct way.

Fixed: #777

@bcardosolopes
Copy link
Member

cc @smeenai

@smeenai
Copy link
Collaborator

smeenai commented Nov 13, 2024

Thanks for looking at this!

I think the high-level idea is right here, but I'm wondering if we want to lift up the InitListExpr node instead of the CXXStdInitializerListExpr node. From what I can see in this change, we're still constructing the init list via a sequence of stores, and then have the higher-level op to get an initializer_list from that init list. That's definitely an improvement over the status quo, but having the init list itself be represented directly would be even better.

You raised the question of struct vs array inits in #777, and I think we can keep them separate. One other interesting difference is for InitListExprs that directly initialize an array vs. become part of an initializer_list; the former are stored in constant memory and the latter constructed via stores on the stack (https://godbolt.org/z/Ee47xcoaM). Have you looked into what causes that difference? Understanding why the codegen for those two cases differs would help us figure out the appropriate high-level design.

@HerrCai0907
Copy link
Contributor Author

That's definitely an improvement over the status quo, but having the init list itself be represented directly would be even better

I agree with you. I will try to re-design initlist.ctor as a higher abstract op to accept:

  1. initialize_list object
  2. array
  3. n sub-elements

And then "stored in constant memory" or "constructed via stores on the stack" can be determined during the lowering

WDYT?

@HerrCai0907
Copy link
Contributor Author

@smeenai Is this something you describe in origin PR? I don't do store in constant memory when lowering LLVM now but it should be possible.

Copy link

github-actions bot commented Nov 14, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@smeenai smeenai left a comment

Choose a reason for hiding this comment

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

Thank you! This will be a very nice improvement :)

At a high level, I think we want the op to look like (see also https://llvm.github.io/clangir/Dialect/cir-style-guide.html):

%0 = cir.std_initializer_list %elem1, %elem2, %elem3 : !s32i -> !ty_std3A3Ainitializer_list3Cint3E

It'll probably be simpler to decompose that into lower-level ops inside LoweringPrepare instead of lowering it to LLVM directly.

In the future we may want to lift up other forms of init lists as well, but just tackling std::initializer_list in this PR is a good starting point (and then we can incrementally build upon that in the future).

@@ -4900,6 +4900,12 @@ def ClearCacheOp : CIR_Op<"clear_cache", [AllTypesMatch<["begin", "end"]>]> {
}];
}

def InitListCtor : CIR_Op<"initlist.ctor"> {
let summary = "Starts a variable argument list";
Copy link
Collaborator

Choose a reason for hiding this comment

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

This needs updating.

@@ -4900,6 +4900,12 @@ def ClearCacheOp : CIR_Op<"clear_cache", [AllTypesMatch<["begin", "end"]>]> {
}];
}

def InitListCtor : CIR_Op<"initlist.ctor"> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

All op names end with Op, Given that this is specific to std::initializer_list (and not the generic clang InitListExpr concept, which we may also want to have a higher-level representation for in the future), I think we should also make the name indicate that.

Suggested change
def InitListCtor : CIR_Op<"initlist.ctor"> {
def StdInitializerListOp : CIR_Op<"std_initializer_list"> {

@@ -4900,6 +4900,12 @@ def ClearCacheOp : CIR_Op<"clear_cache", [AllTypesMatch<["begin", "end"]>]> {
}];
}

def InitListCtor : CIR_Op<"initlist.ctor"> {
let summary = "Starts a variable argument list";
let arguments = (ins CIR_PointerType:$initlist,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it'd be better if the argument was just the arg list, and the op produced a result of the appropriate type. That way you can add the SameTypeOperands trait to verify that all the operand types are the same. You'll also want to verify that the result type is consistent with the argument type; LoadOp uses the TypesMatchWith trait for that, but our logic is a bit more complex, so you might need to implement a custom verifier (https://mlir.llvm.org/docs/DefiningDialects/Operations/#custom-verifier-code).

#endif
mlir::Type llvmArrayType =
mlir::LLVM::LLVMArrayType::get(llvmElementType, argListSize);
mlir::Value llvmArrayPtr = rewriter.create<mlir::LLVM::AllocaOp>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's important for all the allocas to be at the start of the function, otherwise LLVM will generate worse code.

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.

higher level representation for initlist
3 participants