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

Side by Side Diff: runtime/vm/flow_graph_compiler_x64.cc

Issue 10458031: In generated code for x64 don't load object's class directly from class_ field. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: CoreClass in flow_graph_compiler_x64.cc should return RawClass* Created 8 years, 6 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
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64.
6 #if defined(TARGET_ARCH_X64) 6 #if defined(TARGET_ARCH_X64)
7 7
8 #include "vm/flow_graph_compiler.h" 8 #include "vm/flow_graph_compiler.h"
9 9
10 #include "lib/error.h" 10 #include "lib/error.h"
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; 129 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
130 char* chars = reinterpret_cast<char*>( 130 char* chars = reinterpret_cast<char*>(
131 Isolate::Current()->current_zone()->Allocate(len)); 131 Isolate::Current()->current_zone()->Allocate(len));
132 OS::SNPrint(chars, len, kFormat, function_name, reason); 132 OS::SNPrint(chars, len, kFormat, function_name, reason);
133 const Error& error = Error::Handle( 133 const Error& error = Error::Handle(
134 LanguageError::New(String::Handle(String::New(chars)))); 134 LanguageError::New(String::Handle(String::New(chars))));
135 Isolate::Current()->long_jump_base()->Jump(1, error); 135 Isolate::Current()->long_jump_base()->Jump(1, error);
136 } 136 }
137 137
138 138
139 static const Class* CoreClass(const char* c_name) { 139 static RawClass* CoreClass(const char* c_name) {
140 const String& class_name = String::Handle(String::NewSymbol(c_name)); 140 const String& class_name = String::Handle(String::NewSymbol(c_name));
141 const Class& cls = Class::ZoneHandle(Library::Handle( 141 RawClass* raw_class = Library::Handle(
142 Library::CoreImplLibrary()).LookupClass(class_name)); 142 Library::CoreImplLibrary()).LookupClass(class_name);
143 ASSERT(!cls.IsNull()); 143 ASSERT(raw_class != Class::null());
144 return &cls; 144 return raw_class;
145 } 145 }
146 146
147 147
148 #define __ assembler_-> 148 #define __ assembler_->
149 149
150 150
151 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if 151 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
152 // type test is conclusive, otherwise fallthrough if a type test could not 152 // type test is conclusive, otherwise fallthrough if a type test could not
153 // be completed. 153 // be completed.
154 // RAX: instance (must survive), 154 // RAX: instance (must survive),
(...skipping 10 matching lines...) Expand all
165 // A Smi object cannot be the instance of a parameterized class. 165 // A Smi object cannot be the instance of a parameterized class.
166 __ testq(RAX, Immediate(kSmiTagMask)); 166 __ testq(RAX, Immediate(kSmiTagMask));
167 __ j(ZERO, is_not_instance_lbl); 167 __ j(ZERO, is_not_instance_lbl);
168 const AbstractTypeArguments& type_arguments = 168 const AbstractTypeArguments& type_arguments =
169 AbstractTypeArguments::ZoneHandle(type.arguments()); 169 AbstractTypeArguments::ZoneHandle(type.arguments());
170 const bool is_raw_type = type_arguments.IsNull() || 170 const bool is_raw_type = type_arguments.IsNull() ||
171 type_arguments.IsRaw(type_arguments.Length()); 171 type_arguments.IsRaw(type_arguments.Length());
172 if (is_raw_type) { 172 if (is_raw_type) {
173 // Dynamic type argument, check only classes. 173 // Dynamic type argument, check only classes.
174 // List is a very common case. 174 // List is a very common case.
175 __ movq(R10, FieldAddress(RAX, Object::class_offset())); 175 __ LoadClassIndexOfObject(R10, RAX);
176 if (!type_class.is_interface()) { 176 if (!type_class.is_interface()) {
177 __ CompareObject(R10, type_class); 177 __ cmpl(R10, Immediate(type_class.index()));
178 __ j(EQUAL, is_instance_lbl); 178 __ j(EQUAL, is_instance_lbl);
179 } 179 }
180 if (type.IsListInterface()) { 180 if (type.IsListInterface()) {
181 Label unknown; 181 Label unknown;
182 GrowableArray<const Class*> args; 182 GrowableArray<const Class*> args;
183 args.Add(CoreClass("ObjectArray")); 183 args.Add(&Class::Handle(CoreClass("ObjectArray")));
184 args.Add(CoreClass("GrowableObjectArray")); 184 args.Add(&Class::Handle(CoreClass("GrowableObjectArray")));
185 args.Add(CoreClass("ImmutableArray")); 185 args.Add(&Class::Handle(CoreClass("ImmutableArray")));
186 CheckClasses(args, is_instance_lbl, &unknown); 186 CheckClasses(args, is_instance_lbl, &unknown);
187 __ Bind(&unknown); 187 __ Bind(&unknown);
188 } 188 }
189 return GenerateSubtype1TestCacheLookup( 189 return GenerateSubtype1TestCacheLookup(
190 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); 190 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl);
191 } 191 }
192 // If one type argument only, check if type argument is Object or Dynamic. 192 // If one type argument only, check if type argument is Object or Dynamic.
193 if (type_arguments.Length() == 1) { 193 if (type_arguments.Length() == 1) {
194 const AbstractType& tp_argument = AbstractType::ZoneHandle( 194 const AbstractType& tp_argument = AbstractType::ZoneHandle(
195 type_arguments.TypeAt(0)); 195 type_arguments.TypeAt(0));
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 __ j(EQUAL, &runtime_call, Assembler::kNearJump); 228 __ j(EQUAL, &runtime_call, Assembler::kNearJump);
229 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); 229 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
230 __ CompareObject(RCX, bool_true); 230 __ CompareObject(RCX, bool_true);
231 __ j(EQUAL, is_instance_lbl); 231 __ j(EQUAL, is_instance_lbl);
232 __ jmp(is_not_instance_lbl); 232 __ jmp(is_not_instance_lbl);
233 __ Bind(&runtime_call); 233 __ Bind(&runtime_call);
234 return type_test_cache.raw(); 234 return type_test_cache.raw();
235 } 235 }
236 236
237 237
238 // R10: instance class to check. 238 // R10: instance class to check.
Ivan Posva 2012/05/30 18:15:19 // R10: instance class id to check.
239 void FlowGraphCompiler::CheckClasses(const GrowableArray<const Class*>& classes, 239 void FlowGraphCompiler::CheckClasses(const GrowableArray<const Class*>& classes,
Ivan Posva 2012/05/30 18:15:19 How about CheckClassIds and passing a GrowableArra
240 Label* is_instance_lbl, 240 Label* is_instance_lbl,
241 Label* is_not_instance_lbl) { 241 Label* is_not_instance_lbl) {
242 for (intptr_t i = 0; i < classes.length(); i++) { 242 for (intptr_t i = 0; i < classes.length(); i++) {
243 __ CompareObject(R10, *classes[i]); 243 __ cmpl(R10, Immediate(classes[i]->index()));
244 __ j(EQUAL, is_instance_lbl); 244 __ j(EQUAL, is_instance_lbl);
245 } 245 }
246 __ jmp(is_not_instance_lbl); 246 __ jmp(is_not_instance_lbl);
247 } 247 }
248 248
249 249
250 250
251 // Testing against an instantiated type with no arguments, without 251 // Testing against an instantiated type with no arguments, without
252 // SubtypeTestCache. 252 // SubtypeTestCache.
253 // RAX: instance to test against (preserved). 253 // RAX: instance to test against (preserved).
(...skipping 19 matching lines...) Expand all
273 TypeArguments::Handle(), 273 TypeArguments::Handle(),
274 &malformed_error)) { 274 &malformed_error)) {
275 __ jmp(is_instance_lbl); 275 __ jmp(is_instance_lbl);
276 } else { 276 } else {
277 __ jmp(is_not_instance_lbl); 277 __ jmp(is_not_instance_lbl);
278 } 278 }
279 279
280 ObjectStore* object_store = Isolate::Current()->object_store(); 280 ObjectStore* object_store = Isolate::Current()->object_store();
281 // Compare if the classes are equal. Instance is not Smi. 281 // Compare if the classes are equal. Instance is not Smi.
282 __ Bind(&compare_classes); 282 __ Bind(&compare_classes);
283 __ movq(R10, FieldAddress(RAX, Object::class_offset())); 283 __ LoadClassIndexOfObject(R10, RAX);
284 // If type is an interface, we can skip the class equality check. 284 // If type is an interface, we can skip the class equality check.
285 if (!type_class.is_interface()) { 285 if (!type_class.is_interface()) {
286 __ CompareObject(R10, type_class); 286 __ cmpl(R10, Immediate(type_class.index()));
287 __ j(EQUAL, is_instance_lbl); 287 __ j(EQUAL, is_instance_lbl);
288 } 288 }
289 // Check for interfaces that cannot be implemented by user. 289 // Check for interfaces that cannot be implemented by user.
290 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). 290 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces).
291 // Bool interface can be implemented only by core class Bool. 291 // Bool interface can be implemented only by core class Bool.
292 if (type.IsBoolInterface()) { 292 if (type.IsBoolInterface()) {
293 const Class& bool_class = Class::ZoneHandle(object_store->bool_class()); 293 __ cmpl(R10, Immediate(kBool));
294 __ CompareObject(R10, bool_class);
295 __ j(EQUAL, is_instance_lbl); 294 __ j(EQUAL, is_instance_lbl);
296 __ jmp(is_not_instance_lbl); 295 __ jmp(is_not_instance_lbl);
297 return; 296 return;
298 } 297 }
299 if (type.IsFunctionInterface()) { 298 if (type.IsFunctionInterface()) {
300 // Check if instance is a closure. 299 // Check if instance is a closure.
301 const Immediate raw_null = 300 const Immediate raw_null =
302 Immediate(reinterpret_cast<intptr_t>(Object::null())); 301 Immediate(reinterpret_cast<intptr_t>(Object::null()));
303 __ movq(R10, FieldAddress(R10, Class::signature_function_offset())); 302 __ LoadClassByIndex(R13, R10);
304 __ cmpq(R10, raw_null); 303 __ movq(R13, FieldAddress(R13, Class::signature_function_offset()));
304 __ cmpq(R13, raw_null);
305 __ j(NOT_EQUAL, is_instance_lbl); 305 __ j(NOT_EQUAL, is_instance_lbl);
306 __ jmp(is_not_instance_lbl); 306 __ jmp(is_not_instance_lbl);
307 return; 307 return;
308 } 308 }
309 // Custom checking for numbers (Smi, Mint, Bigint and Double). 309 // Custom checking for numbers (Smi, Mint, Bigint and Double).
310 // Note that instance is not Smi(checked above). 310 // Note that instance is not Smi(checked above).
311 if (type.IsSubtypeOf( 311 if (type.IsSubtypeOf(
312 Type::Handle(Type::NumberInterface()), &malformed_error)) { 312 Type::Handle(Type::NumberInterface()), &malformed_error)) {
313 const Class& mint_class = Class::ZoneHandle(object_store->mint_class()); 313 const Class& mint_class = Class::Handle(object_store->mint_class());
Ivan Posva 2012/05/30 18:15:19 kMint
314 const Class& bigint_class = Class::ZoneHandle(object_store->bigint_class()); 314 const Class& bigint_class = Class::Handle(object_store->bigint_class());
Ivan Posva 2012/05/30 18:15:19 kBigInt
315 const Class& double_class = Class::ZoneHandle(object_store->double_class()); 315 const Class& double_class = Class::Handle(object_store->double_class());
Ivan Posva 2012/05/30 18:15:19 kDouble
316 GrowableArray<const Class*> args; 316 GrowableArray<const Class*> args;
317 if (type.IsNumberInterface()) { 317 if (type.IsNumberInterface()) {
318 args.Add(&double_class); 318 args.Add(&double_class);
319 args.Add(&mint_class); 319 args.Add(&mint_class);
320 args.Add(&bigint_class); 320 args.Add(&bigint_class);
321 } else if (type.IsIntInterface()) { 321 } else if (type.IsIntInterface()) {
322 args.Add(&mint_class); 322 args.Add(&mint_class);
323 args.Add(&bigint_class); 323 args.Add(&bigint_class);
324 } else if (type.IsDoubleInterface()) { 324 } else if (type.IsDoubleInterface()) {
325 args.Add(&double_class); 325 args.Add(&double_class);
326 } 326 }
327 CheckClasses(args, is_instance_lbl, is_not_instance_lbl); 327 CheckClasses(args, is_instance_lbl, is_not_instance_lbl);
328 return; 328 return;
329 } 329 }
330 if (type.IsStringInterface()) { 330 if (type.IsStringInterface()) {
331 const Class& one_byte_string_class = 331 const Class& one_byte_string_class =
Ivan Posva 2012/05/30 18:15:19 ditto, here and below...
332 Class::ZoneHandle(object_store->one_byte_string_class()); 332 Class::Handle(object_store->one_byte_string_class());
333 const Class& two_byte_string_class = 333 const Class& two_byte_string_class =
334 Class::ZoneHandle(object_store->two_byte_string_class()); 334 Class::Handle(object_store->two_byte_string_class());
335 const Class& four_byte_string_class = 335 const Class& four_byte_string_class =
336 Class::ZoneHandle(object_store->four_byte_string_class()); 336 Class::Handle(object_store->four_byte_string_class());
337 const Class& external_one_byte_string_class = 337 const Class& external_one_byte_string_class =
338 Class::ZoneHandle(object_store->external_one_byte_string_class()); 338 Class::Handle(object_store->external_one_byte_string_class());
339 const Class& external_two_byte_string_class = 339 const Class& external_two_byte_string_class =
340 Class::ZoneHandle(object_store->external_two_byte_string_class()); 340 Class::Handle(object_store->external_two_byte_string_class());
341 const Class& external_four_byte_string_class = 341 const Class& external_four_byte_string_class =
342 Class::ZoneHandle(object_store->external_four_byte_string_class()); 342 Class::Handle(object_store->external_four_byte_string_class());
343 GrowableArray<const Class*> args; 343 GrowableArray<const Class*> args;
344 args.Add(&one_byte_string_class); 344 args.Add(&one_byte_string_class);
345 args.Add(&two_byte_string_class); 345 args.Add(&two_byte_string_class);
346 args.Add(&four_byte_string_class); 346 args.Add(&four_byte_string_class);
347 args.Add(&external_one_byte_string_class); 347 args.Add(&external_one_byte_string_class);
348 args.Add(&external_two_byte_string_class); 348 args.Add(&external_two_byte_string_class);
349 args.Add(&external_four_byte_string_class); 349 args.Add(&external_four_byte_string_class);
350 CheckClasses(args, is_instance_lbl, is_not_instance_lbl); 350 CheckClasses(args, is_instance_lbl, is_not_instance_lbl);
351 return; 351 return;
352 } 352 }
353 // Otherwise fallthrough. 353 // Otherwise fallthrough.
354 } 354 }
355 355
356 356
357 // Uses SubtypeTestCache to store instance class and result. 357 // Uses SubtypeTestCache to store instance class and result.
358 // RAX: instance to test. 358 // RAX: instance to test.
359 // Immediate class test already done. 359 // Immediate class test already done.
360 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( 360 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
361 intptr_t cid, 361 intptr_t cid,
362 intptr_t token_index, 362 intptr_t token_index,
363 const Class& type_class, 363 const Class& type_class,
364 Label* is_instance_lbl, 364 Label* is_instance_lbl,
365 Label* is_not_instance_lbl) { 365 Label* is_not_instance_lbl) {
366 const SubtypeTestCache& type_test_cache = 366 const SubtypeTestCache& type_test_cache =
367 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); 367 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
368 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); 368 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
369 const Immediate raw_null = 369 const Immediate raw_null =
370 Immediate(reinterpret_cast<intptr_t>(Object::null())); 370 Immediate(reinterpret_cast<intptr_t>(Object::null()));
371 __ movq(R10, FieldAddress(RAX, Object::class_offset())); 371 __ LoadClassOfObject(R10, RAX);
372 // Check immediate superclass equality. 372 // Check immediate superclass equality.
373 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); 373 __ movq(R13, FieldAddress(R10, Class::super_type_offset()));
374 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); 374 __ movq(R13, FieldAddress(R13, Type::type_class_offset()));
375 __ CompareObject(R13, type_class); 375 __ CompareObject(R13, type_class);
376 __ j(EQUAL, is_instance_lbl); 376 __ j(EQUAL, is_instance_lbl);
377 377
378 __ LoadObject(R10, type_test_cache); 378 __ LoadObject(R10, type_test_cache);
379 __ pushq(R10); // Cache array. 379 __ pushq(R10); // Cache array.
380 __ pushq(RAX); // Instance. 380 __ pushq(RAX); // Instance.
381 __ pushq(raw_null); // Unused 381 __ pushq(raw_null); // Unused
(...skipping 28 matching lines...) Expand all
410 if (type.IsTypeParameter()) { 410 if (type.IsTypeParameter()) {
411 // Load instantiator (or null) and instantiator type arguments on stack. 411 // Load instantiator (or null) and instantiator type arguments on stack.
412 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. 412 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments.
413 // RDX: instantiator type arguments. 413 // RDX: instantiator type arguments.
414 // Check if type argument is Dynamic. 414 // Check if type argument is Dynamic.
415 __ cmpq(RDX, raw_null); 415 __ cmpq(RDX, raw_null);
416 __ j(EQUAL, is_instance_lbl); 416 __ j(EQUAL, is_instance_lbl);
417 // Can handle only type arguments that are instances of TypeArguments. 417 // Can handle only type arguments that are instances of TypeArguments.
418 // (runtime checks canonicalize type arguments). 418 // (runtime checks canonicalize type arguments).
419 Label fall_through; 419 Label fall_through;
420 __ movq(R10, FieldAddress(RDX, Object::class_offset())); 420 __ CompareClassOfObject(RDX,
421 __ CompareObject(R10, Object::ZoneHandle(Object::type_arguments_class())); 421 Class::Handle(Object::type_arguments_class()));
422 __ j(NOT_EQUAL, &fall_through); 422 __ j(NOT_EQUAL, &fall_through);
423 __ movq(RDI, 423 __ movq(RDI,
424 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); 424 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index())));
425 // RDI: Concrete type. 425 // RDI: Concrete type.
426 // Check if it is Dynamic, 426 // Check if it is Dynamic,
427 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType())); 427 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType()));
428 __ j(EQUAL, is_instance_lbl); 428 __ j(EQUAL, is_instance_lbl);
429 __ cmpq(RDI, raw_null); 429 __ cmpq(RDI, raw_null);
430 __ j(EQUAL, is_instance_lbl); 430 __ j(EQUAL, is_instance_lbl);
431 // For Smi check quickly against int and num interface types. 431 // For Smi check quickly against int and num interface types.
(...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after
1009 __ cmpq(RAX, raw_null); 1009 __ cmpq(RAX, raw_null);
1010 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); 1010 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
1011 } 1011 }
1012 // Instantiate non-null type arguments. 1012 // Instantiate non-null type arguments.
1013 if (comp->type_arguments().IsUninstantiatedIdentity()) { 1013 if (comp->type_arguments().IsUninstantiatedIdentity()) {
1014 // Check if the instantiator type argument vector is a TypeArguments of a 1014 // Check if the instantiator type argument vector is a TypeArguments of a
1015 // matching length and, if so, use it as the instantiated type_arguments. 1015 // matching length and, if so, use it as the instantiated type_arguments.
1016 // No need to check the instantiator (RAX) for null here, because a null 1016 // No need to check the instantiator (RAX) for null here, because a null
1017 // instantiator will have the wrong class (Null instead of TypeArguments). 1017 // instantiator will have the wrong class (Null instead of TypeArguments).
1018 Label type_arguments_uninstantiated; 1018 Label type_arguments_uninstantiated;
1019 __ LoadObject(RCX, Class::ZoneHandle(Object::type_arguments_class())); 1019 __ CompareClassOfObject(RAX,
1020 __ cmpq(RCX, FieldAddress(RAX, Object::class_offset())); 1020 Class::Handle(Object::type_arguments_class()));
1021 __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump); 1021 __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump);
1022 Immediate arguments_length = 1022 Immediate arguments_length =
1023 Immediate(Smi::RawValue(comp->type_arguments().Length())); 1023 Immediate(Smi::RawValue(comp->type_arguments().Length()));
1024 __ cmpq(FieldAddress(RAX, TypeArguments::length_offset()), 1024 __ cmpq(FieldAddress(RAX, TypeArguments::length_offset()),
1025 arguments_length); 1025 arguments_length);
1026 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); 1026 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
1027 __ Bind(&type_arguments_uninstantiated); 1027 __ Bind(&type_arguments_uninstantiated);
1028 } 1028 }
1029 // In the non-factory case, we rely on the allocation stub to 1029 // In the non-factory case, we rely on the allocation stub to
1030 // instantiate the type arguments. 1030 // instantiate the type arguments.
(...skipping 691 matching lines...) Expand 10 before | Expand all | Expand 10 after
1722 1722
1723 void FlowGraphCompiler::FinalizeComments(const Code& code) { 1723 void FlowGraphCompiler::FinalizeComments(const Code& code) {
1724 code.set_comments(assembler_->GetCodeComments()); 1724 code.set_comments(assembler_->GetCodeComments());
1725 } 1725 }
1726 1726
1727 #undef __ 1727 #undef __
1728 1728
1729 } // namespace dart 1729 } // namespace dart
1730 1730
1731 #endif // defined TARGET_ARCH_X64 1731 #endif // defined TARGET_ARCH_X64
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698