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

Rebase of julia effects to LLVM #50188

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

gbaraldi
Copy link
Member

What other effects can we do, i.e readnone etc.

@gbaraldi
Copy link
Member Author

We might be able to add readnone if we see we have no pointer arguments

@pchintalapudi
Copy link
Member

That might conflict with the pgcstack being passed as an argument.

src/codegen.cpp Outdated
attr.addAttribute("consistent");
}
if (effect_free == 0) {
attr.addAttribute("effect_free");
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this not desirable to make readnone/only/write only where applicable

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I think we can add more things to the functions for sure.

src/codegen.cpp Outdated
attr.addAttribute("noinbounds");
}
if (is_decl && consistent == 0 && effect_free == 0 && nothrow == 1 && terminates == 1){
attr.addAttribute(Attribute::Speculatable);
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably want to change these to be added at functionindex

It may be worthwhile considering if any attributes on params are also useful

Copy link
Member Author

Choose a reason for hiding this comment

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

I think they might be, but I'm not sure what information we can infer from here

Copy link
Member Author

Choose a reason for hiding this comment

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

Also when creating the AttrList we pass the builder to the FnAttr builder

src/codegen.cpp Outdated
@@ -4204,7 +4256,13 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos
TheCallee = ai.decorateInst(ctx.builder.CreateAlignedLoad(TheCallee->getType(), GV, Align(sizeof(void*))));
}
CallInst *call = ctx.builder.CreateCall(cft, TheCallee, argvals);
call->setAttributes(returninfo.attrs);

if (isa<Function>(TheCallee)){
Copy link
Contributor

Choose a reason for hiding this comment

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

Make this a F=dyn_cast for ease?

@gbaraldi
Copy link
Member Author

I guess readnone is always wrong with or without the pgcstack arg, at least it's not a global anymore

@pchintalapudi
Copy link
Member

One thing that should be possible is that if we determine a function has the terminates effect we should just not insert a safepoint at entry.

@gbaraldi
Copy link
Member Author

The issue with that is that if you have a for loop that calls the function a bunch of times you aren't ever hitting a safepoint.

@pchintalapudi
Copy link
Member

That's true, but if we've proven a function to terminate, its runtime is probably really small and the safepoint will probably take up a good fraction of its runtime, so it's likely that even a loop on a terminates function will itself exit quickly.

@gbaraldi
Copy link
Member Author

But I think that's kind of the point, because we don't know termination for loops, only for the function itself, and it is what's guaranteeing that a GC may run.

@gbaraldi gbaraldi marked this pull request as ready for review June 21, 2023 18:28
@gbaraldi
Copy link
Member Author

@nanosoldier runtests(configuration = (buildflags=["LLVM_ASSERTIONS=1", "FORCE_ASSERTIONS=1"],), vs_configuration = (buildflags = ["LLVM_ASSERTIONS=1", "FORCE_ASSERTIONS=1"],))

@nanosoldier
Copy link
Collaborator

Your package evaluation job has completed - possible new issues were detected.
A full report can be found here.

@gbaraldi
Copy link
Member Author

So digging into the the failure, it is reproducible.

julia> CHUNK_TYPE = Dict(1=>"FileHeader")
Dict{Int64, String} with 1 entry:
  1 => "FileHeader"

julia> foo(v) = [length(x) for x in values(v)]
foo (generic function with 1 method)

julia> foo(CHUNK_TYPE)

is enough to reproduce.

Interestingly, while debugging with Cthulhu, I trigger

Toggles: [w]arn, [h]ide type-stable statements, [t]ype annotations, [s]yntax highlight for Source/LLVM/Native.
Show: [S]ource code, [A]ST, [T]yped code, [L]LVM IR, [N]ative code
Actions: [E]dit source code, [R]evise and redisplay
   g::Base.Generator{Base.ValueIterator{Dict{Int64, String}}, var"#1#2"}.iter
 • iterate(g::Base.Generator{Base.ValueIterator{Dict{Int64, String}}, var"#1#2"}.iter::Base.ValueIterator{Dict{Int64, String}}, s::Tuple{}...)
   g::Base.Generator{Base.ValueIterator{Dict{Int64, String}}, var"#1#2"}.f
   y::Union{Nothing, Tuple{String, Int64}}[1]
   g::Base.Generator{Base.ValueIterator{Dict{Int64, String}}, var"#1#2"}.f::var"#1#2"(y::Union{Nothing, Tuple{String, Int64}}[1]::String)
   y::Union{Nothing, Tuple{String, Int64}}[2]
   ↩
Assertion failed: (ndt->types == NULL), function jl_reinstantiate_inner_types, file jltypes.c, line 2506.

[24213] signal (6): Abort trap: 6
in expression starting at REPL[5]:1
__pthread_kill at /usr/lib/system/libsystem_kernel.dylib (unknown line)
Allocations: 3765944 (Pool: 3760493; Big: 5451); GC: 6
fish: Job 1, './julia' terminated by signal SIGABRT (Abort)

@gbaraldi
Copy link
Member Author

So that might have been a red herring but doing

 @code_llvm raw=true dump_module=true foo(CHUNK_TYPE)

shows that we codegen

    %32 = call swiftcc i64 @j_length_234({}*** nonnull swiftself %0, {} addrspace(10)* null), !dbg !134

which doesn't seem very correct.

src/codegen.cpp Outdated Show resolved Hide resolved
@gbaraldi
Copy link
Member Author

@nanosoldier runtests(configuration = (buildflags=["LLVM_ASSERTIONS=1", "FORCE_ASSERTIONS=1"],), vs_configuration = (buildflags = ["LLVM_ASSERTIONS=1", "FORCE_ASSERTIONS=1"],))

@gbaraldi
Copy link
Member Author

@nanosoldier runbenchmarks(ALL, vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

@nanosoldier
Copy link
Collaborator

Your package evaluation job has completed - possible new issues were detected.
A full report can be found here.

@brenhinkeller brenhinkeller added the compiler:llvm For issues that relate to LLVM label Aug 6, 2023
src/codegen.cpp Outdated
@@ -4327,7 +4379,12 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos
setName(ctx.emission_context, TheCallee, namep);
}
CallInst *call = ctx.builder.CreateCall(cft, TheCallee, argvals);
call->setAttributes(returninfo.attrs);
if (auto fun = dyn_cast<Function>(TheCallee)){
fun->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_attrs_ipoeffects(ctx.builder.getContext(), effects, true, has_ptr_arg), returninfo.attrs, attrs}));
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

