// -----------------------------------------------------------------------------
+// NearLabels are labels used for short jumps (in Intel jargon).
+// NearLabels should be used if it can be guaranteed that the jump range is
+// within -128 to +127. We already use short jumps when jumping backwards,
+// so using a NearLabel will only have performance impact if used for forward
+// jumps.
+class NearLabel BASE_EMBEDDED {
+ public:
+ NearLabel() { Unuse(); }
+ ~NearLabel() { ASSERT(!is_linked()); }
+
+ void Unuse() {
+ pos_ = -1;
+ unresolved_branches_ = 0;
+#ifdef DEBUG
+ for (int i = 0; i < kMaxUnresolvedBranches; i++) {
+ unresolved_positions_[i] = -1;
+ }
+#endif
+ }
+
+ int pos() {
+ ASSERT(is_bound());
+ return pos_;
+ }
+
+ bool is_bound() { return pos_ >= 0; }
+ bool is_linked() { return !is_bound() && unresolved_branches_ > 0; }
+ bool is_unused() { return !is_bound() && unresolved_branches_ == 0; }
+
+ void bind_to(int position) {
+ ASSERT(!is_bound());
+ pos_ = position;
+ }
+
+ void link_to(int position) {
+ ASSERT(!is_bound());
+ ASSERT(unresolved_branches_ < kMaxUnresolvedBranches);
+ unresolved_positions_[unresolved_branches_++] = position;
+ }
+
+ private:
+ static const int kMaxUnresolvedBranches = 8;
+ int pos_;
+ int unresolved_branches_;
+ int unresolved_positions_[kMaxUnresolvedBranches];
+
+ friend class Assembler;
+};
+
+
+// -----------------------------------------------------------------------------
// Relocation information
}
-void Assembler::link_to(Label* L, Label* appendix) {
- EnsureSpace ensure_space(this);
- last_pc_ = NULL;
- if (appendix->is_linked()) {
- if (L->is_linked()) {
- // Append appendix to L's list.
- Label p;
- Label q = *L;
- do {
- p = q;
- Displacement disp = disp_at(&q);
- disp.next(&q);
- } while (q.is_linked());
- Displacement disp = disp_at(&p);
- disp.link_to(appendix);
- disp_at_put(&p, disp);
- p.Unuse(); // to avoid assertion failure in ~Label
- } else {
- // L is empty, simply use appendix.
- *L = *appendix;
- }
- }
- appendix->Unuse(); // appendix should not be used anymore
-}
-
-
void Assembler::bind(Label* L) {
EnsureSpace ensure_space(this);
last_pc_ = NULL;
}
+void Assembler::bind(NearLabel* L) {
+ ASSERT(!L->is_bound());
+ last_pc_ = NULL;
+ while (L->unresolved_branches_ > 0) {
+ int branch_pos = L->unresolved_positions_[L->unresolved_branches_ - 1];
+ int disp = pc_offset() - branch_pos;
+ ASSERT(is_int8(disp));
+ set_byte_at(branch_pos - sizeof(int8_t), disp);
+ L->unresolved_branches_--;
+ }
+ L->bind_to(pc_offset());
+}
+
void Assembler::call(Label* L) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
}
+void Assembler::jmp(NearLabel* L) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ if (L->is_bound()) {
+ const int short_size = 2;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ ASSERT(is_int8(offs - short_size));
+ // 1110 1011 #8-bit disp.
+ EMIT(0xEB);
+ EMIT((offs - short_size) & 0xFF);
+ } else {
+ EMIT(0xEB);
+ EMIT(0x00); // The displacement will be resolved later.
+ L->link_to(pc_offset());
+ }
+}
+
void Assembler::j(Condition cc, Label* L, Hint hint) {
EnsureSpace ensure_space(this);
}
+void Assembler::j(Condition cc, NearLabel* L, Hint hint) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ ASSERT(0 <= cc && cc < 16);
+ if (FLAG_emit_branch_hints && hint != no_hint) EMIT(hint);
+ if (L->is_bound()) {
+ const int short_size = 2;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ ASSERT(is_int8(offs - short_size));
+ // 0111 tttn #8-bit disp
+ EMIT(0x70 | cc);
+ EMIT((offs - short_size) & 0xFF);
+ } else {
+ EMIT(0x70 | cc);
+ EMIT(0x00); // The displacement will be resolved later.
+ L->link_to(pc_offset());
+ }
+}
+
+
// FPU instructions.
void Assembler::fld(int i) {
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
+ void bind(NearLabel* L);
// Calls
void call(Label* L);
void jmp(const Operand& adr);
void jmp(Handle<Code> code, RelocInfo::Mode rmode);
+ // Short jump
+ void jmp(NearLabel* L);
+
// Conditional jumps
void j(Condition cc, Label* L, Hint hint = no_hint);
void j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint = no_hint);
void j(Condition cc, Handle<Code> code, Hint hint = no_hint);
+ // Conditional short jump
+ void j(Condition cc, NearLabel* L, Hint hint = no_hint);
+
// Floating-point operations
void fld(int i);
void fstp(int i);
private:
byte* addr_at(int pos) { return buffer_ + pos; }
byte byte_at(int pos) { return buffer_[pos]; }
+ void set_byte_at(int pos, byte value) { buffer_[pos] = value; }
uint32_t long_at(int pos) {
return *reinterpret_cast<uint32_t*>(addr_at(pos));
}
// labels
void print(Label* L);
void bind_to(Label* L, int pos);
- void link_to(Label* L, Label* appendix);
// displacements
inline Displacement disp_at(Label* L);
// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
void ToBooleanStub::Generate(MacroAssembler* masm) {
- Label false_result, true_result, not_string;
+ NearLabel false_result, true_result, not_string;
__ mov(eax, Operand(esp, 1 * kPointerSize));
// 'null' => false.
__ bind(&non_smi_result);
// Allocate a heap number if needed.
__ mov(ebx, Operand(eax)); // ebx: result
- Label skip_allocation;
+ NearLabel skip_allocation;
switch (mode_) {
case OVERWRITE_LEFT:
case OVERWRITE_RIGHT:
}
// Test if left operand is a string.
- Label lhs_not_string;
+ NearLabel lhs_not_string;
__ test(lhs, Immediate(kSmiTagMask));
__ j(zero, &lhs_not_string);
__ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx);
StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
__ TailCallStub(&string_add_left_stub);
- Label call_runtime_with_args;
+ NearLabel call_runtime_with_args;
// Left operand is not a string, test right.
__ bind(&lhs_not_string);
__ test(rhs, Immediate(kSmiTagMask));
// Test that eax is a number.
Label runtime_call;
Label runtime_call_clear_stack;
- Label input_not_smi;
- Label loaded;
+ NearLabel input_not_smi;
+ NearLabel loaded;
__ mov(eax, Operand(esp, kPointerSize));
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &input_not_smi);
__ lea(ecx, Operand(ecx, ecx, times_2, 0));
__ lea(ecx, Operand(eax, ecx, times_4, 0));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- Label cache_miss;
+ NearLabel cache_miss;
__ cmp(ebx, Operand(ecx, 0));
__ j(not_equal, &cache_miss);
__ cmp(edx, Operand(ecx, kIntSize));
void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
// Only free register is edi.
- Label done;
+ NearLabel done;
ASSERT(type_ == TranscendentalCache::SIN ||
type_ == TranscendentalCache::COS);
// More transcendental types can be added later.
// Both fsin and fcos require arguments in the range +/-2^63 and
// return NaN for infinities and NaN. They can share all code except
// the actual fsin/fcos operation.
- Label in_range;
+ NearLabel in_range;
// If argument is outside the range -2^63..2^63, fsin/cos doesn't
// work. We must reduce it to the appropriate range.
__ mov(edi, edx);
__ j(below, &in_range, taken);
// Check for infinity and NaN. Both return NaN for sin.
__ cmp(Operand(edi), Immediate(0x7ff00000));
- Label non_nan_result;
+ NearLabel non_nan_result;
__ j(not_equal, &non_nan_result, taken);
// Input is +/-Infinity or NaN. Result is NaN.
__ fstp(0);
__ fld(1);
// FPU Stack: input, 2*pi, input.
{
- Label no_exceptions;
+ NearLabel no_exceptions;
__ fwait();
__ fnstsw_ax();
// Clear if Illegal Operand or Zero Division exceptions are set.
// Compute st(0) % st(1)
{
- Label partial_remainder_loop;
+ NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
__ shr_cl(scratch2);
// Now the unsigned answer is in scratch2. We need to move it to ecx and
// we may need to fix the sign.
- Label negative;
+ NearLabel negative;
__ xor_(ecx, Operand(ecx));
__ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
__ j(greater, &negative);
void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
Register number) {
- Label load_smi, done;
+ NearLabel load_smi, done;
__ test(number, Immediate(kSmiTagMask));
__ j(zero, &load_smi, not_taken);
void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
- Label load_smi_edx, load_eax, load_smi_eax, done;
+ NearLabel load_smi_edx, load_eax, load_smi_eax, done;
// Load operand in edx into xmm0.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
Label* not_numbers) {
- Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
+ NearLabel load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
// Load operand in edx into xmm0, or branch to not_numbers.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
Register scratch,
ArgLocation arg_location) {
- Label load_smi_1, load_smi_2, done_load_1, done;
+ NearLabel load_smi_1, load_smi_2, done_load_1, done;
if (arg_location == ARGS_IN_REGISTERS) {
__ mov(scratch, edx);
} else {
void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
Label* non_float,
Register scratch) {
- Label test_other, done;
+ NearLabel test_other, done;
// Test if both operands are floats or smi -> scratch=k_is_float;
// Otherwise scratch = k_not_float.
__ test(edx, Immediate(kSmiTagMask));
if (op_ == Token::SUB) {
if (include_smi_code_) {
// Check whether the value is a smi.
- Label try_float;
+ NearLabel try_float;
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &try_float, not_taken);
&slow);
// Do the bitwise operation and check if the result fits in a smi.
- Label try_float;
+ NearLabel try_float;
__ not_(ecx);
__ cmp(ecx, 0xc0000000);
__ j(sign, &try_float, not_taken);
__ j(not_zero, &slow, not_taken);
// Check if the calling frame is an arguments adaptor frame.
- Label adaptor;
+ NearLabel adaptor;
__ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
__ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
// Try the new space allocation. Start out with computing the size of
// the arguments object and the elements array.
- Label add_arguments_object;
+ NearLabel add_arguments_object;
__ bind(&try_allocate);
__ test(ecx, Operand(ecx));
__ j(zero, &add_arguments_object);
__ SmiUntag(ecx);
// Copy the fixed array slots.
- Label loop;
+ NearLabel loop;
__ bind(&loop);
__ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
__ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
// Argument 4: End of string data
// Argument 3: Start of string data
- Label setup_two_byte, setup_rest;
+ NearLabel setup_two_byte, setup_rest;
__ test(edi, Operand(edi));
__ mov(edi, FieldOperand(eax, String::kLengthOffset));
__ j(zero, &setup_two_byte);
// ebx: last_match_info backing store (FixedArray)
// ecx: offsets vector
// edx: number of capture registers
- Label next_capture, done;
+ NearLabel next_capture, done;
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
// number string cache for smis is just the smi value, and the hash for
// doubles is the xor of the upper and lower words. See
// Heap::GetNumberStringCache.
- Label smi_hash_calculated;
- Label load_result_from_cache;
+ NearLabel smi_hash_calculated;
+ NearLabel load_result_from_cache;
if (object_is_smi) {
__ mov(scratch, object);
__ SmiUntag(scratch);
} else {
- Label not_smi, hash_calculated;
+ NearLabel not_smi, hash_calculated;
STATIC_ASSERT(kSmiTag == 0);
__ test(object, Immediate(kSmiTagMask));
__ j(not_zero, ¬_smi);
if (cc_ != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
- Label check_for_nan;
+ NearLabel check_for_nan;
__ cmp(edx, Factory::undefined_value());
__ j(not_equal, &check_for_nan);
__ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
} else {
- Label heap_number;
+ NearLabel heap_number;
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
__ j(equal, &heap_number);
__ setcc(above_equal, eax);
__ ret(0);
} else {
- Label nan;
+ NearLabel nan;
__ j(above_equal, &nan);
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
// Non-strict object equality is slower, so it is handled later in the stub.
if (cc_ == equal && strict_) {
Label slow; // Fallthrough label.
- Label not_smis;
+ NearLabel not_smis;
// If we're doing a strict equality comparison, we don't have to do
// type conversion, so we generate code to do fast comparison for objects
// and oddballs. Non-smi numbers and strings still go through the usual
// Get the type of the first operand.
// If the first object is a JS object, we have done pointer comparison.
- Label first_non_object;
+ NearLabel first_non_object;
STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
__ j(below, &first_non_object);
// Return non-zero (eax is not zero)
- Label return_not_equal;
+ NearLabel return_not_equal;
STATIC_ASSERT(kHeapObjectTag != 0);
__ bind(&return_not_equal);
__ ret(0);
// Don't base result on EFLAGS when a NaN is involved.
__ j(parity_even, &unordered, not_taken);
- Label below_label, above_label;
+ NearLabel below_label, above_label;
// Return a result of -1, 0, or 1, based on EFLAGS.
__ j(below, &below_label, not_taken);
__ j(above, &above_label, not_taken);
// Non-strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
- Label not_both_objects;
- Label return_unequal;
+ NearLabel not_both_objects;
+ NearLabel return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
// not NULL. The frame pointer is NULL in the exception handler of
// a JS entry frame.
__ xor_(esi, Operand(esi)); // Tentatively set context pointer to NULL.
- Label skip;
+ NearLabel skip;
__ cmp(ebp, 0);
__ j(equal, &skip, not_taken);
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
// Make sure we're not trying to return 'the hole' from the runtime
// call as this may lead to crashes in the IC code later.
if (FLAG_debug_code) {
- Label okay;
+ NearLabel okay;
__ cmp(eax, Factory::the_hole_value());
__ j(not_equal, &okay);
__ int3();
__ mov(esp, Operand::StaticVariable(handler_address));
// Unwind the handlers until the ENTRY handler is found.
- Label loop, done;
+ NearLabel loop, done;
__ bind(&loop);
// Load the type of the current stack handler.
const int kStateOffset = StackHandlerConstants::kStateOffset;
// edx is function, eax is map.
// Look up the function and the map in the instanceof cache.
- Label miss;
+ NearLabel miss;
ExternalReference roots_address = ExternalReference::roots_address();
__ mov(ecx, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
__ cmp(edx, Operand::StaticArray(ecx, times_pointer_size, roots_address));
__ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset));
// Loop through the prototype chain looking for the function prototype.
- Label loop, is_instance, is_not_instance;
+ NearLabel loop, is_instance, is_not_instance;
__ bind(&loop);
__ cmp(ecx, Operand(ebx));
__ j(equal, &is_instance);
// eax: first string
// edx: second string
// Check if either of the strings are empty. In that case return the other.
- Label second_not_zero_length, both_not_zero_length;
+ NearLabel second_not_zero_length, both_not_zero_length;
__ mov(ecx, FieldOperand(edx, String::kLengthOffset));
STATIC_ASSERT(kSmiTag == 0);
__ test(ecx, Operand(ecx));
Register count,
Register scratch,
bool ascii) {
- Label loop;
+ NearLabel loop;
__ bind(&loop);
// This loop just copies one character at a time, as it is only used for very
// short strings.
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
- Label last_bytes;
+ NearLabel last_bytes;
__ test(count, Immediate(~3));
__ j(zero, &last_bytes);
__ j(zero, &done);
// Copy remaining characters.
- Label loop;
+ NearLabel loop;
__ bind(&loop);
__ mov_b(scratch, Operand(src, 0));
__ mov_b(Operand(dest, 0), scratch);
// Make sure that both characters are not digits as such strings has a
// different hash algorithm. Don't try to look for these in the symbol table.
- Label not_array_index;
+ NearLabel not_array_index;
__ mov(scratch, c1);
__ sub(Operand(scratch), Immediate(static_cast<int>('0')));
__ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
__ add(hash, Operand(scratch));
// if (hash == 0) hash = 27;
- Label hash_not_zero;
+ NearLabel hash_not_zero;
__ test(hash, Operand(hash));
__ j(not_zero, &hash_not_zero);
__ mov(hash, Immediate(27));
__ IncrementCounter(&Counters::string_compare_native, 1);
// Find minimum length.
- Label left_shorter;
+ NearLabel left_shorter;
__ mov(scratch1, FieldOperand(left, String::kLengthOffset));
__ mov(scratch3, scratch1);
__ sub(scratch3, FieldOperand(right, String::kLengthOffset));
{
// Compare loop.
- Label loop;
+ NearLabel loop;
__ bind(&loop);
// Compare characters.
__ mov_b(scratch2, Operand(left, index, times_1, 0));
__ mov(edx, Operand(esp, 2 * kPointerSize)); // left
__ mov(eax, Operand(esp, 1 * kPointerSize)); // right
- Label not_same;
+ NearLabel not_same;
__ cmp(edx, Operand(eax));
__ j(not_equal, ¬_same);
STATIC_ASSERT(EQUAL == 0);
}
{ Comment cmnt(masm_, "[ Stack check");
- Label ok;
+ NearLabel ok;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit();
__ cmp(esp, Operand::StaticVariable(stack_limit));
break;
case Expression::kValue: {
- Label done;
+ NearLabel done;
switch (location_) {
case kAccumulator:
__ bind(materialize_true);
__ mov(edx, Operand(esp, 0)); // Switch value.
bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
if (inline_smi_code) {
- Label slow_case;
+ NearLabel slow_case;
__ mov(ecx, edx);
__ or_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
__ j(equal, &exit);
// Convert the object to a JS object.
- Label convert, done_convert;
+ NearLabel convert, done_convert;
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &convert);
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
__ j(zero, &call_runtime);
// For all objects but the receiver, check that the cache is empty.
- Label check_prototype;
+ NearLabel check_prototype;
__ cmp(ecx, Operand(eax));
__ j(equal, &check_prototype);
__ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.
- Label use_cache;
+ NearLabel use_cache;
__ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
__ jmp(&use_cache);
// If we got a map from the runtime call, we can do a fast
// modification check. Otherwise, we got a fixed array, and we have
// to do a slow check.
- Label fixed_array;
+ NearLabel fixed_array;
__ cmp(FieldOperand(eax, HeapObject::kMapOffset), Factory::meta_map());
__ j(not_equal, &fixed_array);
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
- Label update_each;
+ NearLabel update_each;
__ mov(ecx, Operand(esp, 4 * kPointerSize));
__ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset));
__ j(equal, &update_each);
EmitAssignment(stmt->each());
// Generate code for the body of the loop.
- Label stack_limit_hit, stack_check_done;
+ Label stack_limit_hit;
+ NearLabel stack_check_done;
Visit(stmt->body());
__ StackLimitCheck(&stack_limit_hit);
if (s != NULL && s->is_eval_scope()) {
// Loop up the context chain. There is no frame effect so it is
// safe to use raw labels here.
- Label next, fast;
+ NearLabel next, fast;
if (!context.is(temp)) {
__ mov(temp, context);
}
if (var->mode() == Variable::CONST) {
// Constants may be the hole value if they have not been initialized.
// Unhole them.
- Label done;
+ NearLabel done;
MemOperand slot_operand = EmitSlotSearch(slot, eax);
__ mov(eax, slot_operand);
__ cmp(eax, Factory::the_hole_value());
void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
Comment cmnt(masm_, "[ RegExpLiteral");
- Label materialized;
+ NearLabel materialized;
// Registers will be used as follows:
// edi = JS function.
// ecx = literals array.
OverwriteMode mode,
bool left_is_constant_smi,
Smi* value) {
- Label call_stub, done;
+ NearLabel call_stub;
+ Label done;
__ add(Operand(eax), Immediate(value));
__ j(overflow, &call_stub);
__ test(eax, Immediate(kSmiTagMask));
VisitForValue(args->at(0), kAccumulator); // Load the object.
- Label done;
+ NearLabel done;
// If the object is a smi return the object.
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &done);
VisitForValue(args->at(1), kAccumulator); // Load the value.
__ pop(ebx); // eax = value. ebx = object.
- Label done;
+ NearLabel done;
// If the object is a smi, return the value.
__ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &done);
Label done;
bool inline_smi_case = ShouldInlineSmiCase(expr->op());
if (inline_smi_case) {
- Label call_stub;
+ NearLabel call_stub;
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &call_stub);
__ lea(eax, Operand(eax, kSmiTagMask));
}
// Call ToNumber only if operand is not a smi.
- Label no_conversion;
+ NearLabel no_conversion;
if (ShouldInlineSmiCase(expr->op())) {
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &no_conversion);
}
// Inline smi case if we are in a loop.
- Label stub_call, done;
+ NearLabel stub_call;
+ Label done;
if (ShouldInlineSmiCase(expr->op())) {
if (expr->op() == Token::INC) {
__ add(Operand(eax), Immediate(Smi::FromInt(1)));
bool inline_smi_code = ShouldInlineSmiCase(op);
if (inline_smi_code) {
- Label slow_case;
+ NearLabel slow_case;
__ mov(ecx, Operand(edx));
__ or_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
}
+void Assembler::bind(NearLabel* L) {
+ ASSERT(!L->is_bound());
+ last_pc_ = NULL;
+ while (L->unresolved_branches_ > 0) {
+ int branch_pos = L->unresolved_positions_[L->unresolved_branches_ - 1];
+ int disp = pc_offset() - branch_pos;
+ ASSERT(is_int8(disp));
+ set_byte_at(branch_pos - sizeof(int8_t), disp);
+ L->unresolved_branches_--;
+ }
+ L->bind_to(pc_offset());
+}
+
+
void Assembler::GrowBuffer() {
ASSERT(buffer_overflow());
if (!own_buffer_) FATAL("external code buffer is too small");
}
+void Assembler::j(Condition cc, NearLabel* L, Hint hint) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ ASSERT(0 <= cc && cc < 16);
+ if (FLAG_emit_branch_hints && hint != no_hint) emit(hint);
+ if (L->is_bound()) {
+ const int short_size = 2;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ ASSERT(is_int8(offs - short_size));
+ // 0111 tttn #8-bit disp
+ emit(0x70 | cc);
+ emit((offs - short_size) & 0xFF);
+ } else {
+ emit(0x70 | cc);
+ emit(0x00); // The displacement will be resolved later.
+ L->link_to(pc_offset());
+ }
+}
+
+
void Assembler::jmp(Label* L) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
}
+void Assembler::jmp(NearLabel* L) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ if (L->is_bound()) {
+ const int short_size = sizeof(int8_t);
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ ASSERT(is_int8(offs - short_size));
+ // 1110 1011 #8-bit disp.
+ emit(0xEB);
+ emit((offs - short_size) & 0xFF);
+ } else {
+ emit(0xEB);
+ emit(0x00); // The displacement will be resolved later.
+ L->link_to(pc_offset());
+ }
+}
+
+
void Assembler::jmp(Register target) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
+ void bind(NearLabel* L);
// Calls
// Call near relative 32-bit displacement, relative to next instruction.
// Jump near absolute indirect (m64)
void jmp(const Operand& src);
+ // Short jump
+ void jmp(NearLabel* L);
+
// Conditional jumps
void j(Condition cc, Label* L);
void j(Condition cc, Handle<Code> target, RelocInfo::Mode rmode);
+ // Conditional short jump
+ void j(Condition cc, NearLabel* L, Hint hint = no_hint);
+
// Floating-point operations
void fld(int i);
private:
byte* addr_at(int pos) { return buffer_ + pos; }
byte byte_at(int pos) { return buffer_[pos]; }
+ void set_byte_at(int pos, byte value) { buffer_[pos] = value; }
uint32_t long_at(int pos) {
return *reinterpret_cast<uint32_t*>(addr_at(pos));
}
// labels
// void print(Label* L);
void bind_to(Label* L, int pos);
- void link_to(Label* L, Label* appendix);
// record reloc info for current pc_
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
void ToBooleanStub::Generate(MacroAssembler* masm) {
- Label false_result, true_result, not_string;
+ NearLabel false_result, true_result, not_string;
__ movq(rax, Operand(rsp, 1 * kPointerSize));
// 'null' => false.
Label runtime_call;
Label runtime_call_clear_stack;
Label input_not_smi;
- Label loaded;
+ NearLabel loaded;
// Test that rax is a number.
__ movq(rax, Operand(rsp, kPointerSize));
__ JumpIfNotSmi(rax, &input_not_smi);
__ addl(rcx, rcx);
__ lea(rcx, Operand(rax, rcx, times_8, 0));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- Label cache_miss;
+ NearLabel cache_miss;
__ cmpq(rbx, Operand(rcx, 0));
__ j(not_equal, &cache_miss);
// Cache hit!
// Compute st(0) % st(1)
{
- Label partial_remainder_loop;
+ NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
// cvttsd2si (32-bit version) directly.
Register double_exponent = rbx;
Register double_value = rdi;
- Label done, exponent_63_plus;
+ NearLabel done, exponent_63_plus;
// Get double and extract exponent.
__ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
// Clear result preemptively, in case we need to return zero.
// rcx: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
- Label seq_ascii_string, seq_two_byte_string, check_code;
+ NearLabel seq_ascii_string, seq_two_byte_string, check_code;
__ movq(rax, Operand(rsp, kSubjectOffset));
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
// Argument 4: End of string data
// Argument 3: Start of string data
- Label setup_two_byte, setup_rest;
+ NearLabel setup_two_byte, setup_rest;
__ testb(rdi, rdi);
__ j(zero, &setup_two_byte);
__ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
__ pop(rsi);
// Check the result.
- Label success;
+ NearLabel success;
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS));
__ j(equal, &success);
- Label failure;
+ NearLabel failure;
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
__ j(equal, &failure);
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
// rbx: last_match_info backing store (FixedArray)
// rcx: offsets vector
// rdx: number of capture registers
- Label next_capture, done;
+ NearLabel next_capture, done;
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
// Two identical objects are equal unless they are both NaN or undefined.
{
- Label not_identical;
+ NearLabel not_identical;
__ cmpq(rax, rdx);
__ j(not_equal, ¬_identical);
if (cc_ != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
- Label check_for_nan;
+ NearLabel check_for_nan;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
__ j(not_equal, &check_for_nan);
__ Set(rax, NegativeComparisonResult(cc_));
__ Set(rax, EQUAL);
__ ret(0);
} else {
- Label heap_number;
+ NearLabel heap_number;
// If it's not a heap number, then return equal for (in)equality operator.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
Factory::heap_number_map());
// If the first object is a JS object, we have done pointer comparison.
STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- Label first_non_object;
+ NearLabel first_non_object;
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
__ j(below, &first_non_object);
// Return non-zero (eax (not rax) is not zero)
// Generate the number comparison code.
if (include_number_compare_) {
Label non_number_comparison;
- Label unordered;
+ NearLabel unordered;
FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison);
__ xorl(rax, rax);
__ xorl(rcx, rcx);
// Not strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
- Label not_both_objects, return_unequal;
+ NearLabel not_both_objects, return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
// Before returning we restore the context from the frame pointer if not NULL.
// The frame pointer is NULL in the exception handler of a JS entry frame.
__ xor_(rsi, rsi); // tentatively set context pointer to NULL
- Label skip;
+ NearLabel skip;
__ cmpq(rbp, Immediate(0));
__ j(equal, &skip);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Handling of failure.
__ bind(&failure_returned);
- Label retry;
+ NearLabel retry;
// If the returned exception is RETRY_AFTER_GC continue at retry label
STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
__ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ movq(rsp, Operand(kScratchRegister, 0));
// Unwind the handlers until the ENTRY handler is found.
- Label loop, done;
+ NearLabel loop, done;
__ bind(&loop);
// Load the type of the current stack handler.
const int kStateOffset = StackHandlerConstants::kStateOffset;
// rdx is function, rax is map.
// Look up the function and the map in the instanceof cache.
- Label miss;
+ NearLabel miss;
__ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
__ j(not_equal, &miss);
__ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
__ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
// Loop through the prototype chain looking for the function prototype.
- Label loop, is_instance, is_not_instance;
+ NearLabel loop, is_instance, is_not_instance;
__ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
__ bind(&loop);
__ cmpq(rcx, rbx);
// rax: first string
// rdx: second string
// Check if either of the strings are empty. In that case return the other.
- Label second_not_zero_length, both_not_zero_length;
+ NearLabel second_not_zero_length, both_not_zero_length;
__ movq(rcx, FieldOperand(rdx, String::kLengthOffset));
__ SmiTest(rcx);
__ j(not_zero, &second_not_zero_length);
ASSERT(count.is(rcx)); // rep movs count
// Nothing to do for zero characters.
- Label done;
+ NearLabel done;
__ testl(count, count);
__ j(zero, &done);
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
- Label last_bytes;
+ NearLabel last_bytes;
__ testl(count, Immediate(~7));
__ j(zero, &last_bytes);
// Make sure that both characters are not digits as such strings has a
// different hash algorithm. Don't try to look for these in the symbol table.
- Label not_array_index;
+ NearLabel not_array_index;
__ leal(scratch, Operand(c1, -'0'));
__ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
__ j(above, ¬_array_index);
NULL);
// Register scratch4 now holds left.length - right.length.
const Register length_difference = scratch4;
- Label left_shorter;
+ NearLabel left_shorter;
__ j(less, &left_shorter);
// The right string isn't longer that the left one.
// Get the right string's length by subtracting the (non-negative) difference
// Register scratch1 now holds Min(left.length, right.length).
const Register min_length = scratch1;
- Label compare_lengths;
+ NearLabel compare_lengths;
// If min-length is zero, go directly to comparing lengths.
__ SmiTest(min_length);
__ j(zero, &compare_lengths);
__ SmiToInteger32(min_length, min_length);
// Registers scratch2 and scratch3 are free.
- Label result_not_equal;
+ NearLabel result_not_equal;
Label loop;
{
// Check characters 0 .. min_length - 1 in a loop.
__ Move(rax, Smi::FromInt(EQUAL));
__ ret(0);
- Label result_greater;
+ NearLabel result_greater;
__ bind(&result_not_equal);
// Unequal comparison of left to right, either character or length.
__ j(greater, &result_greater);
__ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
// Check for identity.
- Label not_same;
+ NearLabel not_same;
__ cmpq(rdx, rax);
__ j(not_equal, ¬_same);
__ Move(rax, Smi::FromInt(EQUAL));
}
{ Comment cmnt(masm_, "[ Stack check");
- Label ok;
+ NearLabel ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
__ j(above_equal, &ok);
StackCheckStub stub;
break;
case Expression::kValue: {
- Label done;
+ NearLabel done;
switch (location_) {
case kAccumulator:
__ bind(materialize_true);
// If we got a map from the runtime call, we can do a fast
// modification check. Otherwise, we got a fixed array, and we have
// to do a slow check.
- Label fixed_array;
+ NearLabel fixed_array;
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kMetaMapRootIndex);
__ j(not_equal, &fixed_array);
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
- Label update_each;
+ NearLabel update_each;
__ movq(rcx, Operand(rsp, 4 * kPointerSize));
__ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset));
__ j(equal, &update_each);
if (s != NULL && s->is_eval_scope()) {
// Loop up the context chain. There is no frame effect so it is
// safe to use raw labels here.
- Label next, fast;
+ NearLabel next, fast;
if (!context.is(temp)) {
__ movq(temp, context);
}
if (var->mode() == Variable::CONST) {
// Constants may be the hole value if they have not been initialized.
// Unhole them.
- Label done;
+ NearLabel done;
MemOperand slot_operand = EmitSlotSearch(slot, rax);
__ movq(rax, slot_operand);
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
// function and receiver and have the slow path jump around this
// code.
if (done.is_linked()) {
- Label call;
+ NearLabel call;
__ jmp(&call);
__ bind(&done);
// Push function.
void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
ASSERT(args->length() == 0);
- Label exit;
+ NearLabel exit;
// Get the number of formal parameters.
__ Move(rax, Smi::FromInt(scope()->num_parameters()));
__ movq(cache,
FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
- Label done, not_found;
+ NearLabel done, not_found;
// tmp now holds finger offset as a smi.
ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
__ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
VisitForValue(args->at(1), kAccumulator);
__ pop(left);
- Label done, fail, ok;
+ NearLabel done, fail, ok;
__ cmpq(left, right);
__ j(equal, &ok);
// Fail if either is a non-HeapObject.
case Token::ADD: {
Comment cmt(masm_, "[ UnaryOperation (ADD)");
VisitForValue(expr->expression(), kAccumulator);
- Label no_conversion;
+ NearLabel no_conversion;
Condition is_smi = masm_->CheckSmi(result_register());
__ j(is_smi, &no_conversion);
__ push(result_register());
}
// Call ToNumber only if operand is not a smi.
- Label no_conversion;
+ NearLabel no_conversion;
Condition is_smi;
is_smi = masm_->CheckSmi(rax);
__ j(is_smi, &no_conversion);