Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

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: Switched flag to false by default. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/hydrogen-instructions.h ('k') | src/ia32/lithium-codegen-ia32.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 1016 matching lines...) Expand 10 before | Expand all | Expand 10 after
1027 if (context->upper_bound() == length() && 1027 if (context->upper_bound() == length() &&
1028 context->lower_bound_guarantee() != NULL && 1028 context->lower_bound_guarantee() != NULL &&
1029 context->lower_bound_guarantee() != this && 1029 context->lower_bound_guarantee() != this &&
1030 context->lower_bound_guarantee()->block() != block() && 1030 context->lower_bound_guarantee()->block() != block() &&
1031 offset() < context->offset() && 1031 offset() < context->offset() &&
1032 index_can_increase() && 1032 index_can_increase() &&
1033 context->upper_bound_guarantee() == NULL) { 1033 context->upper_bound_guarantee() == NULL) {
1034 offset_ = context->offset(); 1034 offset_ = context->offset();
1035 SetResponsibilityForRange(DIRECTION_UPPER); 1035 SetResponsibilityForRange(DIRECTION_UPPER);
1036 context->set_upper_bound_guarantee(this); 1036 context->set_upper_bound_guarantee(this);
1037 isolate()->counters()->bounds_checks_eliminated()->Increment();
1037 } else if (context->upper_bound_guarantee() != NULL && 1038 } else if (context->upper_bound_guarantee() != NULL &&
1038 context->upper_bound_guarantee() != this && 1039 context->upper_bound_guarantee() != this &&
1039 context->upper_bound_guarantee()->block() != block() && 1040 context->upper_bound_guarantee()->block() != block() &&
1040 offset() > context->offset() && 1041 offset() > context->offset() &&
1041 index_can_decrease() && 1042 index_can_decrease() &&
1042 context->lower_bound_guarantee() == NULL) { 1043 context->lower_bound_guarantee() == NULL) {
1043 offset_ = context->offset(); 1044 offset_ = context->offset();
1044 SetResponsibilityForRange(DIRECTION_LOWER); 1045 SetResponsibilityForRange(DIRECTION_LOWER);
1045 context->set_lower_bound_guarantee(this); 1046 context->set_lower_bound_guarantee(this);
1047 isolate()->counters()->bounds_checks_eliminated()->Increment();
1046 } 1048 }
1047 } 1049 }
1048 1050
1049 1051
1050 void HBoundsCheck::ApplyIndexChange() { 1052 void HBoundsCheck::ApplyIndexChange() {
1051 if (skip_check()) return; 1053 if (skip_check()) return;
1052 1054
1053 DecompositionResult decomposition; 1055 DecompositionResult decomposition;
1054 bool index_is_decomposable = index()->TryDecompose(&decomposition); 1056 bool index_is_decomposable = index()->TryDecompose(&decomposition);
1055 if (index_is_decomposable) { 1057 if (index_is_decomposable) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1096 scale_ = 0; 1098 scale_ = 0;
1097 responsibility_direction_ = DIRECTION_NONE; 1099 responsibility_direction_ = DIRECTION_NONE;
1098 } 1100 }
1099 1101
1100 1102
1101 void HBoundsCheck::AddInformativeDefinitions() { 1103 void HBoundsCheck::AddInformativeDefinitions() {
1102 // TODO(mmassi): Executing this code during AddInformativeDefinitions 1104 // TODO(mmassi): Executing this code during AddInformativeDefinitions
1103 // is a hack. Move it to some other HPhase. 1105 // is a hack. Move it to some other HPhase.
1104 if (FLAG_array_bounds_checks_elimination) { 1106 if (FLAG_array_bounds_checks_elimination) {
1105 if (index()->TryGuaranteeRange(length())) { 1107 if (index()->TryGuaranteeRange(length())) {
1106 set_skip_check(true); 1108 set_skip_check();
1107 } 1109 }
1108 if (DetectCompoundIndex()) { 1110 if (DetectCompoundIndex()) {
1109 HBoundsCheckBaseIndexInformation* base_index_info = 1111 HBoundsCheckBaseIndexInformation* base_index_info =
1110 new(block()->graph()->zone()) 1112 new(block()->graph()->zone())
1111 HBoundsCheckBaseIndexInformation(this); 1113 HBoundsCheckBaseIndexInformation(this);
1112 base_index_info->InsertAfter(this); 1114 base_index_info->InsertAfter(this);
1113 } 1115 }
1114 } 1116 }
1115 } 1117 }
1116 1118
(...skipping 844 matching lines...) Expand 10 before | Expand all | Expand 10 after
1961 result = false; 1963 result = false;
1962 break; 1964 break;
1963 } 1965 }
1964 } 1966 }
1965 ClearFlag(kNumericConstraintEvaluationInProgress); 1967 ClearFlag(kNumericConstraintEvaluationInProgress);
1966 1968
1967 return result; 1969 return result;
1968 } 1970 }
1969 1971
1970 1972
1973 InductionVariableData* InductionVariableData::ExaminePhi(HPhi* phi) {
1974 if (phi->block()->loop_information() == NULL) return NULL;
1975 if (phi->OperandCount() != 2) return NULL;
1976 int32_t candidate_increment;
1977
1978 candidate_increment = ComputeIncrement(phi, phi->OperandAt(0));
1979 if (candidate_increment != 0) {
1980 return new(phi->block()->graph()->zone())
1981 InductionVariableData(phi, phi->OperandAt(1), candidate_increment);
1982 }
1983
1984 candidate_increment = ComputeIncrement(phi, phi->OperandAt(1));
1985 if (candidate_increment != 0) {
1986 return new(phi->block()->graph()->zone())
1987 InductionVariableData(phi, phi->OperandAt(0), candidate_increment);
1988 }
1989
1990 return NULL;
1991 }
1992
1993
1994 /*
1995 * This function tries to match the following patterns (and all the relevant
1996 * variants related to |, & and + being commutative):
1997 * base | constant_or_mask
1998 * base & constant_and_mask
1999 * (base + constant_offset) & constant_and_mask
2000 * (base - constant_offset) & constant_and_mask
2001 */
2002 void InductionVariableData::DecomposeBitwise(
2003 HValue* value,
2004 BitwiseDecompositionResult* result) {
2005 HValue* base = IgnoreOsrValue(value);
2006 result->base = value;
2007
2008 if (!base->representation().IsInteger32()) return;
2009
2010 if (base->IsBitwise()) {
2011 bool allow_offset = false;
2012 int32_t mask = 0;
2013
2014 HBitwise* bitwise = HBitwise::cast(base);
2015 if (bitwise->right()->IsInteger32Constant()) {
2016 mask = bitwise->right()->GetInteger32Constant();
2017 base = bitwise->left();
2018 } else if (bitwise->left()->IsInteger32Constant()) {
2019 mask = bitwise->left()->GetInteger32Constant();
2020 base = bitwise->right();
2021 } else {
2022 return;
2023 }
2024 if (bitwise->op() == Token::BIT_AND) {
2025 result->and_mask = mask;
2026 allow_offset = true;
2027 } else if (bitwise->op() == Token::BIT_OR) {
2028 result->or_mask = mask;
2029 } else {
2030 return;
2031 }
2032
2033 result->context = bitwise->context();
2034
2035 if (allow_offset) {
2036 if (base->IsAdd()) {
2037 HAdd* add = HAdd::cast(base);
2038 if (add->right()->IsInteger32Constant()) {
2039 base = add->left();
2040 } else if (add->left()->IsInteger32Constant()) {
2041 base = add->right();
2042 }
2043 } else if (base->IsSub()) {
2044 HSub* sub = HSub::cast(base);
2045 if (sub->right()->IsInteger32Constant()) {
2046 base = sub->left();
2047 }
2048 }
2049 }
2050
2051 result->base = base;
2052 }
2053 }
2054
2055
2056 void InductionVariableData::AddCheck(HBoundsCheck* check,
2057 int32_t upper_limit) {
2058 ASSERT(limit_validity() != NULL);
2059 if (limit_validity() != check->block() &&
2060 !limit_validity()->Dominates(check->block())) return;
2061 if (!phi()->block()->current_loop()->IsNestedInThisLoop(
2062 check->block()->current_loop())) return;
2063
2064 ChecksRelatedToLength* length_checks = checks();
2065 while (length_checks != NULL) {
2066 if (length_checks->length() == check->length()) break;
2067 length_checks = length_checks->next();
2068 }
2069 if (length_checks == NULL) {
2070 length_checks = new(check->block()->zone())
2071 ChecksRelatedToLength(check->length(), checks());
2072 checks_ = length_checks;
2073 }
2074
2075 length_checks->AddCheck(check, upper_limit);
2076 }
2077
2078
2079 void InductionVariableData::ChecksRelatedToLength::CloseCurrentBlock() {
2080 if (checks() != NULL) {
2081 InductionVariableCheck* c = checks();
2082 HBasicBlock* current_block = c->check()->block();
2083 while (c != NULL && c->check()->block() == current_block) {
2084 c->set_upper_limit(current_upper_limit_);
2085 c = c->next();
2086 }
2087 }
2088 }
2089
2090
2091 void InductionVariableData::ChecksRelatedToLength::UseNewIndexInCurrentBlock(
2092 Token::Value token,
2093 int32_t mask,
2094 HValue* index_base,
2095 HValue* context) {
2096 ASSERT(first_check_in_block() != NULL);
2097 HValue* previous_index = first_check_in_block()->index();
2098 ASSERT(context != NULL);
2099
2100 set_added_constant(new(index_base->block()->graph()->zone()) HConstant(
2101 mask, index_base->representation()));
2102 if (added_index() != NULL) {
2103 added_constant()->InsertBefore(added_index());
2104 } else {
2105 added_constant()->InsertBefore(first_check_in_block());
2106 }
2107
2108 if (added_index() == NULL) {
2109 first_check_in_block()->ReplaceAllUsesWith(first_check_in_block()->index());
2110 HInstruction* new_index = HBitwise::New(
2111 index_base->block()->graph()->zone(),
2112 token, context, index_base, added_constant());
2113 ASSERT(new_index->IsBitwise());
2114 new_index->ClearAllSideEffects();
2115 new_index->AssumeRepresentation(Representation::Integer32());
2116 set_added_index(HBitwise::cast(new_index));
2117 added_index()->InsertBefore(first_check_in_block());
2118 }
2119 ASSERT(added_index()->op() == token);
2120
2121 added_index()->SetOperandAt(1, index_base);
2122 added_index()->SetOperandAt(2, added_constant());
2123 first_check_in_block()->SetOperandAt(0, added_index());
2124 if (previous_index->UseCount() == 0) {
2125 previous_index->DeleteAndReplaceWith(NULL);
2126 }
2127 }
2128
2129 void InductionVariableData::ChecksRelatedToLength::AddCheck(
2130 HBoundsCheck* check,
2131 int32_t upper_limit) {
2132 BitwiseDecompositionResult decomposition;
2133 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
2134
2135 if (first_check_in_block() == NULL ||
2136 first_check_in_block()->block() != check->block()) {
2137 CloseCurrentBlock();
2138
2139 first_check_in_block_ = check;
2140 set_added_index(NULL);
2141 set_added_constant(NULL);
2142 current_and_mask_in_block_ = decomposition.and_mask;
2143 current_or_mask_in_block_ = decomposition.or_mask;
2144 current_upper_limit_ = upper_limit;
2145
2146 InductionVariableCheck* new_check = new(check->block()->graph()->zone())
2147 InductionVariableCheck(check, checks_, upper_limit);
2148 checks_ = new_check;
2149 return;
2150 }
2151
2152 if (upper_limit > current_upper_limit()) {
2153 current_upper_limit_ = upper_limit;
2154 }
2155
2156 if (decomposition.and_mask != 0 &&
2157 current_or_mask_in_block() == 0) {
2158 if (current_and_mask_in_block() == 0 ||
2159 decomposition.and_mask > current_and_mask_in_block()) {
2160 UseNewIndexInCurrentBlock(Token::BIT_AND,
2161 decomposition.and_mask,
2162 decomposition.base,
2163 decomposition.context);
2164 current_and_mask_in_block_ = decomposition.and_mask;
2165 }
2166 check->set_skip_check();
2167 }
2168 if (current_and_mask_in_block() == 0) {
2169 if (decomposition.or_mask > current_or_mask_in_block()) {
2170 UseNewIndexInCurrentBlock(Token::BIT_OR,
2171 decomposition.or_mask,
2172 decomposition.base,
2173 decomposition.context);
2174 current_or_mask_in_block_ = decomposition.or_mask;
2175 }
2176 check->set_skip_check();
2177 }
2178
2179 if (!check->skip_check()) {
2180 InductionVariableCheck* new_check = new(check->block()->graph()->zone())
2181 InductionVariableCheck(check, checks_, upper_limit);
2182 checks_ = new_check;
2183 }
2184 }
2185
2186
2187 /*
2188 * This method detects if phi is an induction variable, with phi_operand as
2189 * its "incremented" value (the other operand would be the "base" value).
2190 *
2191 * It cheks is phi_operand has the form "phi + constant".
2192 * If yes, the constant is the increment that the induction variable gets at
2193 * every loop iteration.
2194 * Otherwise it returns 0.
2195 */
2196 int32_t InductionVariableData::ComputeIncrement(HPhi* phi,
2197 HValue* phi_operand) {
2198 if (!phi_operand->representation().IsInteger32()) return 0;
2199
2200 if (phi_operand->IsAdd()) {
2201 HAdd* operation = HAdd::cast(phi_operand);
2202 if (operation->left() == phi &&
2203 operation->right()->IsInteger32Constant()) {
2204 return operation->right()->GetInteger32Constant();
2205 } else if (operation->right() == phi &&
2206 operation->left()->IsInteger32Constant()) {
2207 return operation->left()->GetInteger32Constant();
2208 }
2209 } else if (phi_operand->IsSub()) {
2210 HSub* operation = HSub::cast(phi_operand);
2211 if (operation->left() == phi &&
2212 operation->right()->IsInteger32Constant()) {
2213 return -operation->right()->GetInteger32Constant();
2214 }
2215 }
2216
2217 return 0;
2218 }
2219
2220
2221 /*
2222 * Swaps the information in "update" with the one contained in "this".
2223 * The swapping is important because this method is used while doing a
2224 * dominator tree traversal, and "update" will retain the old data that
2225 * will be restored while backtracking.
2226 */
2227 void InductionVariableData::UpdateAdditionalLimit(
2228 InductionVariableLimitUpdate* update) {
2229 ASSERT(update->updated_variable == this);
2230 if (update->limit_is_upper) {
2231 swap(&additional_upper_limit_, &update->limit);
2232 swap(&additional_upper_limit_is_included_, &update->limit_is_included);
2233 } else {
2234 swap(&additional_lower_limit_, &update->limit);
2235 swap(&additional_lower_limit_is_included_, &update->limit_is_included);
2236 }
2237 }
2238
2239
2240 int32_t InductionVariableData::ComputeUpperLimit(int32_t and_mask,
2241 int32_t or_mask) {
2242 // Should be Smi::kMaxValue but it must fit 32 bits; lower is safe anyway.
2243 const int32_t MAX_LIMIT = 1 << 30;
2244
2245 int32_t result = MAX_LIMIT;
2246
2247 if (limit() != NULL &&
2248 limit()->IsInteger32Constant()) {
2249 int32_t limit_value = limit()->GetInteger32Constant();
2250 if (!limit_included()) {
2251 limit_value--;
2252 }
2253 if (limit_value < result) result = limit_value;
2254 }
2255
2256 if (additional_upper_limit() != NULL &&
2257 additional_upper_limit()->IsInteger32Constant()) {
2258 int32_t limit_value = additional_upper_limit()->GetInteger32Constant();
2259 if (!additional_upper_limit_is_included()) {
2260 limit_value--;
2261 }
2262 if (limit_value < result) result = limit_value;
2263 }
2264
2265 if (and_mask > 0 && and_mask < MAX_LIMIT) {
2266 if (and_mask < result) result = and_mask;
2267 return result;
2268 }
2269
2270 // Add the effect of the or_mask.
2271 result |= or_mask;
2272
2273 return result >= MAX_LIMIT ? kNoLimit : result;
2274 }
2275
2276
2277 HValue* InductionVariableData::IgnoreOsrValue(HValue* v) {
2278 if (!v->IsPhi()) return v;
2279 HPhi* phi = HPhi::cast(v);
2280 if (phi->OperandCount() != 2) return v;
2281 if (phi->OperandAt(0)->block()->is_osr_entry()) {
2282 return phi->OperandAt(1);
2283 } else if (phi->OperandAt(1)->block()->is_osr_entry()) {
2284 return phi->OperandAt(0);
2285 } else {
2286 return v;
2287 }
2288 }
2289
2290
2291 InductionVariableData* InductionVariableData::GetInductionVariableData(
2292 HValue* v) {
2293 v = IgnoreOsrValue(v);
2294 if (v->IsPhi()) {
2295 return HPhi::cast(v)->induction_variable_data();
2296 }
2297 return NULL;
2298 }
2299
2300
2301 /*
2302 * Check if a conditional branch to "current_branch" with token "token" is
2303 * the branch that keeps the induction loop running (and, conversely, will
2304 * terminate it if the "other_branch" is taken).
2305 *
2306 * Three conditions must be met:
2307 * - "current_branch" must be in the induction loop.
2308 * - "other_branch" must be out of the induction loop.
2309 * - "token" and the induction increment must be "compatible": the token should
2310 * be a condition that keeps the execution inside the loop until the limit is
2311 * reached.
2312 */
2313 bool InductionVariableData::CheckIfBranchIsLoopGuard(
2314 Token::Value token,
2315 HBasicBlock* current_branch,
2316 HBasicBlock* other_branch) {
2317 if (!phi()->block()->current_loop()->IsNestedInThisLoop(
2318 current_branch->current_loop())) {
2319 return false;
2320 }
2321
2322 if (phi()->block()->current_loop()->IsNestedInThisLoop(
2323 other_branch->current_loop())) {
2324 return false;
2325 }
2326
2327 if (increment() > 0 && (token == Token::LT || token == Token::LTE)) {
2328 return true;
2329 }
2330 if (increment() < 0 && (token == Token::GT || token == Token::GTE)) {
2331 return true;
2332 }
2333 if (Token::IsInequalityOp(token) && (increment() == 1 || increment() == -1)) {
2334 return true;
2335 }
2336
2337 return false;
2338 }
2339
2340
2341 void InductionVariableData::ComputeLimitFromPredecessorBlock(
2342 HBasicBlock* block,
2343 LimitFromPredecessorBlock* result) {
2344 if (block->predecessors()->length() != 1) return;
2345 HBasicBlock* predecessor = block->predecessors()->at(0);
2346 HInstruction* end = predecessor->last();
2347
2348 if (!end->IsCompareNumericAndBranch()) return;
2349 HCompareNumericAndBranch* branch = HCompareNumericAndBranch::cast(end);
2350
2351 Token::Value token = branch->token();
2352 if (!Token::IsArithmeticCompareOp(token)) return;
2353
2354 HBasicBlock* other_target;
2355 if (block == branch->SuccessorAt(0)) {
2356 other_target = branch->SuccessorAt(1);
2357 } else {
2358 other_target = branch->SuccessorAt(0);
2359 token = Token::NegateCompareOp(token);
2360 ASSERT(block == branch->SuccessorAt(1));
2361 }
2362
2363 InductionVariableData* data;
2364
2365 data = GetInductionVariableData(branch->left());
2366 HValue* limit = branch->right();
2367 if (data == NULL) {
2368 data = GetInductionVariableData(branch->right());
2369 token = Token::ReverseCompareOp(token);
2370 limit = branch->left();
2371 }
2372
2373 if (data != NULL) {
2374 result->variable = data;
2375 result->token = token;
2376 result->limit = limit;
2377 result->other_target = other_target;
2378 }
2379 }
2380
2381
2382 /*
2383 * Compute the limit that is imposed on an induction variable when entering
2384 * "block" (if any).
2385 * If the limit is the "proper" induction limit (the one that makes the loop
2386 * terminate when the induction variable reaches it) it is stored directly in
2387 * the induction variable data.
2388 * Otherwise the limit is written in "additional_limit" and the method
2389 * returns true.
2390 */
2391 bool InductionVariableData::ComputeInductionVariableLimit(
2392 HBasicBlock* block,
2393 InductionVariableLimitUpdate* additional_limit) {
2394 LimitFromPredecessorBlock limit;
2395 ComputeLimitFromPredecessorBlock(block, &limit);
2396 if (!limit.LimitIsValid()) return false;
2397
2398 if (limit.variable->CheckIfBranchIsLoopGuard(limit.token,
2399 block,
2400 limit.other_target)) {
2401 limit.variable->limit_ = limit.limit;
2402 limit.variable->limit_included_ = limit.LimitIsIncluded();
2403 limit.variable->limit_validity_ = block;
2404 limit.variable->induction_exit_block_ = block->predecessors()->at(0);
2405 limit.variable->induction_exit_target_ = limit.other_target;
2406 return false;
2407 } else {
2408 additional_limit->updated_variable = limit.variable;
2409 additional_limit->limit = limit.limit;
2410 additional_limit->limit_is_upper = limit.LimitIsUpper();
2411 additional_limit->limit_is_included = limit.LimitIsIncluded();
2412 return true;
2413 }
2414 }
2415
2416
1971 Range* HMathMinMax::InferRange(Zone* zone) { 2417 Range* HMathMinMax::InferRange(Zone* zone) {
1972 if (representation().IsInteger32()) { 2418 if (representation().IsInteger32()) {
1973 Range* a = left()->range(); 2419 Range* a = left()->range();
1974 Range* b = right()->range(); 2420 Range* b = right()->range();
1975 Range* res = a->Copy(zone); 2421 Range* res = a->Copy(zone);
1976 if (operation_ == kMathMax) { 2422 if (operation_ == kMathMax) {
1977 res->CombinedMax(b); 2423 res->CombinedMax(b);
1978 } else { 2424 } else {
1979 ASSERT(operation_ == kMathMin); 2425 ASSERT(operation_ == kMathMin);
1980 res->CombinedMin(b); 2426 res->CombinedMin(b);
(...skipping 2064 matching lines...) Expand 10 before | Expand all | Expand 10 after
4045 case kBackingStore: 4491 case kBackingStore:
4046 if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString()); 4492 if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
4047 stream->Add("[backing-store]"); 4493 stream->Add("[backing-store]");
4048 break; 4494 break;
4049 } 4495 }
4050 4496
4051 stream->Add("@%d", offset()); 4497 stream->Add("@%d", offset());
4052 } 4498 }
4053 4499
4054 } } // namespace v8::internal 4500 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/hydrogen-instructions.h ('k') | src/ia32/lithium-codegen-ia32.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698