blob: 76b4bcff5dd27a6e369c3e623d5710ce74371fd0 [file] [log] [blame]
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string.h>
#include "v8.h"
#include "log.h"
#include "ast.h"
#include "macro-assembler.h"
#include "regexp-macro-assembler.h"
#include "macro-assembler-ia32.h"
#include "regexp-macro-assembler-ia32.h"
namespace v8 { namespace internal {
/*
* This assembler uses the following register assignment convention
* - edx : current character. Must be loaded using LoadCurrentCharacter
* before using any of the dispatch methods.
* - edi : current position in input, as negative offset from end of string.
* Please notice that this is the byte offset, not the character offset!
* - esi : end of input (points to byte after last character in input).
* - ebp : points to the location above the registers on the stack,
* as if by the "enter <register_count>" opcode.
* - esp : points to tip of backtracking stack.
*
* The registers eax, ebx and ecx are free to use for computations.
*
* Each call to a public method should retain this convention.
* The stack will have the following structure:
* - at_start (if 1, start at start of string, if 0, don't)
* - int* capture_array (int[num_saved_registers_], for output).
* - end of input (index of end of string, relative to *string_base)
* - start of input (index of first character in string, relative
* to *string_base)
* - void** string_base (location of a handle containing the string)
* - return address
* - backup of esi
* - backup of edi
* - backup of ebx
* ebp-> - old ebp
* - register 0 ebp[-4] (Only positions must be stored in the first
* - register 1 ebp[-8] num_saved_registers_ registers)
* - ...
*
* The first num_saved_registers_ registers are initialized to point to
* "character -1" in the string (i.e., char_size() bytes before the first
* character of the string). The remaining registers starts out as garbage.
*
* The data up to the return address must be placed there by the calling
* code, e.g., by calling the code as cast to:
* bool (*match)(String** string_base,
* int start_offset,
* int end_offset,
* int* capture_output_array,
* bool at_start)
*/
#define __ masm_->
RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(
Mode mode,
int registers_to_save)
: masm_(new MacroAssembler(NULL, kRegExpCodeSize)),
constants_(kRegExpConstantsSize),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
entry_label_(),
start_label_(),
success_label_(),
exit_label_(),
self_(Heap::undefined_value()) {
__ jmp(&entry_label_); // We'll write the entry code later.
__ bind(&start_label_); // And then continue from here.
}
RegExpMacroAssemblerIA32::~RegExpMacroAssemblerIA32() {
delete masm_;
// Unuse labels in case we throw away the assembler without calling GetCode.
entry_label_.Unuse();
start_label_.Unuse();
success_label_.Unuse();
exit_label_.Unuse();
}
void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) {
ASSERT(by > 0);
Label inside_string;
__ add(Operand(edi), Immediate(by * char_size()));
}
void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) {
ASSERT(reg >= 0);
ASSERT(reg < num_registers_);
__ add(register_location(reg), Immediate(by));
}
void RegExpMacroAssemblerIA32::Backtrack() {
__ pop(ecx);
__ add(Operand(ecx), Immediate(self_));
__ jmp(Operand(ecx));
}
void RegExpMacroAssemblerIA32::Bind(Label* label) {
__ bind(label);
}
void RegExpMacroAssemblerIA32::CheckBitmap(uc16 start,
Label* bitmap,
Label* on_zero) {
UNREACHABLE();
__ mov(eax, current_character());
__ sub(Operand(eax), Immediate(start));
__ cmp(eax, 64); // FIXME: 64 = length_of_bitmap_in_bits.
BranchOrBacktrack(greater_equal, on_zero);
__ mov(ebx, eax);
__ shr(ebx, 3);
// TODO(lrn): Where is the bitmap stored? Pass the bitmap as argument instead.
// __ mov(ecx, position_of_bitmap);
__ movzx_b(ebx, Operand(ecx, ebx, times_1, 0));
__ and_(eax, (1<<3)-1);
__ bt(Operand(ebx), eax);
__ j(carry, on_zero);
}
void RegExpMacroAssemblerIA32::CheckCharacter(uc16 c, Label* on_equal) {
__ cmp(current_character(), c);
BranchOrBacktrack(equal, on_equal);
}
void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) {
__ cmp(current_character(), limit);
BranchOrBacktrack(greater, on_greater);
}
void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) {
__ cmp(Operand(ebp, kAtStart), Immediate(0));
BranchOrBacktrack(equal, on_not_at_start);
__ mov(eax, Operand(ebp, kInputEndOffset));
__ add(eax, Operand(edi));
__ cmp(eax, Operand(ebp, kInputStartOffset));
BranchOrBacktrack(not_equal, on_not_at_start);
}
void RegExpMacroAssemblerIA32::CheckCharacterLT(uc16 limit, Label* on_less) {
__ cmp(current_character(), limit);
BranchOrBacktrack(less, on_less);
}
void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str,
int cp_offset,
Label* on_failure) {
int byte_length = str.length() * char_size();
int byte_offset = cp_offset * char_size();
__ cmp(Operand(edi), Immediate(-(byte_offset + byte_length)));
BranchOrBacktrack(greater, on_failure);
if (str.length() <= kMaxInlineStringTests) {
for (int i = 0; i < str.length(); i++) {
if (mode_ == ASCII) {
__ cmpb(Operand(esi, edi, times_1, byte_offset + i),
static_cast<int8_t>(str[i]));
} else {
ASSERT(mode_ == UC16);
__ cmpw(Operand(esi, edi, times_1, byte_offset + i * sizeof(uc16)),
Immediate(str[i]));
}
__ j(not_equal, on_failure);
}
return;
}
ArraySlice constant_buffer = constants_.GetBuffer(str.length(), char_size());
if (mode_ == ASCII) {
for (int i = 0; i < str.length(); i++) {
constant_buffer.at<char>(i) = static_cast<char>(str[i]);
}
} else {
memcpy(constant_buffer.location(),
str.start(),
str.length() * sizeof(uc16));
}
__ mov(eax, edi);
__ mov(ebx, esi);
__ lea(edi, Operand(esi, edi, times_1, byte_offset));
LoadConstantBufferAddress(esi, &constant_buffer);
__ mov(ecx, str.length());
if (char_size() == 1) {
__ rep_cmpsb();
} else {
ASSERT(char_size() == 2);
__ rep_cmpsw();
}
__ mov(esi, ebx);
__ mov(edi, eax);
BranchOrBacktrack(not_equal, on_failure);
}
void RegExpMacroAssemblerIA32::CheckCurrentPosition(int register_index,
Label* on_equal) {
__ cmp(edi, register_location(register_index));
BranchOrBacktrack(equal, on_equal);
}
void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
int start_reg, Label* on_no_match) {
Label fallthrough;
__ mov(eax, register_location(start_reg));
__ mov(ecx, register_location(start_reg + 1));
__ sub(ecx, Operand(eax)); // Length to check.
__ j(less, on_no_match);
__ j(equal, &fallthrough);
UNIMPLEMENTED(); // TODO(lrn): Call runtime function to do test.
__ bind(&fallthrough);
}
void RegExpMacroAssemblerIA32::CheckNotBackReference(
int start_reg, Label* on_no_match) {
Label fallthrough;
__ mov(eax, register_location(start_reg));
__ mov(ecx, register_location(start_reg + 1));
__ sub(ecx, Operand(eax)); // Length to check.
BranchOrBacktrack(less, on_no_match);
__ j(equal, &fallthrough);
// Check that there are sufficient characters left in the input.
__ mov(ebx, edi);
__ add(ebx, Operand(ecx));
BranchOrBacktrack(greater, on_no_match);
__ mov(ebx, edi);
__ mov(edx, esi);
__ add(edi, Operand(esi));
__ add(esi, Operand(eax));
__ rep_cmpsb();
__ mov(esi, edx);
Label success;
__ j(equal, &success);
__ mov(edi, ebx);
BranchOrBacktrack(no_condition, on_no_match);
__ bind(&success);
__ sub(edi, Operand(esi));
__ bind(&fallthrough);
}
void RegExpMacroAssemblerIA32::CheckNotRegistersEqual(int reg1,
int reg2,
Label* on_not_equal) {
__ mov(eax, register_location(reg1));
__ cmp(eax, register_location(reg2));
BranchOrBacktrack(not_equal, on_not_equal);
}
void RegExpMacroAssemblerIA32::CheckNotCharacter(uc16 c, Label* on_not_equal) {
__ cmp(current_character(), c);
BranchOrBacktrack(not_equal, on_not_equal);
}
void RegExpMacroAssemblerIA32::CheckNotCharacterAfterOr(uc16 c,
uc16 mask,
Label* on_not_equal) {
__ mov(eax, current_character());
__ or_(eax, mask);
__ cmp(eax, c);
BranchOrBacktrack(not_equal, on_not_equal);
}
void RegExpMacroAssemblerIA32::CheckNotCharacterAfterMinusOr(
uc16 c,
uc16 mask,
Label* on_not_equal) {
__ lea(eax, Operand(current_character(), -mask));
__ or_(eax, mask);
__ cmp(eax, c);
BranchOrBacktrack(not_equal, on_not_equal);
}
void RegExpMacroAssemblerIA32::DispatchHalfNibbleMap(
uc16 start,
Label* half_nibble_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
__ mov(eax, current_character());
__ sub(Operand(eax), Immediate(start));
__ mov(ecx, eax);
__ shr(eax, 2);
// FIXME: ecx must hold address of map
__ movzx_b(eax, Operand(ecx, eax, times_1, 0));
__ and_(ecx, 0x03);
__ add(ecx, Operand(ecx));
__ shr(eax); // Shift right cl times
Label second_bit_set, case_3, case_1;
__ test(eax, Immediate(0x02));
__ j(not_zero, &second_bit_set);
__ test(eax, Immediate(0x01));
__ j(not_zero, &case_1);
// Case 0:
__ jmp(destinations[0]);
__ bind(&case_1);
// Case 1:
__ jmp(destinations[1]);
__ bind(&second_bit_set);
__ test(eax, Immediate(0x01));
__ j(not_zero, &case_3);
// Case 2
__ jmp(destinations[2]);
__ bind(&case_3);
// Case 3:
__ jmp(destinations[3]);
}
void RegExpMacroAssemblerIA32::DispatchByteMap(
uc16 start,
Label* byte_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
Label fallthrough;
__ mov(eax, current_character());
__ sub(Operand(eax), Immediate(start));
__ cmp(eax, 64); // FIXME: 64 = size of map. Found somehow??
__ j(greater_equal, &fallthrough);
// TODO(lrn): ecx must hold address of map
__ movzx_b(eax, Operand(ecx, eax, times_1, 0));
// jump table: jump to destinations[eax];
__ bind(&fallthrough);
}
void RegExpMacroAssemblerIA32::DispatchHighByteMap(
byte start,
Label* byte_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
Label fallthrough;
__ mov(eax, current_character());
__ shr(eax, 8);
__ sub(Operand(eax), Immediate(start));
__ cmp(eax, destinations.length() - start);
__ j(greater_equal, &fallthrough);
// TODO(lrn) jumptable: jump to destinations[eax]
__ bind(&fallthrough);
}
void RegExpMacroAssemblerIA32::EmitOrLink(Label* label) {
UNIMPLEMENTED(); // Has no use.
}
void RegExpMacroAssemblerIA32::Fail() {
__ xor_(eax, Operand(eax)); // zero eax.
__ jmp(&exit_label_);
}
Handle<Object> RegExpMacroAssemblerIA32::GetCode() {
// Finalize code - write the entry point code now we know how many
// registers we need.
// Entry code:
__ bind(&entry_label_);
// Save callee-save registers. Order here should correspond to order of
// kBackup_ebx etc.
__ push(esi);
__ push(edi);
__ push(ebx); // Callee-save on MacOS.
__ enter(Immediate(num_registers_ * kPointerSize));
// Load string length.
__ mov(esi, Operand(ebp, kInputEndOffset));
// Load input position.
__ mov(edi, Operand(ebp, kInputStartOffset));
// Set up edi to be negative offset from string end.
__ sub(edi, Operand(esi));
// Set up esi to be end of string. First get location.
__ mov(edx, Operand(ebp, kInputBuffer));
// Dereference location to get string start.
__ mov(edx, Operand(edx, 0));
// Add start to length to complete esi setup.
__ add(esi, Operand(edx));
if (num_saved_registers_ > 0) {
// Fill saved registers with initial value = start offset - 1
__ mov(ecx, -num_saved_registers_);
__ mov(eax, Operand(edi));
__ sub(Operand(eax), Immediate(char_size()));
Label init_loop;
__ bind(&init_loop);
__ mov(Operand(ebp, ecx, times_4, +0), eax);
__ inc(ecx);
__ j(not_equal, &init_loop);
}
// Load previous char as initial value of current-character.
Label at_start;
__ cmp(Operand(ebp, kAtStart), Immediate(0));
__ j(not_equal, &at_start);
LoadCurrentCharToRegister(-1); // Load previous char.
__ jmp(&start_label_);
__ bind(&at_start);
__ mov(current_character(), '\n');
__ jmp(&start_label_);
// Exit code:
// Success
__ bind(&success_label_);
if (num_saved_registers_ > 0) {
// copy captures to output
__ mov(ebx, Operand(ebp, kRegisterOutput));
__ mov(ecx, Operand(ebp, kInputEndOffset));
__ sub(ecx, Operand(ebp, kInputStartOffset));
for (int i = 0; i < num_saved_registers_; i++) {
__ mov(eax, register_location(i));
__ add(eax, Operand(ecx)); // Convert to index from start, not end.
if (char_size() > 1) {
ASSERT(char_size() == 2);
__ sar(eax, 1); // Convert to character index, not byte.
}
__ mov(Operand(ebx, i * kPointerSize), eax);
}
}
__ mov(eax, Immediate(1));
// Exit and return eax
__ bind(&exit_label_);
__ leave();
__ pop(ebx);
__ pop(edi);
__ pop(esi);
__ ret(0);
CodeDesc code_desc;
masm_->GetCode(&code_desc);
Handle<Code> code = Factory::NewCode(code_desc,
NULL,
Code::ComputeFlags(Code::REGEXP),
self_);
LOG(CodeCreateEvent("RegExp", *code, "(Compiled RegExp)"));
return Handle<Object>::cast(code);
}
void RegExpMacroAssemblerIA32::GoTo(Label* to) {
__ jmp(to);
}
void RegExpMacroAssemblerIA32::IfRegisterGE(int reg,
int comparand,
Label* if_ge) {
__ cmp(register_location(reg), Immediate(comparand));
BranchOrBacktrack(greater_equal, if_ge);
}
void RegExpMacroAssemblerIA32::IfRegisterLT(int reg,
int comparand,
Label* if_lt) {
__ cmp(register_location(reg), Immediate(comparand));
BranchOrBacktrack(less, if_lt);
}
RegExpMacroAssembler::IrregexpImplementation
RegExpMacroAssemblerIA32::Implementation() {
return kIA32Implementation;
}
void RegExpMacroAssemblerIA32::LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input) {
ASSERT(cp_offset >= 0);
ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
__ cmp(edi, -cp_offset * char_size());
BranchOrBacktrack(greater_equal, on_end_of_input);
LoadCurrentCharToRegister(cp_offset);
}
void RegExpMacroAssemblerIA32::PopCurrentPosition() {
__ pop(edi);
}
void RegExpMacroAssemblerIA32::PopRegister(int register_index) {
__ pop(register_location(register_index));
}
void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) {
// CheckStackLimit(); // Not ready yet.
__ push(label, RelocInfo::NONE);
}
void RegExpMacroAssemblerIA32::PushCurrentPosition() {
__ push(edi);
}
void RegExpMacroAssemblerIA32::PushRegister(int register_index) {
__ push(register_location(register_index));
}
void RegExpMacroAssemblerIA32::ReadCurrentPositionFromRegister(int reg) {
__ mov(edi, register_location(reg));
}
void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
__ mov(esp, register_location(reg));
}
void RegExpMacroAssemblerIA32::SetRegister(int register_index, int to) {
ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
__ mov(register_location(register_index), Immediate(to));
}
void RegExpMacroAssemblerIA32::Succeed() {
__ jmp(&success_label_);
}
void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister(int reg) {
__ mov(register_location(reg), edi);
}
void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
__ mov(register_location(reg), esp);
}
// Private methods:
Operand RegExpMacroAssemblerIA32::register_location(int register_index) {
ASSERT(register_index < (1<<30));
if (num_registers_ <= register_index) {
num_registers_ = register_index + 1;
}
return Operand(ebp, -(register_index + 1) * kPointerSize);
}
Register RegExpMacroAssemblerIA32::current_character() {
return edx;
}
size_t RegExpMacroAssemblerIA32::char_size() {
return static_cast<size_t>(mode_);
}
void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
Label* to) {
if (condition < 0) { // No condition
if (to == NULL) {
Backtrack();
return;
}
__ jmp(to);
return;
}
if (to == NULL) {
Label skip;
__ j(NegateCondition(condition), &skip);
Backtrack();
__ bind(&skip);
return;
}
__ j(condition, to);
}
void RegExpMacroAssemblerIA32::CheckStackLimit() {
if (FLAG_check_stack) {
// Check for preemption first.
Label no_preempt;
Label retry_preempt;
// Check for preemption.
ExternalReference stack_guard_limit =
ExternalReference::address_of_stack_guard_limit();
__ cmp(esp, Operand::StaticVariable(stack_guard_limit));
__ j(above, &no_preempt, taken);
__ push(edi); // Current position.
__ push(edx); // Current character.
// Restore original edi, esi.
__ mov(edi, Operand(ebp, kBackup_edi));
__ mov(esi, Operand(ebp, kBackup_esi));
__ bind(&retry_preempt);
// simulate stack for Runtime call.
__ push(eax);
__ push(Immediate(Smi::FromInt(0))); // Dummy receiver
__ CallRuntime(Runtime::kStackGuard, 1);
__ pop(eax);
__ cmp(esp, Operand::StaticVariable(stack_guard_limit));
__ j(below_equal, &retry_preempt);
__ pop(edx);
__ pop(edi);
__ mov(esi, Operand(ebp, kInputBuffer));
__ mov(esi, Operand(esi, 0));
__ add(esi, Operand(ebp, kInputEndOffset));
__ bind(&no_preempt);
}
}
void RegExpMacroAssemblerIA32::LoadCurrentCharToRegister(int cp_offset) {
if (mode_ == ASCII) {
__ movzx_b(current_character(), Operand(esi, edi, times_1, cp_offset));
return;
}
ASSERT(mode_ == UC16);
__ movzx_w(current_character(),
Operand(esi, edi, times_1, cp_offset * sizeof(uc16)));
}
void RegExpMacroAssemblerIA32::LoadConstantBufferAddress(Register reg,
ArraySlice* buffer) {
__ mov(reg, buffer->array());
__ add(Operand(reg), Immediate(buffer->base_offset()));
}
#undef __
}} // namespace v8::internal