Revision a121721f975fc4105ed24ebd0ad1020d08d07a38 authored by Shuhei Kadowaki on 01 November 2021, 10:49:07 UTC, committed by GitHub on 01 November 2021, 10:49:07 UTC
* inference: form `PartialStruct` for extra type information propagation This commit forms `PartialStruct` whenever there is any type-level refinement available about a field, even if it's not "constant" information. In Julia "definitions" are allowed to be abstract whereas "usages" (i.e. callsites) are often concrete. The basic idea is to allow inference to make more use of such precise callsite type information by encoding it as `PartialStruct`. This may increase optimization possibilities of "unidiomatic" Julia code, which may contain poorly-typed definitions, like this very contrived example: ```julia struct Problem n; s; c; t end function main(args...) prob = Problem(args...) s = 0 for i in 1:prob.n m = mod(i, 3) s += m == 0 ? sin(prob.s) : m == 1 ? cos(prob.c) : tan(prob.t) end return prob, s end main(10000, 1, 2, 3) ``` One of the obvious limitation is that this extra type information can be propagated inter-procedurally only as a const-propagation. I'm not sure this kind of "just a type-level" refinement can often make constant-prop' successful (i.e. shape-up a method body and allow it to be inlined, encoding the extra type information into the generated code), thus I didn't not modify any part of const-prop' heuristics. So the improvements from this change might not be very useful for general inter-procedural analysis currently, but they should definitely improve the accuracy of local analysis and very simple inter-procedural analysis.
1 parent 6c274ed
version.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license
## semantic version numbers (https://semver.org/)
const VerTuple = Tuple{Vararg{Union{UInt64,String}}}
const VInt = UInt32
"""
VersionNumber
Version number type which follow the specifications of
[semantic versioning](https://semver.org/), composed of major, minor
and patch numeric values, followed by pre-release and build
alpha-numeric annotations. See also [`@v_str`](@ref).
# Examples
```jldoctest
julia> VersionNumber("1.2.3")
v"1.2.3"
julia> VersionNumber("2.0.1-rc1")
v"2.0.1-rc1"
```
"""
struct VersionNumber
major::VInt
minor::VInt
patch::VInt
prerelease::VerTuple
build::VerTuple
function VersionNumber(major::VInt, minor::VInt, patch::VInt,
pre::VerTuple,
bld::VerTuple)
major >= 0 || throw(ArgumentError("invalid negative major version: $major"))
minor >= 0 || throw(ArgumentError("invalid negative minor version: $minor"))
patch >= 0 || throw(ArgumentError("invalid negative patch version: $patch"))
for ident in pre
if ident isa Integer
ident >= 0 || throw(ArgumentError("invalid negative pre-release identifier: $ident"))
else
if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) ||
isempty(ident) && !(length(pre)==1 && isempty(bld))
throw(ArgumentError("invalid pre-release identifier: $(repr(ident))"))
end
end
end
for ident in bld
if ident isa Integer
ident >= 0 || throw(ArgumentError("invalid negative build identifier: $ident"))
else
if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) ||
isempty(ident) && length(bld)!=1
throw(ArgumentError("invalid build identifier: $(repr(ident))"))
end
end
end
new(major, minor, patch, pre, bld)
end
end
VersionNumber(major::Integer, minor::Integer = 0, patch::Integer = 0,
pre::Tuple{Vararg{Union{Integer,AbstractString}}} = (),
bld::Tuple{Vararg{Union{Integer,AbstractString}}} = ()) =
VersionNumber(VInt(major), VInt(minor), VInt(patch),
map(x->x isa Integer ? UInt64(x) : String(x), pre),
map(x->x isa Integer ? UInt64(x) : String(x), bld))
VersionNumber(v::Tuple) = VersionNumber(v...)
function print(io::IO, v::VersionNumber)
v == typemax(VersionNumber) && return print(io, "∞")
print(io, v.major)
print(io, '.')
print(io, v.minor)
print(io, '.')
print(io, v.patch)
if !isempty(v.prerelease)
print(io, '-')
join(io, v.prerelease,'.')
end
if !isempty(v.build)
print(io, '+')
join(io, v.build,'.')
end
end
show(io::IO, v::VersionNumber) = print(io, "v\"", v, "\"")
Broadcast.broadcastable(v::VersionNumber) = Ref(v)
const VERSION_REGEX = r"^
v? # prefix (optional)
(\d+) # major (required)
(?:\.(\d+))? # minor (optional)
(?:\.(\d+))? # patch (optional)
(?:(-)| # pre-release (optional)
([a-z][0-9a-z-]*(?:\.[0-9a-z-]+)*|-(?:[0-9a-z-]+\.)*[0-9a-z-]+)?
(?:(\+)|
(?:\+((?:[0-9a-z-]+\.)*[0-9a-z-]+))? # build (optional)
))
$"ix
function split_idents(s::AbstractString)
idents = eachsplit(s, '.')
pidents = Union{UInt64,String}[occursin(r"^\d+$", ident) ? parse(UInt64, ident) : String(ident) for ident in idents]
return tuple(pidents...)::VerTuple
end
function tryparse(::Type{VersionNumber}, v::AbstractString)
v == "∞" && return typemax(VersionNumber)
m = match(VERSION_REGEX, String(v)::String)
m === nothing && return nothing
major, minor, patch, minus, prerl, plus, build = m.captures
major = parse(VInt, major::AbstractString)
minor = minor !== nothing ? parse(VInt, minor) : VInt(0)
patch = patch !== nothing ? parse(VInt, patch) : VInt(0)
if prerl !== nothing && !isempty(prerl) && prerl[1] == '-'
prerl = prerl[2:end] # strip leading '-'
end
prerl = prerl !== nothing ? split_idents(prerl) : minus !== nothing ? ("",) : ()
build = build !== nothing ? split_idents(build) : plus !== nothing ? ("",) : ()
return VersionNumber(major, minor, patch, prerl::VerTuple, build::VerTuple)
end
function parse(::Type{VersionNumber}, v::AbstractString)
ver = tryparse(VersionNumber, v)
ver === nothing && throw(ArgumentError("invalid version string: $v"))
return ver
end
VersionNumber(v::AbstractString) = parse(VersionNumber, v)
"""
@v_str
String macro used to parse a string to a [`VersionNumber`](@ref).
# Examples
```jldoctest
julia> v"1.2.3"
v"1.2.3"
julia> v"2.0.1-rc1"
v"2.0.1-rc1"
```
"""
macro v_str(v); VersionNumber(v); end
function typemax(::Type{VersionNumber})
∞ = typemax(VInt)
VersionNumber(∞, ∞, ∞, (), ("",))
end
typemin(::Type{VersionNumber}) = v"0-"
ident_cmp(a::Integer, b::Integer) = cmp(a, b)
ident_cmp(a::Integer, b::String ) = isempty(b) ? +1 : -1
ident_cmp(a::String, b::Integer) = isempty(a) ? -1 : +1
ident_cmp(a::String, b::String ) = cmp(a, b)
function ident_cmp(A::VerTuple, B::VerTuple)
for (a, b) in Iterators.Zip{Tuple{VerTuple,VerTuple}}((A, B))
c = ident_cmp(a, b)
(c != 0) && return c
end
length(A) < length(B) ? -1 :
length(B) < length(A) ? +1 : 0
end
function ==(a::VersionNumber, b::VersionNumber)
(a.major != b.major) && return false
(a.minor != b.minor) && return false
(a.patch != b.patch) && return false
(ident_cmp(a.prerelease, b.prerelease) != 0) && return false
(ident_cmp(a.build, b.build) != 0) && return false
return true
end
issupbuild(v::VersionNumber) = length(v.build)==1 && isempty(v.build[1])
function isless(a::VersionNumber, b::VersionNumber)
(a.major < b.major) && return true
(a.major > b.major) && return false
(a.minor < b.minor) && return true
(a.minor > b.minor) && return false
(a.patch < b.patch) && return true
(a.patch > b.patch) && return false
(!isempty(a.prerelease) && isempty(b.prerelease)) && return true
(isempty(a.prerelease) && !isempty(b.prerelease)) && return false
c = ident_cmp(a.prerelease,b.prerelease)
(c < 0) && return true
(c > 0) && return false
(!issupbuild(a) && issupbuild(b)) && return true
(issupbuild(a) && !issupbuild(b)) && return false
c = ident_cmp(a.build,b.build)
(c < 0) && return true
return false
end
function hash(v::VersionNumber, h::UInt)
h += 0x8ff4ffdb75f9fede % UInt
h = hash(v.major, h)
h = hash(v.minor, h)
h = hash(v.patch, h)
h = hash(v.prerelease, ~h)
h = hash(v.build, ~h)
end
lowerbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, ("",), ())
upperbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, (), ("",))
thispatch(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch)
thisminor(v::VersionNumber) = VersionNumber(v.major, v.minor, 0)
thismajor(v::VersionNumber) = VersionNumber(v.major, 0, 0)
nextpatch(v::VersionNumber) = v < thispatch(v) ? thispatch(v) : VersionNumber(v.major, v.minor, v.patch+1)
nextminor(v::VersionNumber) = v < thisminor(v) ? thisminor(v) : VersionNumber(v.major, v.minor+1, 0)
nextmajor(v::VersionNumber) = v < thismajor(v) ? thismajor(v) : VersionNumber(v.major+1, 0, 0)
## julia version info
"""
VERSION
A `VersionNumber` object describing which version of Julia is in use. For details see
[Version Number Literals](@ref man-version-number-literals).
"""
const VERSION = try
ver = VersionNumber(VERSION_STRING)
if !isempty(ver.prerelease) && !GIT_VERSION_INFO.tagged_commit
if GIT_VERSION_INFO.build_number >= 0
ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., GIT_VERSION_INFO.build_number), ver.build)
else
println("WARNING: no build number found for pre-release version")
ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., "unknown"), ver.build)
end
elseif GIT_VERSION_INFO.build_number > 0
println("WARNING: ignoring non-zero build number for VERSION")
end
ver
catch e
println("while creating Base.VERSION, ignoring error $e")
VersionNumber(0)
end
const libllvm_version = if endswith(libllvm_version_string, "jl")
# strip the "jl" SONAME suffix (JuliaLang/julia#33058)
# (LLVM does never report a prerelease version anyway)
VersionNumber(libllvm_version_string[1:end-2])
else
VersionNumber(libllvm_version_string)
end
libllvm_path() = ccall(:jl_get_libllvm, Any, ())
function banner(io::IO = stdout)
if GIT_VERSION_INFO.tagged_commit
commit_string = TAGGED_RELEASE_BANNER
elseif isempty(GIT_VERSION_INFO.commit)
commit_string = ""
else
days = Int(floor((ccall(:jl_clock_now, Float64, ()) - GIT_VERSION_INFO.fork_master_timestamp) / (60 * 60 * 24)))
days = max(0, days)
unit = days == 1 ? "day" : "days"
distance = GIT_VERSION_INFO.fork_master_distance
commit = GIT_VERSION_INFO.commit_short
if distance == 0
commit_string = "Commit $(commit) ($(days) $(unit) old master)"
else
branch = GIT_VERSION_INFO.branch
commit_string = "$(branch)/$(commit) (fork: $(distance) commits, $(days) $(unit))"
end
end
commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : " ($(split(Base.GIT_VERSION_INFO.date_string)[1]))"
if get(io, :color, false)
c = text_colors
tx = c[:normal] # text
jl = c[:normal] # julia
d1 = c[:bold] * c[:blue] # first dot
d2 = c[:bold] * c[:red] # second dot
d3 = c[:bold] * c[:green] # third dot
d4 = c[:bold] * c[:magenta] # fourth dot
print(io,""" $(d3)_$(tx)
$(d1)_$(tx) $(jl)_$(tx) $(d2)_$(d3)(_)$(d4)_$(tx) | Documentation: https://docs.julialang.org
$(d1)(_)$(jl) | $(d2)(_)$(tx) $(d4)(_)$(tx) |
$(jl)_ _ _| |_ __ _$(tx) | Type \"?\" for help, \"]?\" for Pkg help.
$(jl)| | | | | | |/ _` |$(tx) |
$(jl)| | |_| | | | (_| |$(tx) | Version $(VERSION)$(commit_date)
$(jl)_/ |\\__'_|_|_|\\__'_|$(tx) | $(commit_string)
$(jl)|__/$(tx) |
""")
else
print(io,"""
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type \"?\" for help, \"]?\" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version $(VERSION)$(commit_date)
_/ |\\__'_|_|_|\\__'_| | $(commit_string)
|__/ |
""")
end
end
Computing file changes ...