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

Fix nothrow behavior for intrinsics #34539

Merged
merged 3 commits into from
Feb 28, 2020
Merged

Fix nothrow behavior for intrinsics #34539

merged 3 commits into from
Feb 28, 2020

Conversation

Keno
Copy link
Member

@Keno Keno commented Jan 27, 2020

This fixes the case in #34482 and adds some more robustness for similar
cases, though I wouldn't be surprised if there were more dragons hiding
here. Intrinsics are a bit special and from the start, we sort of expected
them to only ever be called correctly under pain of segfaults or other
undefined behavior. We've been gradually making these more robust,
but fundamentally, they were never intended to be used by users directly,
only through the type-validating wrappers in Base. What has changed
to cause the recent influx of issues in this area is that people now
like to do compiler transforms that happily recurse through these wrappers
and perform transforms that are not always legal on Intrinsics. This should
help catch a number of them, but this interface is still not very thoroughly
validated, and I would be surprised to see crashes or other errors stemming
from incorrect usage here.

base/compiler/tfuncs.jl Outdated Show resolved Hide resolved
src/intrinsics.cpp Outdated Show resolved Hide resolved
base/compiler/tfuncs.jl Outdated Show resolved Hide resolved
if f === Intrinsics.checked_udiv_int || f === Intrinsics.checked_urem_int || f === Intrinsics.checked_srem_int || f === Intrinsics.checked_sdiv_int
# Nothrow as long as the second argument is guaranteed not to be zero
isa(argtypes[2], Const) || return false
den_val = argtypes[2].val
den_val !== zero(typeof(den_val)) || return false
return true
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Feel like these should have isprimitivetype tests added too? (and checked_sdiv_int below)

@@ -1169,8 +1169,7 @@ function early_inline_special_case(ir::IRCode, s::Signature, e::Expr, params::Pa
val = etype.val
is_inlineable_constant(val) || return nothing
if isa(f, IntrinsicFunction)
if is_pure_intrinsic_infer(f) &&
(intrinsic_nothrow(f) || intrinsic_nothrow(f, atypes[2:end]))
if is_pure_intrinsic_infer(f) && intrinsic_nothrow(f, atypes[2:end])
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 need to handle Vararg early in this function and bail?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, vararg is handled earlier in inlining and it refuses to construct a Signature object for such cases.

if f in (Intrinsics.sext_int, Intrinsics.zext_int, Intrinsics.trunc_int,
Intrinsics.fptoui, Intrinsics.fptosi, Intrinsics.uitofp,
Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext)
ty = instanceof_tfunc(argtypes[1])[1]
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

IIUC, if the second return value is false, then ty may include the value Union{} (e.g. the call Intrinsics.sext_int(Union{}, 1))

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext)
ty = instanceof_tfunc(argtypes[1])[1]
xty = widenconst(argtypes[2])
return isprimitivetype(ty) && isprimitivetype(xty)
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Separately, we may need to also review what happens if the relation between ty.size ≏ xty.size is not what LLVM expects for these

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but I'll leave that for future work.

base/compiler/tfuncs.jl Outdated Show resolved Hide resolved
return isprimitivetype(ty) && isprimitivetype(xty)
end
# The remaining intrinsics are math/bits intrinsics. They work on all
# primitive types.
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

"...all primitive types of the same type" ?

Copy link
Member Author

Choose a reason for hiding this comment

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

In theory yes, but I don't think we check this in the runtime implementation.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

easy to check...

julia> Core.Intrinsics.add_int(Int64(1), UInt64(1))
ERROR: add_int: types of a and b must match
Stacktrace:
 [1] top-level scope at REPL[1]:1

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I misread some code.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Feb 19, 2020

Looking good! Just thought of a few more cases we may want to handle, while we're at it.

This fixes the case in #34482 and adds some more robustness for similar
cases, though I wouldn't be surprised if there were more dragons hiding
here. Intrinsics are a bit special and from the start, we sort of expected
them to only ever be called correctly under pain of segfaults or other
undefined behavior. We've been gradually making these more robust,
but fundamentally, they were never intended to be used by users directly,
only through the type-validating wrappers in Base. What has changed
to cause the recent influx of issues in this area is that people now
like to do compiler transforms that happily recurse through these wrappers
and perform transforms that are not always legal on Intrinsics. This should
help catch a number of them, but this interface is still not very thoroughly
validated, and I would be surprised to see crashes or other errors stemming
from incorrect usage here.
@Keno Keno closed this Feb 27, 2020
@Keno Keno reopened this Feb 27, 2020
@Keno
Copy link
Member Author

Keno commented Feb 28, 2020

Good to go?

@vtjnash
Copy link
Sponsor Member

vtjnash commented Feb 28, 2020

Sure. I was just realizing that the test we do is insufficient though:

julia> isprimitivetype(Ptr)
true
julia> Ptr === Ptr
true
julia> Ptr >: Ptr{UInt8} === Ptr{Int8} <: Ptr
false

@Keno
Copy link
Member Author

Keno commented Feb 28, 2020

Are there cases where our intrinsics throw when they see the Ptr UnionAll? Anyway, let's get this in and we can refine in future commits.

@Keno Keno merged commit 3f05b0d into master Feb 28, 2020
@Keno Keno deleted the kf/intrinsicnothrow branch February 28, 2020 21:03
ravibitsgoa pushed a commit to ravibitsgoa/julia that referenced this pull request Apr 9, 2020
This fixes the case in JuliaLang#34482 and adds some more robustness for similar
cases, though I wouldn't be surprised if there were more dragons hiding
here. Intrinsics are a bit special and from the start, we sort of expected
them to only ever be called correctly under pain of segfaults or other
undefined behavior. We've been gradually making these more robust,
but fundamentally, they were never intended to be used by users directly,
only through the type-validating wrappers in Base. What has changed
to cause the recent influx of issues in this area is that people now
like to do compiler transforms that happily recurse through these wrappers
and perform transforms that are not always legal on Intrinsics. This should
help catch a number of them, but this interface is still not very thoroughly
validated, and I would be surprised to see crashes or other errors stemming
from incorrect usage here.
KristofferC pushed a commit that referenced this pull request Apr 11, 2020
This fixes the case in #34482 and adds some more robustness for similar
cases, though I wouldn't be surprised if there were more dragons hiding
here. Intrinsics are a bit special and from the start, we sort of expected
them to only ever be called correctly under pain of segfaults or other
undefined behavior. We've been gradually making these more robust,
but fundamentally, they were never intended to be used by users directly,
only through the type-validating wrappers in Base. What has changed
to cause the recent influx of issues in this area is that people now
like to do compiler transforms that happily recurse through these wrappers
and perform transforms that are not always legal on Intrinsics. This should
help catch a number of them, but this interface is still not very thoroughly
validated, and I would be surprised to see crashes or other errors stemming
from incorrect usage here.
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.

None yet

2 participants