| Index: runtime/vm/intrinsifier_x64.cc
 | 
| ===================================================================
 | 
| --- runtime/vm/intrinsifier_x64.cc	(revision 9572)
 | 
| +++ runtime/vm/intrinsifier_x64.cc	(working copy)
 | 
| @@ -8,6 +8,7 @@
 | 
|  #include "vm/intrinsifier.h"
 | 
|  
 | 
|  #include "vm/assembler.h"
 | 
| +#include "vm/assembler_macros.h"
 | 
|  #include "vm/instructions.h"
 | 
|  #include "vm/object_store.h"
 | 
|  
 | 
| @@ -485,12 +486,21 @@
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX contains right argument.
 | 
| +  __ movq(RCX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ addq(RAX, RCX);
 | 
| +  __ j(OVERFLOW, &fall_through, Assembler::kNearJump);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_add(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_addFromInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -508,18 +518,39 @@
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_sub(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX contains right argument, which is the actual subtrahend of subtraction.
 | 
| +  __ movq(RCX, RAX);
 | 
| +  __ movq(RAX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ subq(RAX, RCX);
 | 
| +  __ j(OVERFLOW, &fall_through, Assembler::kNearJump);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_mulFromInteger(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX is the right argument.
 | 
| +  ASSERT(kSmiTag == 0);  // Adjust code below if not the case.
 | 
| +  __ SmiUntag(RAX);
 | 
| +  __ movq(RCX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ imulq(RAX, RCX);
 | 
| +  __ j(OVERFLOW, &fall_through, Assembler::kNearJump);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_mul(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_mulFromInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -558,42 +589,95 @@
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_truncDivide(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX: right argument (divisor)
 | 
| +  __ cmpq(RAX, Immediate(0));
 | 
| +  __ j(EQUAL, &fall_through, Assembler::kNearJump);
 | 
| +  __ movq(RCX, RAX);
 | 
| +  __ SmiUntag(RCX);
 | 
| +  __ movq(RAX, Address(RSP, + 2 * kWordSize));  // Left argument (dividend).
 | 
| +  __ SmiUntag(RAX);
 | 
| +  __ pushq(RDX);  // Preserve RDX in case of 'fall_through'.
 | 
| +  __ cqo();
 | 
| +  __ idivq(RCX);
 | 
| +  __ popq(RDX);
 | 
| +  // Check the corner case of dividing the 'MIN_SMI' with -1, in which case we
 | 
| +  // cannot tag the result.
 | 
| +  __ cmpq(RAX, Immediate(0x4000000000000000));
 | 
| +  __ j(EQUAL, &fall_through);
 | 
| +  __ SmiTag(RAX);
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_negate(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));
 | 
| +  __ testq(RAX, Immediate(kSmiTagMask));
 | 
| +  __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);  // Non-smi value.
 | 
| +  __ negq(RAX);
 | 
| +  __ j(OVERFLOW, &fall_through, Assembler::kNearJump);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX is the right argument.
 | 
| +  __ movq(RCX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ andq(RAX, RCX);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitAnd(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_bitAndFromInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX is the right argument.
 | 
| +  __ movq(RCX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ orq(RAX, RCX);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitOr(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_bitOrFromInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  TestBothArgumentsSmis(assembler, &fall_through);
 | 
| +  // RAX is the right argument.
 | 
| +  __ movq(RCX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ xorq(RAX, RCX);
 | 
| +  // Result is in RAX.
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_bitXor(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_bitXorFromInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -652,7 +736,7 @@
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Integer_equal(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  return Integer_equalToInteger(assembler);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -748,57 +832,293 @@
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Double_isNegative(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  const Bool& bool_true = Bool::ZoneHandle(Bool::True());
 | 
| +  const Bool& bool_false = Bool::ZoneHandle(Bool::False());
 | 
| +  Label is_false, is_true, is_zero;
 | 
| +  __ movq(RAX, Address(RSP, +1 * kWordSize));
 | 
| +  __ movsd(XMM0, FieldAddress(RAX, Double::value_offset()));
 | 
| +  __ xorpd(XMM1, XMM1);  // 0.0 -> XMM1.
 | 
| +  __ comisd(XMM0, XMM1);
 | 
| +  __ j(PARITY_EVEN, &is_false, Assembler::kNearJump);  // NaN -> false.
 | 
| +  __ j(EQUAL, &is_zero, Assembler::kNearJump);  // Check for negative zero.
 | 
| +  __ j(ABOVE_EQUAL, &is_false, Assembler::kNearJump);  // >= 0 -> false.
 | 
| +  __ Bind(&is_true);
 | 
| +  __ LoadObject(RAX, bool_true);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_false);
 | 
| +  __ LoadObject(RAX, bool_false);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_zero);
 | 
| +  // Check for negative zero (get the sign bit).
 | 
| +  __ movmskpd(RAX, XMM0);
 | 
| +  __ testq(RAX, Immediate(1));
 | 
| +  __ j(NOT_ZERO, &is_true, Assembler::kNearJump);
 | 
| +  __ jmp(&is_false, Assembler::kNearJump);
 | 
| +  return true;  // Method is complete, no slow case.
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Check if the last argument is a double, jump to label 'is_smi' if smi
 | 
| +// (easy to convert to double), otherwise jump to label 'not_double_smi',
 | 
| +// Returns the last argument in RAX.
 | 
| +static void TestLastArgumentIsDouble(Assembler* assembler,
 | 
| +                                     Label* is_smi,
 | 
| +                                     Label* not_double_smi) {
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));
 | 
| +  __ testq(RAX, Immediate(kSmiTagMask));
 | 
| +  __ j(ZERO, is_smi, Assembler::kNearJump);  // Jump if Smi.
 | 
| +  __ CompareClassId(RAX, kDouble);
 | 
| +  __ j(NOT_EQUAL, not_double_smi, Assembler::kNearJump);
 | 
| +  // Fall through if double.
 | 
| +}
 | 
| +
 | 
| +
 | 
| +enum TrigonometricFunctions {
 | 
| +  kSine,
 | 
| +  kCosine,
 | 
| +};
 | 
| +
 | 
| +static void EmitTrigonometric(Assembler* assembler,
 | 
| +                              TrigonometricFunctions kind) {
 | 
| +  Label fall_through, is_smi, double_op;
 | 
| +  TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
 | 
| +  // Argument is double and is in EAX.
 | 
| +  __ fldl(FieldAddress(RAX, Double::value_offset()));
 | 
| +  __ Bind(&double_op);
 | 
| +  switch (kind) {
 | 
| +    case kSine:   __ fsin(); break;
 | 
| +    case kCosine: __ fcos(); break;
 | 
| +    default:
 | 
| +      UNREACHABLE();
 | 
| +  }
 | 
| +  const Class& double_class = Class::Handle(
 | 
| +      Isolate::Current()->object_store()->double_class());
 | 
| +  Label alloc_failed;
 | 
| +  AssemblerMacros::TryAllocate(assembler,
 | 
| +                               double_class,
 | 
| +                               &alloc_failed,
 | 
| +                               RAX);  // Result register.
 | 
| +  __ fstpl(FieldAddress(RAX, Double::value_offset()));
 | 
| +  __ ret();
 | 
| +
 | 
| +  __ Bind(&is_smi);  // smi -> double.
 | 
| +  __ SmiUntag(RAX);
 | 
| +  __ pushq(RAX);
 | 
| +  __ fildl(Address(RSP, 0));
 | 
| +  __ popq(RAX);
 | 
| +  __ jmp(&double_op);
 | 
| +
 | 
| +  __ Bind(&alloc_failed);
 | 
| +  __ ffree(0);
 | 
| +  __ fincstp();
 | 
| +
 | 
| +  __ Bind(&fall_through);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  bool Intrinsifier::Math_sqrt(Assembler* assembler) {
 | 
| +  Label fall_through, is_smi, double_op;
 | 
| +  TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
 | 
| +  // Argument is double and is in RAX.
 | 
| +  __ movsd(XMM1, FieldAddress(RAX, Double::value_offset()));
 | 
| +  __ Bind(&double_op);
 | 
| +  __ sqrtsd(XMM0, XMM1);
 | 
| +  const Class& double_class = Class::Handle(
 | 
| +      Isolate::Current()->object_store()->double_class());
 | 
| +  AssemblerMacros::TryAllocate(assembler,
 | 
| +                               double_class,
 | 
| +                               &fall_through,
 | 
| +                               RAX);  // Result register.
 | 
| +  __ movsd(FieldAddress(RAX, Double::value_offset()), XMM0);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_smi);
 | 
| +  __ SmiUntag(RAX);
 | 
| +  __ cvtsi2sd(XMM1, RAX);
 | 
| +  __ jmp(&double_op);
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Math_sin(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  EmitTrigonometric(assembler, kSine);
 | 
| +  return false;  // Compile method for slow case.
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::Math_cos(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  EmitTrigonometric(assembler, kCosine);
 | 
| +  return false;  // Compile method for slow case.
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Identity comparison.
 | 
|  bool Intrinsifier::Object_equal(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  Label is_true;
 | 
| +  const Bool& bool_true = Bool::ZoneHandle(Bool::True());
 | 
| +  const Bool& bool_false = Bool::ZoneHandle(Bool::False());
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));
 | 
| +  __ cmpq(RAX, Address(RSP, + 2 * kWordSize));
 | 
| +  __ j(EQUAL, &is_true, Assembler::kNearJump);
 | 
| +  __ LoadObject(RAX, bool_false);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_true);
 | 
| +  __ LoadObject(RAX, bool_true);
 | 
| +  __ ret();
 | 
| +  return true;
 | 
|  }
 | 
|  
 | 
|  
 | 
| +static intptr_t GetOffsetForField(const char* class_name_p,
 | 
| +                                  const char* field_name_p) {
 | 
| +  const String& class_name = String::Handle(String::NewSymbol(class_name_p));
 | 
| +  const String& field_name = String::Handle(String::NewSymbol(field_name_p));
 | 
| +  const Class& cls = Class::Handle(Library::Handle(
 | 
| +      Library::CoreImplLibrary()).LookupClass(class_name));
 | 
| +  ASSERT(!cls.IsNull());
 | 
| +  const Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name));
 | 
| +  ASSERT(!field.IsNull());
 | 
| +  return field.Offset();
 | 
| +}
 | 
| +
 | 
| +static const char* kFixedSizeArrayIteratorClassName = "FixedSizeArrayIterator";
 | 
| +
 | 
| +// Class 'FixedSizeArrayIterator':
 | 
| +//   T next() {
 | 
| +//     return _array[_pos++];
 | 
| +//   }
 | 
| +// Intrinsify: return _array[_pos++];
 | 
| +// TODO(srdjan): Throw a 'NoMoreElementsException' exception if the iterator
 | 
| +// has no more elements.
 | 
|  bool Intrinsifier::FixedSizeArrayIterator_next(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  const intptr_t array_offset =
 | 
| +      GetOffsetForField(kFixedSizeArrayIteratorClassName, "_array");
 | 
| +  const intptr_t pos_offset =
 | 
| +      GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos");
 | 
| +  ASSERT((array_offset >= 0) && (pos_offset >= 0));
 | 
| +  // Receiver is not NULL.
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // Receiver.
 | 
| +  __ movq(RCX, FieldAddress(RAX, pos_offset));  // Field _pos.
 | 
| +  // '_pos' cannot be greater than array length and therefore is always Smi.
 | 
| +#if defined(DEBUG)
 | 
| +  Label pos_ok;
 | 
| +  __ testq(RCX, Immediate(kSmiTagMask));
 | 
| +  __ j(ZERO, &pos_ok, Assembler::kNearJump);
 | 
| +  __ Stop("pos must be Smi");
 | 
| +  __ Bind(&pos_ok);
 | 
| +#endif
 | 
| +  // Check that we are not trying to call 'next' when 'hasNext' is false.
 | 
| +  __ movq(RAX, FieldAddress(RAX, array_offset));  // Field _array.
 | 
| +  __ cmpq(RCX, FieldAddress(RAX, Array::length_offset()));  // Range check.
 | 
| +  __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump);
 | 
| +
 | 
| +  // RCX is Smi, i.e, times 2.
 | 
| +  ASSERT(kSmiTagShift == 1);
 | 
| +  __ movq(RDI, FieldAddress(RAX, RCX, TIMES_4, sizeof(RawArray)));  // Result.
 | 
| +  const Immediate value = Immediate(reinterpret_cast<int64_t>(Smi::New(1)));
 | 
| +  __ addq(RCX, value);  // _pos++.
 | 
| +  __ j(OVERFLOW, &fall_through, Assembler::kNearJump);
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // Receiver.
 | 
| +  __ StoreIntoObjectNoBarrier(RAX,
 | 
| +                              FieldAddress(RAX, pos_offset),
 | 
| +                              RCX);  // Store _pos.
 | 
| +  __ movq(RAX, RDI);
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Class 'FixedSizeArrayIterator':
 | 
| +//   bool hasNext() {
 | 
| +//     return _length > _pos;
 | 
| +//   }
 | 
|  bool Intrinsifier::FixedSizeArrayIterator_hasNext(Assembler* assembler) {
 | 
| +  Label fall_through, is_true;
 | 
| +  const Bool& bool_true = Bool::ZoneHandle(Bool::True());
 | 
| +  const Bool& bool_false = Bool::ZoneHandle(Bool::False());
 | 
| +  const intptr_t length_offset =
 | 
| +      GetOffsetForField(kFixedSizeArrayIteratorClassName, "_length");
 | 
| +  const intptr_t pos_offset =
 | 
| +      GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos");
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));     // Receiver.
 | 
| +  __ movq(RCX, FieldAddress(RAX, length_offset));  // Field _length.
 | 
| +  __ movq(RAX, FieldAddress(RAX, pos_offset));     // Field _pos.
 | 
| +  __ movq(RDI, RAX);
 | 
| +  __ orq(RDI, RCX);
 | 
| +  __ testq(RDI, Immediate(kSmiTagMask));
 | 
| +  __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);  // Non-smi _length/_pos.
 | 
| +  __ cmpq(RCX, RAX);     // _length > _pos.
 | 
| +  __ j(GREATER, &is_true, Assembler::kNearJump);
 | 
| +  __ LoadObject(RAX, bool_false);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_true);
 | 
| +  __ LoadObject(RAX, bool_true);
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::String_getLength(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // String object.
 | 
| +  __ movq(RAX, FieldAddress(RAX, String::length_offset()));
 | 
| +  __ ret();
 | 
| +  return true;
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// TODO(srdjan): Implement for two and four byte strings as well.
 | 
|  bool Intrinsifier::String_charCodeAt(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  __ movq(RCX, Address(RSP, + 1 * kWordSize));  // Index.
 | 
| +  __ movq(RAX, Address(RSP, + 2 * kWordSize));  // String.
 | 
| +  __ testq(RCX, Immediate(kSmiTagMask));
 | 
| +  __ j(NOT_ZERO, &fall_through, Assembler::kNearJump);  // Non-smi index.
 | 
| +  // Range check.
 | 
| +  __ cmpq(RCX, FieldAddress(RAX, String::length_offset()));
 | 
| +  // Runtime throws exception.
 | 
| +  __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump);
 | 
| +  __ CompareClassId(RAX, kOneByteString);
 | 
| +  __ j(NOT_EQUAL, &fall_through);
 | 
| +  __ SmiUntag(RCX);
 | 
| +  __ movzxb(RAX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
 | 
| +  __ SmiTag(RAX);
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::String_hashCode(Assembler* assembler) {
 | 
| +  Label fall_through;
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // String object.
 | 
| +  __ movq(RAX, FieldAddress(RAX, String::hash_offset()));
 | 
| +  __ cmpq(RAX, Immediate(0));
 | 
| +  __ j(EQUAL, &fall_through, Assembler::kNearJump);
 | 
| +  __ ret();
 | 
| +  __ Bind(&fall_through);
 | 
| +  // Hash not yet computed.
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
|  
 | 
|  bool Intrinsifier::String_isEmpty(Assembler* assembler) {
 | 
| -  return false;
 | 
| +  Label is_true;
 | 
| +  const Bool& bool_true = Bool::ZoneHandle(Bool::True());
 | 
| +  const Bool& bool_false = Bool::ZoneHandle(Bool::False());
 | 
| +  // Get length.
 | 
| +  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // String object.
 | 
| +  __ movq(RAX, FieldAddress(RAX, String::length_offset()));
 | 
| +  __ cmpq(RAX, Immediate(Smi::RawValue(0)));
 | 
| +  __ j(EQUAL, &is_true, Assembler::kNearJump);
 | 
| +  __ LoadObject(RAX, bool_false);
 | 
| +  __ ret();
 | 
| +  __ Bind(&is_true);
 | 
| +  __ LoadObject(RAX, bool_true);
 | 
| +  __ ret();
 | 
| +  return true;
 | 
|  }
 | 
|  
 | 
|  #undef __
 | 
| 
 |