Skip to content

Commit 0860767

Browse files
committedJan 17, 2014
remove special-case lowering for for i = a:b loops (ref #5355)
this fixes some edge-case loops that the special lowering did not handle correctly. colon() now checks for overflow in computing the length, which avoids some buggy Range1s that used to be possible. this required some changes to make sure Range1 is fast enough: specialized start, done, next, and a hack to avoid one of the checks and allow better inlining. in general performance is about the same, but a few cases are actually faster, since Range1 is now faster (comprehensions used Range1 instead of the special-case lowering, for example). also, more loops should be vectorizable when the appropriate LLVM passes are enabled. all that plus better correctness and a simpler front-end, and I'm sold.
1 parent efde013 commit 0860767

File tree

3 files changed

+91
-43
lines changed

3 files changed

+91
-43
lines changed
 

Diff for: ‎base/range.jl

+28-3
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,38 @@ immutable Range1{T<:Real} <: Ranges{T}
2424
len::Int
2525

2626
function Range1(start::T, len::Int)
27-
if !(len >= 0); error("length must be non-negative"); end
27+
if len < 0; error("length must be non-negative"); end
2828
new(start, len)
2929
end
3030
Range1(start::T, len::Integer) = Range1(start, int(len))
31+
32+
# TODO: this is a hack to elide the len<0 check for colon.
33+
# should store start and stop for integer ranges instead
34+
Range1(start::T, len::Integer, _) = new(start, int(len))
3135
end
3236
Range1{T}(start::T, len::Integer) = Range1{T}(start, len)
3337

3438
function colon{T<:Integer}(start::T, step::T, stop::T)
3539
step != 0 || error("step cannot be zero in colon syntax")
36-
Range(start, step, max(0, 1 + fld(stop-start, step)))
40+
Range{T}(start, step, max(0, 1 + fld(stop-start, step)))
3741
end
42+
3843
colon{T<:Integer}(start::T, stop::T) =
39-
Range1(start, max(0, stop-start+1))
44+
Range1{T}(start, ifelse(stop<start, 0, int(stop-start+1)))
45+
46+
if Int === Int32
47+
colon{T<:Union(Int8,Int16,Int32,Uint8,Uint16)}(start::T, stop::T) =
48+
Range1{T}(start,
49+
ifelse(stop<start, 0,
50+
checked_add(checked_sub(convert(Int,stop),convert(Int,start)),1)),
51+
0) # hack to elide negative length check
52+
else
53+
colon{T<:Union(Int8,Int16,Int32,Int64,Uint8,Uint16,Uint32)}(start::T, stop::T) =
54+
Range1{T}(start,
55+
ifelse(stop<start, 0,
56+
checked_add(checked_sub(convert(Int,stop),convert(Int,start)),1)),
57+
0) # hack to elide negative length check
58+
end
4059

4160
function colon{T<:Real}(start::T, step::T, stop::T)
4261
step != 0 || error("step cannot be zero in colon syntax")
@@ -151,6 +170,12 @@ next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1)
151170
next{T}(r::Range1{T}, i) = (oftype(T, r.start + i), i+1)
152171
done(r::Ranges, i) = (length(r) <= i)
153172

173+
# though these look very similar to the above, for some reason LLVM generates
174+
# much better code for these.
175+
start{T<:Integer}(r::Range1{T}) = r.start
176+
next{T<:Integer}(r::Range1{T}, i) = (i, oftype(T, i+1))
177+
done{T<:Integer}(r::Range1{T}, i) = i==(r.start+r.len)
178+
154179
==(r::Ranges, s::Ranges) = (r.start==s.start) & (step(r)==step(s)) & (r.len==s.len)
155180
==(r::Range1, s::Range1) = (r.start==s.start) & (r.len==s.len)
156181

Diff for: ‎src/julia-syntax.scm

