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
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
85 changes: 80 additions & 5 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1807,6 +1807,53 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const
static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p);
static unsigned julia_alignment(jl_value_t *jt);

static AttributeList get_attrs_ipoeffects(jl_codectx_t &ctx, std::pair<bool,uint32_t> effect, bool is_decl, bool has_ptr_arg)
{
auto &context = ctx.builder.getContext();
if (!effect.first)
return AttributeList();
uint32_t effects = effect.second;
uint8_t consistent = (effects >> 0) & 0x07;
uint8_t effect_free = (effects >> 3) & 0x03;
bool nothrow = (effects >> 5) & 0x01;
bool terminates = (effects >> 6) & 0x01;
bool notaskstate = (effects >> 7) & 0x01;
uint8_t inaccessiblememonly = (effects >> 8) & 0x03;
bool nonoverlayed = (effects >> 10) & 0x01;
bool noinbounds = (effects >> 11) & 0x01;
AttrBuilder attr(context);

if (consistent == 0) {
}
if (effect_free == 0) {
// if (!has_ptr_arg)
// attr.addAttribute(Attribute::ReadOnly); //Not legal because of the ptls
}
if (nothrow == 1) {
attr.addAttribute(Attribute::NoUnwind);
}
if (terminates == 1){
attr.addAttribute(Attribute::WillReturn);
}
if (inaccessiblememonly == 0) {
attr.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); // Can't be inaccesiblememonly because of the ptls
} 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.

}
if (nonoverlayed == 1) {
}
if (noinbounds == 1) {
}
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
}
if (ctx.emission_context.debug_level >= 2)
attr.addAttribute(std::to_string(effects));
return AttributeList::get(context,AttributeSet::get(context, attr), AttributeSet(), None);
}

static GlobalVariable *prepare_global_in(Module *M, JuliaVariable *G)
{
return G->realize(M);
Expand Down Expand Up @@ -4246,7 +4293,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value
}

static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, llvm::Value *callee, StringRef specFunctionObject, jl_code_instance_t *fromexternal,
const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty)
const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty, std::pair<bool,uint32_t> effects = {false,0})
{
++EmittedSpecfunCalls;
// emit specialized call site
Expand Down Expand Up @@ -4290,6 +4337,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos
argvals[idx] = ctx.pgcstack;
idx++;
}
bool has_ptr_arg = false;
for (size_t i = 0; i < nargs; i++) {
jl_value_t *jt = jl_nth_slot_type(specTypes, i);
// n.b.: specTypes is required to be a datatype by construction for specsig
Expand Down Expand Up @@ -4333,10 +4381,16 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos
argvals[idx] = val;
}
}
if(argvals[idx]->getType()->isPointerTy())
has_ptr_arg = true;
idx++;
}
assert(idx == nfargs);
Value *TheCallee = returninfo.decl.getCallee();
auto attrs = AttributeList();
if (auto fun = dyn_cast<Function>(TheCallee)){
attrs = fun->getAttributes();
}
if (fromexternal) {
std::string namep("p");
namep += cast<Function>(returninfo.decl.getCallee())->getName();
Expand All @@ -4353,7 +4407,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, effects, true, has_ptr_arg), returninfo.attrs, attrs}));
call->setAttributes(returninfo.attrs);
}
else
call->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_attrs_ipoeffects(ctx, effects, false, has_ptr_arg), returninfo.attrs}));
if (gcstack_arg)
call->setCallingConv(CallingConv::Swift);

Expand Down Expand Up @@ -4395,11 +4454,11 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos
}

static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal,
const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty)
const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty, std::pair<bool,uint32_t> effects = {false,0})
{
bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure;
return emit_call_specfun_other(ctx, is_opaque_closure, mi->specTypes, jlretty, NULL,
specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty);
specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty, effects);
}

static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal,
Expand Down Expand Up @@ -4530,7 +4589,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const
jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed;
unsigned return_roots = 0;
if (specsig)
result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt);
result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt, {true,codeinst->ipo_purity_bits});
else
result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt);
handled = true;
Expand Down Expand Up @@ -7075,15 +7134,21 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
else {
rt = ctx.types().T_prjlvalue;
}
const DataLayout &DL = M->getDataLayout();

SmallVector<AttributeSet, 8> attrs; // function declaration attributes
if (props.cc == jl_returninfo_t::SRet) {
assert(srt);
TypeSize sz = DL.getTypeAllocSize(srt);
Align al = DL.getPrefTypeAlign(srt);
AttrBuilder param(ctx.builder.getContext());
param.addStructRetAttr(srt);
param.addAttribute(Attribute::NonNull);
param.addAttribute(Attribute::NoAlias);
param.addAttribute(Attribute::NoCapture);
param.addAttribute(Attribute::NoUndef);
param.addDereferenceableAttr(sz);
param.addAlignmentAttr(al);
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
assert(fsig.size() == 1);
}
Expand All @@ -7092,6 +7157,9 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
param.addAttribute(Attribute::NoAlias);
param.addAttribute(Attribute::NoCapture);
param.addAttribute(Attribute::NoUndef);
param.addAttribute(Attribute::NonNull);
param.addDereferenceableAttr(props.union_bytes);
param.addAlignmentAttr(props.union_align);
Comment on lines +7161 to +7162
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?

attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
assert(fsig.size() == 1);
}
Expand All @@ -7101,6 +7169,10 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
param.addAttribute(Attribute::NoAlias);
param.addAttribute(Attribute::NoCapture);
param.addAttribute(Attribute::NoUndef);
param.addAttribute(Attribute::NonNull);
size_t size = props.return_roots * sizeof(jl_value_t*);
param.addDereferenceableAttr(size);
param.addAlignmentAttr(Align(sizeof(jl_value_t*)));
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
fsig.push_back(get_returnroots_type(ctx, props.return_roots)->getPointerTo(0));
argnames.push_back("return_roots");
Expand Down Expand Up @@ -7141,6 +7213,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
if (ty->isAggregateType()) { // aggregate types are passed by pointer
param.addAttribute(Attribute::NoCapture);
param.addAttribute(Attribute::ReadOnly);
maybe_mark_argument_dereferenceable(param, jt);
ty = PointerType::get(ty, AddressSpace::Derived);
}
else if (isboxed && jl_is_immutable_datatype(jt)) {
Expand All @@ -7151,6 +7224,8 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value
Attribute::AttrKind attr = issigned ? Attribute::SExt : Attribute::ZExt;
param.addAttribute(attr);
}
if (isboxed)
maybe_mark_argument_dereferenceable(param, jt);
attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param));
fsig.push_back(ty);
if (used_arguments)
Expand Down