Chromium Code Reviews

Side by Side Diff: src/hydrogen-instructions.cc

Issue 17568015: New array bounds check elimination pass (focused on induction variables and bitwise operations). (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressed review comments (3). Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 1015 matching lines...)
1026 if (context->upper_bound() == length() && 1026 if (context->upper_bound() == length() &&
1027 context->lower_bound_guarantee() != NULL && 1027 context->lower_bound_guarantee() != NULL &&
1028 context->lower_bound_guarantee() != this && 1028 context->lower_bound_guarantee() != this &&
1029 context->lower_bound_guarantee()->block() != block() && 1029 context->lower_bound_guarantee()->block() != block() &&
1030 offset() < context->offset() && 1030 offset() < context->offset() &&
1031 index_can_increase() && 1031 index_can_increase() &&
1032 context->upper_bound_guarantee() == NULL) { 1032 context->upper_bound_guarantee() == NULL) {
1033 offset_ = context->offset(); 1033 offset_ = context->offset();
1034 SetResponsibilityForRange(DIRECTION_UPPER); 1034 SetResponsibilityForRange(DIRECTION_UPPER);
1035 context->set_upper_bound_guarantee(this); 1035 context->set_upper_bound_guarantee(this);
1036 isolate()->counters()->bounds_checks_covered()->Increment();
1036 } else if (context->upper_bound_guarantee() != NULL && 1037 } else if (context->upper_bound_guarantee() != NULL &&
1037 context->upper_bound_guarantee() != this && 1038 context->upper_bound_guarantee() != this &&
1038 context->upper_bound_guarantee()->block() != block() && 1039 context->upper_bound_guarantee()->block() != block() &&
1039 offset() > context->offset() && 1040 offset() > context->offset() &&
1040 index_can_decrease() && 1041 index_can_decrease() &&
1041 context->lower_bound_guarantee() == NULL) { 1042 context->lower_bound_guarantee() == NULL) {
1042 offset_ = context->offset(); 1043 offset_ = context->offset();
1043 SetResponsibilityForRange(DIRECTION_LOWER); 1044 SetResponsibilityForRange(DIRECTION_LOWER);
1044 context->set_lower_bound_guarantee(this); 1045 context->set_lower_bound_guarantee(this);
1046 isolate()->counters()->bounds_checks_covered()->Increment();
1045 } 1047 }
1046 } 1048 }
1047 1049
1048 1050
1049 void HBoundsCheck::ApplyIndexChange() { 1051 void HBoundsCheck::ApplyIndexChange() {
1050 if (skip_check()) return; 1052 if (skip_check()) return;
1051 1053
1052 DecompositionResult decomposition; 1054 DecompositionResult decomposition;
1053 bool index_is_decomposable = index()->TryDecompose(&decomposition); 1055 bool index_is_decomposable = index()->TryDecompose(&decomposition);
1054 if (index_is_decomposable) { 1056 if (index_is_decomposable) {
(...skipping 40 matching lines...)
1095 scale_ = 0; 1097 scale_ = 0;
1096 responsibility_direction_ = DIRECTION_NONE; 1098 responsibility_direction_ = DIRECTION_NONE;
1097 } 1099 }
1098 1100
1099 1101
1100 void HBoundsCheck::AddInformativeDefinitions() { 1102 void HBoundsCheck::AddInformativeDefinitions() {
1101 // TODO(mmassi): Executing this code during AddInformativeDefinitions 1103 // TODO(mmassi): Executing this code during AddInformativeDefinitions
1102 // is a hack. Move it to some other HPhase. 1104 // is a hack. Move it to some other HPhase.
1103 if (FLAG_array_bounds_checks_elimination) { 1105 if (FLAG_array_bounds_checks_elimination) {
1104 if (index()->TryGuaranteeRange(length())) { 1106 if (index()->TryGuaranteeRange(length())) {
1105 set_skip_check(true); 1107 set_skip_check();
1106 } 1108 }
1107 if (DetectCompoundIndex()) { 1109 if (DetectCompoundIndex()) {
1108 HBoundsCheckBaseIndexInformation* base_index_info = 1110 HBoundsCheckBaseIndexInformation* base_index_info =
1109 new(block()->graph()->zone()) 1111 new(block()->graph()->zone())
1110 HBoundsCheckBaseIndexInformation(this); 1112 HBoundsCheckBaseIndexInformation(this);
1111 base_index_info->InsertAfter(this); 1113 base_index_info->InsertAfter(this);
1112 } 1114 }
1113 } 1115 }
1114 } 1116 }
1115 1117
(...skipping 825 matching lines...)
1941 result = false; 1943 result = false;
1942 break; 1944 break;
1943 } 1945 }
1944 } 1946 }
1945 ClearFlag(kNumericConstraintEvaluationInProgress); 1947 ClearFlag(kNumericConstraintEvaluationInProgress);
1946 1948
1947 return result; 1949 return result;
1948 } 1950 }
1949 1951
1950 1952
1953 InductionVariableData* InductionVariableData::ExaminePhi(HPhi* phi) {
1954 if (phi->block()->loop_information() == NULL) return NULL;
1955 if (phi->OperandCount() != 2) return NULL;
1956 int32_t candidate_increment;
1957
1958 candidate_increment = ComputeIncrement(phi, phi->OperandAt(0));
1959 if (candidate_increment != 0) {
1960 return new(phi->block()->graph()->zone())
1961 InductionVariableData(phi, phi->OperandAt(1), candidate_increment);
1962 }
1963
1964 candidate_increment = ComputeIncrement(phi, phi->OperandAt(1));
1965 if (candidate_increment != 0) {
1966 return new(phi->block()->graph()->zone())
1967 InductionVariableData(phi, phi->OperandAt(0), candidate_increment);
1968 }
1969
1970 return NULL;
1971 }
1972
1973 /*
1974 * This function tries to match the following patterns (and all the relevant
1975 * variants related to |, & and + being commutative):
1976 * base | or_mask
1977 * base & and_mask
1978 * (base + constant_offset) & and_mask
1979 * (base - constant_offset) & and_mask
1980 */
1981 void InductionVariableData::DecomposeBitwise(
1982 HValue* value,
1983 BitwiseDecompositionResult* result) {
1984 HValue* current = IgnoreOsrValue(value);
1985 bool has_offset = false;
1986 result->base = value;
titzer 2013/07/10 16:34:18 Thanks for the comment; now that I understand the
Massi 2013/07/11 11:15:22 Done.
1987
1988 if (!current->representation().IsInteger32()) return;
1989
1990 while (true) {
1991 if (current->IsAdd()) {
1992 if (result->and_mask != 0 || result->or_mask != 0) return;
1993 HAdd* operation = HAdd::cast(current);
1994 if (operation->right()->IsInteger32Constant()) {
1995 has_offset = true;
1996 current = operation->left();
1997 } else if (operation->left()->IsInteger32Constant()) {
1998 has_offset = true;
1999 current = operation->right();
2000 } else {
2001 return;
2002 }
2003 continue;
2004 } else if (current->IsSub()) {
2005 if (result->and_mask != 0 || result->or_mask != 0) return;
2006 HSub* operation = HSub::cast(current);
2007 if (operation->right()->IsInteger32Constant()) {
2008 has_offset = true;
2009 current = operation->left();
2010 } else {
2011 return;
2012 }
2013 continue;
2014 } else if (current->IsBitwise()) {
2015 HBitwise* operation = HBitwise::cast(current);
2016 int32_t* mask = NULL;
2017 if (operation->op() == Token::BIT_AND) {
2018 mask = &(result->and_mask);
2019 } else if (operation->op() == Token::BIT_OR) {
2020 if (has_offset) {
2021 return;
2022 }
2023 mask = &(result->or_mask);
2024 } else {
2025 return;
2026 }
2027 if (operation->right()->IsInteger32Constant()) {
2028 *mask = operation->right()->GetInteger32Constant();
2029 current = operation->left();
2030 } else if (operation->left()->IsInteger32Constant()) {
2031 *mask = operation->left()->GetInteger32Constant();
2032 current = operation->right();
2033 }
2034 result->context = operation->context();
2035 result->base = current;
2036 return;
2037 } else {
2038 return;
2039 }
2040 }
2041
2042 return;
2043 }
2044
2045
2046 void InductionVariableData::AddCheck(HBoundsCheck* check,
2047 int32_t upper_limit) {
2048 ASSERT(limit_validity() != NULL);
2049 if (limit_validity() != check->block() &&
2050 !limit_validity()->Dominates(check->block())) return;
2051 if (!phi()->block()->current_loop()->IsNestedInThisLoop(
2052 check->block()->current_loop())) return;
2053
2054 ChecksRelatedToLength* length_checks = checks();
2055 while (length_checks != NULL) {
2056 if (length_checks->length() == check->length()) break;
2057 length_checks = length_checks->next();
2058 }
2059 if (length_checks == NULL) {
2060 length_checks = new(check->block()->zone())
2061 ChecksRelatedToLength(check->length(), checks());
2062 checks_ = length_checks;
2063 }
2064
2065 length_checks->AddCheck(check, upper_limit);
2066 }
2067
2068 void InductionVariableData::ChecksRelatedToLength::CloseCurrentBlock() {
2069 if (checks() != NULL) {
2070 InductionVariableCheck* c = checks();
2071 HBasicBlock* current_block = c->check()->block();
2072 while (c != NULL && c->check()->block() == current_block) {
2073 c->set_upper_limit(current_upper_limit_);
2074 c = c->next();
2075 }
2076 }
2077 }
2078
2079
2080 void InductionVariableData::ChecksRelatedToLength::UseNewIndexInCurrentBlock(
2081 Token::Value token,
2082 int32_t mask,
2083 HValue* index_base,
2084 HValue* context) {
2085 ASSERT(first_check_in_block() != NULL);
2086 HValue* previous_index = first_check_in_block()->index();
2087 ASSERT(context != NULL);
2088
2089 set_added_constant(new(index_base->block()->graph()->zone()) HConstant(
2090 mask, index_base->representation()));
2091 if (added_index() != NULL) {
2092 added_constant()->InsertBefore(added_index());
2093 } else {
2094 added_constant()->InsertBefore(first_check_in_block());
2095 }
2096
2097 if (added_index() == NULL) {
2098 first_check_in_block()->ReplaceAllUsesWith(first_check_in_block()->index());
2099 HInstruction* new_index = HBitwise::New(
2100 index_base->block()->graph()->zone(),
2101 token, context, index_base, added_constant());
2102 ASSERT(new_index->IsBitwise());
2103 new_index->ClearAllSideEffects();
2104 new_index->AssumeRepresentation(Representation::Integer32());
2105 set_added_index(HBitwise::cast(new_index));
2106 added_index()->InsertBefore(first_check_in_block());
2107 }
2108 ASSERT(added_index()->op() == token);
2109
2110 added_index()->SetOperandAt(1, index_base);
2111 added_index()->SetOperandAt(2, added_constant());
2112 first_check_in_block()->SetOperandAt(0, added_index());
2113 if (previous_index->UseCount() == 0) {
2114 previous_index->DeleteAndReplaceWith(NULL);
2115 }
2116 }
2117
2118 void InductionVariableData::ChecksRelatedToLength::AddCheck(
2119 HBoundsCheck* check,
2120 int32_t upper_limit) {
2121 BitwiseDecompositionResult decomposition;
2122 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
2123
2124 if (first_check_in_block() == NULL ||
2125 first_check_in_block()->block() != check->block()) {
2126 CloseCurrentBlock();
2127
2128 first_check_in_block_ = check;
2129 set_added_index(NULL);
2130 set_added_constant(NULL);
2131 current_and_mask_in_block_ = decomposition.and_mask;
2132 current_or_mask_in_block_ = decomposition.or_mask;
2133 current_upper_limit_ = upper_limit;
2134
2135 InductionVariableCheck* new_check = new(check->block()->graph()->zone())
2136 InductionVariableCheck(check, checks_, upper_limit);
2137 checks_ = new_check;
2138 return;
2139 }
2140
2141 if (upper_limit > current_upper_limit()) {
2142 current_upper_limit_ = upper_limit;
2143 }
2144
2145 if (decomposition.and_mask != 0 &&
2146 current_or_mask_in_block() == 0) {
2147 if (current_and_mask_in_block() == 0 ||
2148 decomposition.and_mask > current_and_mask_in_block()) {
2149 UseNewIndexInCurrentBlock(Token::BIT_AND,
2150 decomposition.and_mask,
2151 decomposition.base,
2152 decomposition.context);
2153 current_and_mask_in_block_ = decomposition.and_mask;
2154 }
2155 check->set_skip_check();
2156 }
2157 if (current_and_mask_in_block() == 0) {
2158 if (decomposition.or_mask > current_or_mask_in_block()) {
2159 UseNewIndexInCurrentBlock(Token::BIT_OR,
2160 decomposition.or_mask,
2161 decomposition.base,
2162 decomposition.context);
2163 current_or_mask_in_block_ = decomposition.or_mask;
2164 }
2165 check->set_skip_check();
2166 }
2167
2168 if (!check->skip_check()) {
2169 InductionVariableCheck* new_check = new(check->block()->graph()->zone())
2170 InductionVariableCheck(check, checks_, upper_limit);
2171 checks_ = new_check;
2172 }
2173 }
2174
2175
2176 /*
2177 * This method detects if phi is an induction variable, with phi_operand as
2178 * its "incremented" value (the other operand would be the "base" value).
2179 *
2180 * It cheks is phi_operand has the form "phi + constant".
2181 * If yes, the constant is the increment that the induction variable gets at
2182 * every loop iteration.
2183 * Otherwise it returns 0.
2184 */
2185 int32_t InductionVariableData::ComputeIncrement(HPhi* phi,
2186 HValue* phi_operand) {
2187 if (!phi_operand->representation().IsInteger32()) return 0;
2188
2189 if (phi_operand->IsAdd()) {
2190 HAdd* operation = HAdd::cast(phi_operand);
2191 if (operation->left() == phi &&
2192 operation->right()->IsInteger32Constant()) {
2193 return operation->right()->GetInteger32Constant();
2194 } else if (operation->right() == phi &&
2195 operation->left()->IsInteger32Constant()) {
2196 return operation->left()->GetInteger32Constant();
2197 }
2198 } else if (phi_operand->IsSub()) {
2199 HSub* operation = HSub::cast(phi_operand);
2200 if (operation->left() == phi &&
2201 operation->right()->IsInteger32Constant()) {
2202 return -operation->right()->GetInteger32Constant();
2203 }
2204 }
2205
2206 return 0;
2207 }
2208
2209
2210 HValue* InductionVariableData::IgnoreOsrValue(HValue* v) {
2211 if (!v->IsPhi()) return v;
2212 HPhi* phi = HPhi::cast(v);
2213 if (phi->OperandCount() != 2) return v;
2214 if (phi->OperandAt(0)->block()->is_osr_entry()) {
2215 return phi->OperandAt(1);
2216 } else if (phi->OperandAt(1)->block()->is_osr_entry()) {
2217 return phi->OperandAt(0);
2218 } else {
2219 return v;
2220 }
2221 }
2222
2223
2224 InductionVariableData* InductionVariableData::GetInductionVariableData(
2225 HValue* v) {
2226 v = IgnoreOsrValue(v);
2227 if (v->IsPhi()) {
2228 return HPhi::cast(v)->induction_variable_data();
2229 }
2230 return NULL;
2231 }
2232
2233
2234 /*
2235 * Check if a comparison token can represent a branch that keeps the control
2236 * flow inside a loop (in other words, the branch where the increment is still
2237 * applied to the induction variable).
2238 * If this is true, then the condition associated with the branch can be the
2239 * induction variable limit for the loop.
2240 */
2241 bool InductionVariableData::TokenCanRepresentLoopGuard(Token::Value token) {
titzer 2013/07/10 16:34:18 With the name change I see now more clearly what y
Massi 2013/07/11 11:15:22 Inlined it into CheckIfBranchIsLoopGuard.
2242 if (increment() > 0 && (token == Token::GT || token == Token::GTE)) {
2243 return false;
2244 }
2245 if (increment() < 0 && (token == Token::GT || token == Token::GTE)) {
2246 return false;
2247 }
2248 if (Token::IsInequalityOp(token) && increment() != 1 && increment() != -1) {
2249 return false;
2250 }
2251 return true;
2252 }
2253
2254
2255 bool InductionVariableData::CheckIfBranchIsLoopGuard(HBasicBlock* in_branch,
2256 HBasicBlock* out_branch) {
titzer 2013/07/10 16:34:18 This is kind of a strange name for this method. I
Massi 2013/07/11 11:15:22 After the off line discussion I ended up not inlin
2257 return phi()->block()->current_loop()->IsNestedInThisLoop(
2258 in_branch->current_loop()) &&
2259 !phi()->block()->current_loop()->IsNestedInThisLoop(
2260 out_branch->current_loop());
2261 }
2262
2263
2264 bool InductionVariableData::ComputeInductionVariableLimit(
2265 HBasicBlock* block,
2266 InductionVariableLimitUpdate* additional_limit) {
2267 if (block->predecessors()->length() != 1) return false;
2268 HBasicBlock* predecessor = block->predecessors()->at(0);
2269 HInstruction* end = predecessor->last();
titzer 2013/07/10 16:34:18 This method could benefit from being split up into
Massi 2013/07/11 11:15:22 Done.
2270
2271 if (!end->IsCompareIDAndBranch()) return false;
2272 HCompareIDAndBranch* branch = HCompareIDAndBranch::cast(end);
2273
2274 Token::Value token = branch->token();
2275 if (!Token::IsArithmeticCompareOp(token)) return false;
2276
2277 bool is_else_branch;
2278 HBasicBlock* other_target;
2279 if (block == branch->SuccessorAt(0)) {
2280 is_else_branch = false;
2281 other_target = branch->SuccessorAt(1);
2282 } else {
2283 is_else_branch = true;
2284 other_target = branch->SuccessorAt(0);
2285 ASSERT(block == branch->SuccessorAt(1));
2286 }
2287
2288 if (is_else_branch) {
titzer 2013/07/10 16:34:18 Please just negate the token above, and don't use
Massi 2013/07/11 11:15:22 Done.
2289 token = Token::NegateCompareOp(token);
2290 }
2291 bool limit_included = (Token::IsEqualityOp(token) ||
2292 token == Token::GTE || token == Token::LTE);
2293
2294 InductionVariableData* data;
2295
2296 data = GetInductionVariableData(branch->left());
2297 Token::Value actual_token = token;
2298 HValue* limit = branch->right();
2299 if (data == NULL) {
2300 data = GetInductionVariableData(branch->right());
2301 actual_token = Token::ReverseCompareOp(token);
2302 limit = branch->left();
2303 }
2304
2305 if (data != NULL) {
2306 if (data->TokenCanRepresentLoopGuard(actual_token) &&
2307 data->CheckIfBranchIsLoopGuard(block, other_target)) {
2308 data->limit_ = limit;
2309 data->limit_included_ = limit_included;
2310 data->limit_validity_ = block;
2311 data->induction_exit_block_ = predecessor;
2312 data->induction_exit_target_ = other_target;
2313 return false;
2314 } else {
2315 additional_limit->updated_variable = data;
2316 additional_limit->limit = limit;
2317 additional_limit->limit_is_upper = (actual_token == Token::LTE ||
2318 actual_token == Token::LT ||
2319 actual_token == Token::NE);
2320 additional_limit->limit_is_included = limit_included;
2321 return true;
2322 }
2323 }
2324
2325 return false;
2326 }
2327
2328
1951 Range* HMathMinMax::InferRange(Zone* zone) { 2329 Range* HMathMinMax::InferRange(Zone* zone) {
1952 if (representation().IsInteger32()) { 2330 if (representation().IsInteger32()) {
1953 Range* a = left()->range(); 2331 Range* a = left()->range();
1954 Range* b = right()->range(); 2332 Range* b = right()->range();
1955 Range* res = a->Copy(zone); 2333 Range* res = a->Copy(zone);
1956 if (operation_ == kMathMax) { 2334 if (operation_ == kMathMax) {
1957 res->CombinedMax(b); 2335 res->CombinedMax(b);
1958 } else { 2336 } else {
1959 ASSERT(operation_ == kMathMin); 2337 ASSERT(operation_ == kMathMin);
1960 res->CombinedMin(b); 2338 res->CombinedMin(b);
(...skipping 1905 matching lines...)
3866 case kBackingStore: 4244 case kBackingStore:
3867 if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString()); 4245 if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
3868 stream->Add("[backing-store]"); 4246 stream->Add("[backing-store]");
3869 break; 4247 break;
3870 } 4248 }
3871 4249
3872 stream->Add("@%d", offset()); 4250 stream->Add("@%d", offset());
3873 } 4251 }
3874 4252
3875 } } // namespace v8::internal 4253 } } // namespace v8::internal
OLDNEW

Powered by Google App Engine