+16-40
Original file line numberDiff line numberDiff line change
@@ -1722,46 +1722,22 @@
17221722
(let ((X (caddr (cadr e)))
17231723
(lhs (cadr (cadr e)))
17241724
(body (caddr e)))
1725-
(if
1726-
(and (pair? X) (eq? (car X) ':) (length= X 3))
1727-
;; (for (= lhs (: a b)) body)
1728-
(let* ((cnt (gensy))
1729-
(a (cadr X))
1730-
(b (caddr X))
1731-
(lim (if (number? b) b (gensy))))
1732-
`(scope-block
1733-
(block
1734-
(= ,cnt ,(expand-forms a))
1735-
,.(if (eq? lim b) '() `((= ,lim ,(expand-forms b))))
1736-
(break-block loop-exit
1737-
(_while (call (top <=) ,cnt ,lim)
1738-
(scope-block
1739-
(block
1740-
;; NOTE: enable this to force loop-local var
1741-
#;(local ,lhs)
1742-
(= ,lhs ,cnt)
1743-
(break-block loop-cont
1744-
,(expand-forms body))
1745-
(= ,cnt (call (top convert)
1746-
(call (top typeof) ,cnt)
1747-
(call (top +) 1 ,cnt))))))))))
1748-
1749-
;; (for (= lhs X) body)
1750-
(let ((coll (gensy))
1751-
(state (gensy)))
1752-
`(scope-block
1753-
(block (= ,coll ,(expand-forms X))
1754-
(= ,state (call (top start) ,coll))
1755-
,(expand-forms
1756-
`(while
1757-
(call (top !) (call (top done) ,coll ,state))
1758-
(scope-block
1759-
(block
1760-
;; NOTE: enable this to force loop-local var
1761-
#;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs))
1762-
,(lower-tuple-assignment (list lhs state)
1763-
`(call (top next) ,coll ,state))
1764-
,body))))))))))
1725+
;; (for (= lhs X) body)
1726+
(let ((coll (gensy))
1727+
(state (gensy)))
1728+
`(scope-block
1729+
(block (= ,coll ,(expand-forms X))
1730+
(= ,state (call (top start) ,coll))
1731+
,(expand-forms
1732+
`(while
1733+
(call (top !) (call (top done) ,coll ,state))
1734+
(scope-block
1735+
(block
1736+
;; NOTE: enable this to force loop-local var
1737+
#;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs))
1738+
,(lower-tuple-assignment (list lhs state)
1739+
`(call (top next) ,coll ,state))
1740+
,body)))))))))
17651741

17661742
'+= lower-update-op
17671743
'-= lower-update-op

Diff for: ‎test/ranges.jl

+47
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,50 @@ r = (-4*int64(maxintfloat(is(Int,Int32) ? Float32 : Float64))):5
103103

104104
# avoiding intermediate overflow (#5065)
105105
@test length(1:4:typemax(Int)) == div(typemax(Int),4) + 1
106+
107+
# overflow in length
108+
@test_throws 0:typemax(Int)
109+
@test_throws typemin(Int):typemax(Int)
110+
@test_throws -1:typemax(Int)-1
111+
112+
# parity between ranges and for loops (see issue #5355)
113+
114+
@test length(2.0^53:(2.0^53+2)) == 3
115+
let s = 0
116+
r = 2.0^53:(2.0^53+2)
117+
for i in r
118+
s += 1
119+
@test s <= 3
120+
end
121+
@test s == 3
122+
123+
s = 0
124+
for i in 2.0^53:(2.0^53+2)
125+
s += 1
126+
@test s <= 3
127+
end
128+
@test s == 3
129+
end
130+
131+
let s = 0
132+
# loops ending at typemax(Int)
133+
for i = (typemax(Int)-1):typemax(Int)
134+
s += 1
135+
@test s <= 2
136+
end
137+
@test s == 2
138+
139+
s = 0
140+
for i = (typemax(Int)-2):(typemax(Int)-1)
141+
s += 1
142+
@test s <= 2
143+
end
144+
@test s == 2
145+
146+
s = 0
147+
for i = typemin(Int):(typemin(Int)+1)
148+
s += 1
149+
@test s <= 2
150+
end
151+
@test s == 2
152+
end

0 commit comments

Comments
 (0)