Skip to content

Commit 29756a3

Browse files
committed
auto merge of #12867 : alexcrichton/rust/issue-12860, r=thestinger
This switches a "tail call" to a manual loop to get around LLVM not optimizing to a tail call. Close #12860
2 parents 6c895d1 + 05c991c commit 29756a3

File tree

2 files changed

+99
-31
lines changed

2 files changed

+99
-31
lines changed

src/libcollections/hashmap.rs

+37-31
Original file line numberDiff line numberDiff line change
@@ -1069,41 +1069,49 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
10691069
/// so we have some sort of upper bound on the number of probes to do.
10701070
///
10711071
/// 'hash', 'k', and 'v' are the elements to robin hood into the hashtable.
1072-
fn robin_hood(&mut self, index: table::FullIndex, dib_param: uint,
1073-
hash: table::SafeHash, k: K, v: V) {
1074-
let (old_hash, old_key, old_val) = {
1075-
let (old_hash_ref, old_key_ref, old_val_ref) = self.table.read_all_mut(&index);
1076-
1077-
let old_hash = replace(old_hash_ref, hash);
1078-
let old_key = replace(old_key_ref, k);
1079-
let old_val = replace(old_val_ref, v);
1080-
1081-
(old_hash, old_key, old_val)
1082-
};
1083-
1084-
let mut probe = self.probe_next(index.raw_index());
1085-
1086-
for dib in range(dib_param + 1, self.table.size()) {
1087-
let full_index = match self.table.peek(probe) {
1088-
table::Empty(idx) => {
1089-
// Finally. A hole!
1090-
self.table.put(idx, old_hash, old_key, old_val);
1091-
return;
1092-
},
1093-
table::Full(idx) => idx
1072+
fn robin_hood(&mut self, mut index: table::FullIndex, mut dib_param: uint,
1073+
mut hash: table::SafeHash, mut k: K, mut v: V) {
1074+
'outer: loop {
1075+
let (old_hash, old_key, old_val) = {
1076+
let (old_hash_ref, old_key_ref, old_val_ref) =
1077+
self.table.read_all_mut(&index);
1078+
1079+
let old_hash = replace(old_hash_ref, hash);
1080+
let old_key = replace(old_key_ref, k);
1081+
let old_val = replace(old_val_ref, v);
1082+
1083+
(old_hash, old_key, old_val)
10941084
};
10951085

1096-
let probe_dib = self.bucket_distance(&full_index);
1086+
let mut probe = self.probe_next(index.raw_index());
1087+
1088+
for dib in range(dib_param + 1, self.table.size()) {
1089+
let full_index = match self.table.peek(probe) {
1090+
table::Empty(idx) => {
1091+
// Finally. A hole!
1092+
self.table.put(idx, old_hash, old_key, old_val);
1093+
return;
1094+
},
1095+
table::Full(idx) => idx
1096+
};
1097+
1098+
let probe_dib = self.bucket_distance(&full_index);
1099+
1100+
// Robin hood! Steal the spot.
1101+
if probe_dib < dib {
1102+
index = full_index;
1103+
dib_param = probe_dib;
1104+
hash = old_hash;
1105+
k = old_key;
1106+
v = old_val;
1107+
continue 'outer;
1108+
}
10971109

1098-
if probe_dib < dib {
1099-
// Robin hood! Steal the spot. This had better be tail call.
1100-
return self.robin_hood(full_index, probe_dib, old_hash, old_key, old_val);
1110+
probe = self.probe_next(probe);
11011111
}
11021112

1103-
probe = self.probe_next(probe);
1113+
fail!("HashMap fatal error: 100% load factor?");
11041114
}
1105-
1106-
fail!("HashMap fatal error: 100% load factor?");
11071115
}
11081116

11091117
/// Manually insert a pre-hashed key-value pair, without first checking
@@ -1948,7 +1956,6 @@ mod test_map {
19481956

19491957
#[cfg(test)]
19501958
mod test_set {
1951-
use super::HashMap;
19521959
use super::HashSet;
19531960
use std::container::Container;
19541961
use std::vec::ImmutableEqVector;
@@ -2193,7 +2200,6 @@ mod test_set {
21932200
mod bench {
21942201
extern crate test;
21952202
use self::test::BenchHarness;
2196-
use std::iter;
21972203
use std::iter::{range_inclusive};
21982204

21992205
#[bench]

src/test/run-pass/issue-12860.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-fast
12+
13+
extern crate collections;
14+
15+
use collections::HashSet;
16+
17+
#[deriving(Eq, Hash)]
18+
struct XYZ {
19+
x: int,
20+
y: int,
21+
z: int
22+
}
23+
24+
fn main() {
25+
let mut connected = HashSet::new();
26+
let mut border = HashSet::new();
27+
28+
let middle = XYZ{x: 0, y: 0, z: 0};
29+
border.insert(middle);
30+
31+
while border.len() > 0 && connected.len() < 10000 {
32+
let choice = *(border.iter().next().unwrap());
33+
border.remove(&choice);
34+
connected.insert(choice);
35+
36+
let cxp = XYZ{x: choice.x + 1, y: choice.y, z: choice.z};
37+
let cxm = XYZ{x: choice.x - 1, y: choice.y, z: choice.z};
38+
let cyp = XYZ{x: choice.x, y: choice.y + 1, z: choice.z};
39+
let cym = XYZ{x: choice.x, y: choice.y - 1, z: choice.z};
40+
let czp = XYZ{x: choice.x, y: choice.y, z: choice.z + 1};
41+
let czm = XYZ{x: choice.x, y: choice.y, z: choice.z - 1};
42+
43+
if !connected.contains(&cxp) {
44+
border.insert(cxp);
45+
}
46+
if !connected.contains(&cxm){
47+
border.insert(cxm);
48+
}
49+
if !connected.contains(&cyp){
50+
border.insert(cyp);
51+
}
52+
if !connected.contains(&cym) {
53+
border.insert(cym);
54+
}
55+
if !connected.contains(&czp){
56+
border.insert(czp);
57+
}
58+
if !connected.contains(&czm) {
59+
border.insert(czm);
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)