It is generally pretty sketchy to mutate the callee to assume information that was only valid for this particular caller call site. I would delete this change.

Copy link
Member Author

Choose a reason for hiding this comment

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

The slightly annoying thing is that speculatable can't be passed to call sites, only on declarations. Also aren't the ipo effects the ones that are valid on call sites of the specific MI?

Comment on lines +7127 to +7162
param.addDereferenceableAttr(props.union_bytes);
param.addAlignmentAttr(props.union_align);
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Do we actually ensure this when making this sret alloca? Normally this is the upper bounds that are permitted to be assumed, not the actual values at runtime, but this might be a special case.

Copy link
Member Author

Choose a reason for hiding this comment

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

This what we do when we build the function in

param.addDereferenceableAttr(returninfo.union_bytes);

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Ah, fair. That is the only place it matters too. Elsewhere it just adds a little bit of extra overhead to the function declaration to track some extraneous attributes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Doesn't it potenially give more information with how much memory an sret a function has ,i t's likely that the information is there on the pointer now but with opaque pointers it might not be?

src/codegen.cpp Outdated
if (is_decl && consistent == 0 && effect_free == 0 && nothrow == 1 && terminates == 1 && has_ptr_arg == false){
attr.addAttribute(Attribute::Speculatable); // This might not be legal because it assumes pointers can be null
}
attr.addAttribute(std::to_string(effects));
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

This seems like it should be ifndef NDEBUG?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe we can add it as a comment to the module instead.

} else if (SRet_gc == Alloca) {
continue;
//check if isSret is passed directly
if (!(&F.arg_begin()[0] == CI->arg_begin()[0].get())) {
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Even if the sret gets copied directly from somewhere else, it still needs to determine where the associated GC roots are, and make sure they are rooted.

Copy link
Member Author

Choose a reason for hiding this comment

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

So I added this as an attempt to figure an error out. What was happening here was that LLVM eliding everything about the sret and then complaining that it couldn't find it.

@vtjnash vtjnash marked this pull request as draft August 8, 2023 00:45
} else if (inaccessiblememonly == 2){
attr.addAttribute(Attribute::InaccessibleMemOrArgMemOnly);
}
if (notaskstate == 1) {
Copy link
Sponsor Member

@vchuravy vchuravy Sep 13, 2023

Choose a reason for hiding this comment

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

Is that read only task state or task state at all?

I.e. should we verify this?

Copy link
Member Author

Choose a reason for hiding this comment

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

So, from the docs

## `:notaskstate`

The `:notaskstate` setting asserts that the method does not use or modify the
local task state (task local storage, RNG state, etc.) and may thus be safely
moved between tasks without observable results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:llvm For issues that relate to LLVM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants